该题考察序列化反序列化及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 命令输出。
传参: