九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

admin 2023年6月28日18:26:02评论46 views字数 6626阅读22分5秒阅读模式

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

一、PHP反序列化简介

php 反序列化基本上是围绕着serialize()、unserialize()这两个函数展开的,还有PHAR协议用于解析 phar 文件、phar 文件的 meta-data 字段存在反序列化漏洞,可以使用协议读取文件触发反序列化。

那么什么是序列化呢?序列化就是将一个对象变成可以传输的字符串,而反序列化其实就是将序列化得到的字符串再转变成对象。

首先上例子:

json_encode()json_decode()
<?php$book = array('book_1' => 'test_1','book_2' => 'test_2','book_3' => 'test_3', 'book_4' => 'test_4');$json = json_encode($book);echo $json;?>

*左右滑动查看更多

在这里我们有一个 book 数组,如果需要传输这个数组,我们可以利用 json_encode()函数将这个数据序列化成一串字符串,以 key-value 的形式展示出来。

'book_1' => 'test_1', 'book_2' => 'test_2', 'book_3' => 'test_3', 'book_4' => 'test_4'
九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

所以我们将数组序列化成 json 格式的字符串的目的就是为了方便传输,我们可以看见,这里用 json 格式来保存数据主要是使用键值对格式来保存的。

json 格式只是为了传输数据而出现的,那么我们讲反序列化漏洞的话,就需要将字符串反序列化成对象。

二、相关概念

在这里笔者写了一个 class,这个 class 中存有一些变量,当这个class被实例化之后,在使用过程中,里面的一些变量发生了改变,如果以后某些时候还会用到这个变量,我们让这个class一直不销毁,就会浪费系统资源。而如果我们将这个对象序列化,将其保存成一个字符串,当你需要使用的时候,再将其反序列化为对象就可以了。

<?php
class DemoClass{public $name = "test";public $sex = "man";public $age = "24";}$example = new DemoClass();$example->name = "aaron";$example->sex = "woman";$example->age = 22;
echo serialize($example);?>

在这里,我们首先创建了一个DemoClass,里面存了一些数据,然后我们实例化了一个对象,并将这个对象里的信息改变了,当我们还需要使用这个实例的话,就将序列化(serialize)后的字符串存起来,需要使用的时候再反序列化(unserialize)出来就可以了。

我们可以看一下结果:

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

这个时候,序列化对象出来的格式和 json 格式不一样。

   O:9:"DemoClass":3:{s:4:"name";s:5:"aaron";s:3:"sex";s:5:"woman";s:3:"age";i:22;}    // O 表示 object,这里还有一个情况是A,A表示是Array表示数组    // O:9 这个9 表示对象名表示占9个字符    // O:9:"DemoClass":3 这个3 表示是对象里有三个变量    // {s:4:"name";s:5:"aaron";} s=> 表示String 类型格式,s:4 4=>表示变量名占4位(name),s:5表示name的值(aaron)是String类型格式,且占5位    // i => 表示是int类型格式,后面直接跟数据    // d => 表示double类型格式

*左右滑动查看更多

然后如果反序列化(unserialize)回来:

<?php
class DemoClass{public $name = "test";public $sex = "man";public $age = "24";}$example = new DemoClass();$example->name = "aaron";$example->sex = "woman";$example->age = 21;
$val = serialize($example);$x = unserialize($val);echo $x->name;?>

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

三、原理介绍

php 里的魔术方法,通常因为某些条件而触发,不需要手动调用,我理解的是钩子函数,也就是生命周期的概念。

魔术方法:


__construct() //当一个对象创建时被调用

__destruct() //当一个对象销毁时被调用
__toString() //当一个对象被当作一个字符串使用

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

__wakeup //在对象被反序列化时被调用

理解这几个魔术函数,如果 php 接收我们反序列化的字符串,且在魔术方法中能够直接执行我们构造的 payload,就会造成反序列化漏洞。

看一个简单的例子:

   <?php    class A{    var $test = "demo";    function __destruct(){    echo $this->test;    }    }    $a = $_GET['test'];    $a_unser = unserialize($a);    ?>

这里表示是我们传入 test 参数,然后在反序列化成对象,然后在其生命周期当这个反序列化生成的对象要被销毁的时候调用 echo 方法,输出 test 参数。

那么我们构造如下 payload

