该题考察序列化反序列化及Linux命令执行相关知识。
题目
<?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));
?>
开门见山给出代码,考察代码分析能力。
首先,代码定义了一个名为ease
的类,其中__construct()
接收两个参数$method
和$args
,并将它们赋值给对应的属性。__destruct()
在对象销毁时自动调用。它首先检查$method
是否在可调用的方法数组中,如果是,则使用call_user_func_array()
函数调用ping()
方法,并将$args
作为参数传递进去。
方法ping($ip)
接收一个参数$ip
,使用exec()
函数执行$ip
命令,并将结果存储在$result
数组中,最后通过var_dump()
打印出来。
方法waf($str)
用于简单的Web应用防火墙(WAF)功能。它使用正则表达式检测是否包含一些特定的关键字,如|
、&
、;
、/
、cat
、flag
、tac
、php
、ls
等。如果不包含这些关键字,则返回原始字符串;否则,输出"don’t hack"。
__wakeup()
是一个魔术方法,用于在反序列化对象时自动调用。它遍历$args
数组,并对其中的每个元素调用waf()
方法进行过滤。
也就是说,代码通过接收名为ctf
的POST参数,对其进行base64解码并反序列化。
那么思路就很清晰了:构造可进行命令执行的ping命令,并经过序列化、base64加密后赋值给参数ctf。由于ctf在解码后会被反序列化,而反序列化时会执行wakeup魔术方法,即反序列化时会对ping命令中的关键字进行过滤来限制命令执行。所以我们可以使用双引号闭合等的方式进行绕过。
查看目录文件:
结果如下:
接下来就是访问flag_1s_here,由于flag被过滤,用双引号绕过。空格被过滤,用IFS进行绕过。(在PHP中,${}
语法是用于取变量的值,并可以在花括号中使用任意的表达式,IFS默认为空格)
构造POC后回显如下:
传参:
那么接下来要做的就是打印php中的内容
将
cat flag_1s_here /flag_831b69012c67b35f.php
变化为
c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp
$()用于执行命令并获取其输出结果,这里将 ASCII 码为 57 的字符 /
通过 printf 命令输出。
传参: