PHP反序列化


title: php反序列化 date: tags: php开发与安全


php反序列化基础

基础:

魔术方法

construct(): 当本对象被创建的时候自动调用,(unserialize()时不会被自动调用)
wakeup():    对象被unserialize()时自动调用
destruct():  当本对象被销毁时自动调用
tostring():  当本对象被当作字符串处理时调用(echo等)
get()/set():       当试图获取/写入一个不可达到属性或不存在的值时,会自动调用
call():      与get类似,当试图调用一个不可到达方法时调用
sleep/wakeup  当对象被序列化/反序列化时调用
invoke       当对象被当作函数使用时调用。

其中,对于to_string()的触发条件有很多:

echo/print 打印输出对象时
对象与字符串拼接或==比较时
对象在经过字符串处理函数如 strlen()strstr()等时以及class_exists()时

ctf中的反序列化经验

1.序列化后的结果,一切以var_dump出来的页面的源代码界面为准!,且源代码中的乱码部分hex编码都是00! 此外 private的序列化后属性名会变为 %00class_name%00shuxing_name protected 的序列化后属性名会变为 %00 *%00shuxing_name 2.另外一点就是,一个对象被反序列化出来后,他就释放在内存空间成为一个真正存在的对象了 3.还有一点是,序列化只会记录属性和值,不会记录函数 4.反序列化后不会调用__constrict()

利用phar文件

当使用phar文件时,phar文件的meta-data是以序列化的形式存储在phar文件中的.

QQ截图20210221152417

那么如何利用呢

基本姿势:

QQ截图20210219153247

使用phar://协议访问

QQ截图20210219153304

使用phar://协议,是不用管后缀名,一个jpg文件都可以被phar://协议打开 phar://协议访问文件常用 phar://文件路径

进阶姿势

1.幻术头加在stub上 有些waf是检验文件头的,检验到 ?>是不让过的。所以需要改改stub,在stub的前面加上一些幻术头同时修改文件后缀名来绕过.

QQ截图20210219153317

下面是能够触发phar反序列化的函数

QQ截图20210219153330

Phar文件创建模板

    $phar = new Phar('test.phar');
    $phar->startBuffering();
    $phar->addFromString('test.txt', 'text');
    $phar->setStub('<?php __HALT_COMPILER(); ? >');
    $phar->setMetadata($c);
    $phar->stopBuffering();

php版本7.1以上,对类属性检测不严格导致的反序列化问题

php7.1+反序列化的对象可以直接以public属性的形式对原类中的protected形式的属性进行修改。 比如[网鼎杯 2020 青龙组]AreUSerialz一题。其难点在于你必须在反序列化payload中修改一个protected的属性才能拿到flag,但是有一个判断语句让你不能在反序列化payload出现%00字符。 所以此处我们在做payload的时候,把protected属性改成public属性再序列化也行。

    protected $op;
    protected $filename;
    protected $content;

    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

        private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

        function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

关键代码放这里。逻辑是从get参数获取数据并反序列化。然后我们通过反序列化payload操控op让它为2并且filename=flag.php。但是我们正常思路制造payload时因为op和filename是proteced属性,难免会有%00字符出现,但是它又让你不能出现%00这种ascii码小于32的字符。所以我们直接把op和filename当作public属性处理也可以直接过去 也就是说payload这样写

new  FileHandler{
    public $op=2;
    public $filename="flag.php"
}

绕过__wakeup方法(CVE-2016-7124)

只需构建一个序列化字段,它的变量数与实际不符即可。 像这样O:4:”xctf”:3:{s:4:”flag”;s:3:”111″;s:5:”flsag”;s:3:”111″;} 这里本身有2个变量,但在标识变量数时与实际不符,就会绕过__wakeup 适用版本:PHP5 < 5.6.25 PHP7 < 7.0.10

session存储调用与php反序列化引发的安全问题

1.基础功能:

session信息在服务器端存储时,其内容是通过一系列比如通过序列化等操作加工改变了的,而当其被调用时,又能逆加工回原有的内容,这是基础。 session信息在服务器端存储时,其加工方式可以通过php.ini文件的session.save_handler= 参数进行调整。 这个参数的不同值对应的加工方式如下:

$_SESSION['name']=$_GET['name']  //传入参数并在session文件里以name的变量名保存

ini_set('session.serialize_handler','php')=>abc=>name|s:3:"abc";
//变量名|序列化处理后的值 

ini_set('session.serialize_handler','php_binary')=>abc=>#names:4:"abcd";
//#为键名长度对应的ascii字符+变量名+序列化后的值

ini_set('session.serialize_handler','php_serialize')=>abc=>a:1:{s:4:"name";s:3:"abc";}
//将变量以数组形式进行序列化处理

QQ截图20210219153504

2.在此基础上利用upload_process机制来实现 “即使没有输入点也能继续触发序列化漏洞"

注:

如果没关session.upload_progress.cleanup,每次写入session的内容都会被删,这样的话只能使用条件竞争来搞了

QQ截图20210219153528

上面的话翻译为: 当session.upload_progress.enabled INI选项开启时,你在上传文件的同时POST一个参数值与session.upload_progress.name(默认为PHP_SESSION_UPLOAD_PROGRESS)值的值,会在session里留下session.upload_porgress.prefix与session.upload_progress.name链接的值,而后者的内容就是我们上传的文件的文件名. 试验一下

QQ截图20210219154237

抓包改包:

QQ截图20210219154309

确实有残留,于是凭此进行上面那条的操作开始反序列化攻击(文件包含也行)