O:1:"A":1:{s:4:"test";s:11:"hello,world";}

*左右滑动查看更多

test 参数可控的情况下,就会输出 hello,world。

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

我们再来尝试一下不同的生命周期。

1、__construtor

在这里,construct 是处于创建对象的生命周期中,当创建对象的时候会调用该函数,这里要被利用的话,需要配合另一个 Class,这里先用__wakeup在被反序列化时,new 一个新的对象 A,并传入参数,这里表示 test 参数可控的情况下,当 test 参数可控,并在反序列化后,将 test 参数传入 A 的新实例中,那么只要 constructor 中存在可执行代码或者执行命令的函数,那么造成影响。

   <?php    class A{    var $test = "demo";    function __construct($test){    echo "<br/>";    echo $test;    }    }    class B{    public $test_1 = "";    function __wakeup(){    $obj = new A($this->test_1);    }    }    $a = $_GET['test'];    echo $a;    $a_unser = unserialize($a);    new A("123");    ?>
 O:1:"B":1:{s:6:"test_1";s:11:"hello,world";}

*左右滑动查看更多

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

2、__destruct

在这里,destruct 处于对象被销毁的生命周期,当实例化之后,且对该对象的操作完成之后,那么 php 的回收机制则会回收该对象,这里就会调用该钩子函数,这里表示 test 参数可控的情况下,并在反序列化后之后,再打印该值,那么只要 destruct 中存在可执行代码或者执行命令的函数,那么就会造成影响。

  <?php    class A{    var $test = "demo";    function __destruct(){    echo "<br/>";    echo $this->test;    }    }    $a = $_GET['test'];    echo $a;    $a_unser = unserialize($a);    ?>

O:1:"A":1:{s:4:"test";s:11:"hello,world";}

*左右滑动查看更多

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议
3、__toString

在这里,toString 处于当需要将对象输出的生命周期,当反序列化之后,需要输出对象并将其值用作上下文中使用,那么将会调用该钩子函数。当 $test 参数可控的情况下,在反序列化之后形成对象时,如果需要输出该对象,那么只要 toString 方法中存在可执行代码或者命令的函数,那么就会造成影响。

<?php    class A{    var $test = "demo";    function __toString(){    echo "toString()<br/>";    return $this->test;    }    }    $a = $_GET['test'];    echo $a;    echo "<br/>";    $a_unser = unserialize($a);    echo $a_unser;    ?>
 O:1:"A":1:{s:4:"test";s:11:"hello,world";}

*左右滑动查看更多

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议
4、__sleep

在这里,sleep 处于当需要序列化对象的生命周期,在序列化之前,存在该钩子,则会返回一个包含对象中所有应被序列化的变量名称的数组,当 $test 参数可控的情况下,在序列化之后形成字符串时,那么只要 sleep 方法中存在可执行代码或者命令的函数,那么就会造成影响。

 <?php    class A{    var $test = '';    function __construct($test){    $this->test = $test;    }    function __sleep(){    echo "__sleep()    echo $this->test;    echo     return array('test');    }    }    class B{    public $test_1 = "";    function __wakeup(){    $obj = new A($this->test_1);    echo serialize($obj);    }    }    $a = $_GET['test'];    echo $a,    $a_unser = unserialize($a);    ?>
 O:1:"B":1:{s:6:"test_1";s:11:"hello,world";}

*左右滑动查看更多

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

5、__wakeup

在这里wakeup 是字符串反序列化的时候,会调用该钩子函数,只要执行 unserialize 方法就会触发该方法,其实我们关注 php 反序列化漏洞特别需要关注的魔术方法应该是wakeup,destruct,因为这两个方法只要在反序列化过程中一定会用到的,尤其是__wakeup。

    <?php    class A{    var $test = '';    function __construct($test){    $this->test = $test;    }    function __wakeup(){    echo "__wakeup()<br>";    echo $this->test;    }    }    $a = $_GET['test'];    echo $a,"<br>";    $a_unser = unserialize($a);    ?>
  O:1:"A":1:{s:4:"test";s:11:"hello,world";}

*左右滑动查看更多

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

四、举例

  <?php    class A{    var $file = '';    function __construct($file=''){    $this->file = $file;    }    function readfile() {    if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\')==FALSE) {    return @file_get_contents($this->file);    }    else{    echo "false";    }    }    }    $x = new A();    isset($_GET['test']) && $g = $_GET['test'];    if (!empty($g)) {    echo $g,"<br>";    $x = unserialize($g);    }    echo $x->readfile();    ?>

*左右滑动查看更多

在这里,当实例化之前,调用 construt 魔术方法,如果未给 file 传值,那么 file 默认为空,如果 test 参数为空,则不输出文件,那么要输出文件内容则需要置参数不为空,其需要将参数反序列化,最后再调用反序列化后对象的 readfile 函数,并在这个对象实例中必须要存在 file 值,所以在这里构造反序列化字符串,但是在 readfile 里也有限制,不能使用相对路径,也不能带绝对路径,只能访问当前目录的文件。

O:1:"A":1:{s:4:"file";s:5:"1.txt";}

*左右滑动查看更多

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

PHAR

 <?php     class AnyClass{     function __destruct() {     var_dump($_this);     eval($this -> output);     }     }     file_get_contents($_GET["file"]);

*左右滑动查看更多

生成 phar 文件的 poc:

   <?php    class AnyClass{    function __destruct(){    echo $this -> output;    }    }    @unlink("phar.phar");    $phar = new Phar('phar.phar');    $phar -> stopBuffering();    $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');    $phar -> addFromString('test.txt','test');    $object = new AnyClass();    $object -> output= 'system("whoami");';    $phar -> setMetadata($object);    $phar -> stopBuffering();

*左右滑动查看更多

九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议
五、安全开发的防范建议

在安全开发中,防范php反序列化攻击,常用的建议包括以下几点:

1、不要使用 unserialized() 反序列化来处理未知源的数据。应该使用其他安全的反序列化函数,如 JSON 解码 (json_decode()) 等。

2、对于从未知来源接收到的数据,必须进行数据验证,以避免恶意攻击。应该使用 PHP 序列化自定义数组(serialize())的函数很容易被嵌入一个已在内部定义的 PHP 类名,这个类可能会被用户请求中的序列化字符串调用。

3、序列化的数据应该经过加密,以防止可能存在的中间人攻击,比如数据包嗅探。而且,加密的密钥应该存储在安全的位置。

4、在执行反序列化的代码之前,请确保使用合适的输入校验,以确保没有输入到反序列化(unserialize())函数的未知参数。

下面是一个示例代码,演示如何以安全的方式使用反序列化功能。此示例使用json_decode()函数来解析JSON字符串,避免了使用可能存在漏洞的unserialize()函数:

//接收JSON数据$serialized_data= $_POST['json_data'];
//解析并验证JSON数据$data = json_decode($serialized_data, true);if(!is_array($data)){    die("Invalid input data");}

*左右滑动查看更多

六、PHP反序列化防御方法

PHP 反序列化漏洞是一类比较常见的安全漏洞,攻击者可以通过在序列化数据中插入恶意代码,在反序列化时触发漏洞,导致代码执行漏洞。为了防范 PHP 反序列化漏洞,可以考虑以下措施:

1、对用户提交的数据进行校验和过滤:可以使用正则表达式、过滤函数等方式对数据进行过滤和校验,确保数据格式正确。例如,限制数据类型、长度、特殊字符等,避免攻击者插入恶意数据。

2、对反序列化数据进行安全检查:需要仔细检查反序列化数据是否存在安全问题,并对特殊字符、敏感函数等进行处理和过滤。

3、使用 PHP 序列化器的安全模式:PHP 提供了一些安全选项,可以在序列化和反序列化时进行校验和限制,防止攻击者插入恶意代码。例如,可以设置 serialize_precision=0、 disable_functions 等参数来限制序列化和反序列化的范围。

4、更新 PHP 版本和扩展库:PHP 在新版本中对一些反序列化漏洞进行了修复,并增加了一些防御措施。可以及时升级 PHP 版本,或者升级相关扩展库,避免已知漏洞。

防范 PHP 反序列化漏洞需要从多方面入手,包括数据校验、安全检查、使用安全模式和升级版本等,提高系统的安全性和鲁棒性。

参考链接:

https://www.freebuf.com/articles/web/167721.html

原文始发于微信公众号(安恒信息安全服务):九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月28日18:26:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   九维团队-绿队(改进)| PHP反序列化简介、安全开发及防御建议https://cn-sec.com/archives/1840645.html

发表评论

匿名网友 填写信息