PHP反序列化漏洞的获取,利用,攻击和防御

-- PHP反序列化漏洞的基本原理,漏洞获取,利用,攻击和防御
【官网】:

应用场景

序列化和反序列化大量应用在web系统中,其中很多开发者会在序列化和反序列化函数中做一些处理。例如在php中调用反序列化函数unserialize时,如果存在用户可控参数,同时由于反序列化会自动调用一些魔术方法,且恰好魔术方法内存在一些敏感操作例如eVal()函数

基础资源

1)目标web请求的服务端存在反序列化调用. 2)反序列化操作中有动态执行函数.  3)动态执行函数会获取用户可控参数

使用须知

请仅用于合法安全测试和修复,勿用于非法用途。

配置步骤



常见问题

快速入门

A)反序列化漏洞原理。

a1)什么是序列化与反序列化?

   序列化是将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象状态,重新创建该对象。

    换一句话说:序列化:把对象转换为字节序列的过程称为对象的序列化。 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。 

      php的序列化函数:

     https://www.runoob.com/php/php-serialize-function.html

      php反序列化函数:

        https://www.runoob.com/php/php-unserialize-function.html


A2)序列化格式解释.

A2.1)对于没有private字段的情况.

序列化串: O:4:"test":2:{s:2:"id";s:5:"Baize";s:4:"name";s:3:"Sec";}

解释:

O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>}

A2.2)对于有private成员的序列化。

序列化串:O:4:"test":2:{s:8:"testid";s:5:"Baize";s:4:"name";s:3:"Sec";}

O:4:"test":2:{s:8:"%00test%00id";s:5:"Baize";s:4:"name";s:3:"Sec";}


解释: 

O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>}



附:其它语言生成php 序列化串的资料

JavaScript 版本(stable):http://www.devpro.it/code/102.html 
Perl 版本(stable):http://hurring.com/code/perl/serialize/ 
另一个 Perl 版本:http://www.cpan.org/modules/by-module/PHP/JBROWN/php-serialization/ 
Python 版本(beta):http://hurring.com/code/python/serialize/ 
Java 版本(pre-alpha):http://hurring.com/code/java/serialize/ 
Ruby 版本:http://www.aagh.net/files/ruby/php_serialize.rb 
Flash/Actionscript 版本:http://sourceforge.net/projects/serializerclass/ 
C# 版本:http://sourceforge.net/projects/csphpserial/ 


a3)反序列化漏洞产生的原理。

    反序列化漏洞就出在反序列化(把数据转化成对象)的过程中。在这个过程中,应用需要根据数据的内容,去调用特定的魔术方法。而黑客正是利用这个逻辑,在数据中嵌入自定义的代码(比如执行某个系统命令)。应用对数据进行反序列化的时候,会执行这段代码,从而使得黑客能够控制整个应用及服务器。这就是反序列化漏洞攻击的过程。

     

 a3.1)利用 php中的魔术方法。

__construct() 当创建对象时触发,一般用于初始化对象,对变量赋初值
__destruct() 当一个对象被销毁时触发
__sleep() 使用serialize()时自动触发 
__wakeup() 使用unserialize()时自动触发
__toString() 当一个类被当成字符串使用时触发
__invoke() 当尝试以调用函数的方式调用一个对象时触发
__call() 在对象上下文中调用不可访问的方法时触发 
__callStatic() 在静态上下文中调用不可访问的方法时触发 
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发

B)反序列化漏洞的危害.

b1)通过反序列化漏洞,被黑客在内容中嵌入了执行自定义命令的代码,被获得了服务器权限。

   Runtime.exec(...)

   eVal()   

b2) 被黑客构造了一些特殊的(虽然没有执行命令,但嵌套深度很深)导致服务器cpu耗尽(进而停止服务).

   

C)反序列化漏洞获得途径。

  1)源码审计,发现魔术函数,自定义反序列化函数,可利用的pop链,php本身的漏洞

  2)猜测等手段确认对方的系统是基于某些开源框架或系统开发的。

  3)漏洞报告。

  

D)反序列化漏洞的实例。

E)反序列化漏洞的防御。

e1)签名与认证。

如果序列化的内容没有用户可控参数,仅仅是服务端存储和应用,则可以通过签名认证,来避免应用接受黑客的异常输入。   

e2)限制序列化与反序列化的类。

    增加一层序列化和反序列化接口类。这就相当于允许提供了一个白名单的过滤:只允许某些类可以被反序列化。只要你在反序列化的过程中,避免接受处理任何类型(包括类成员中的接口、泛型等),黑客其实很难控制应用反序列化过程中所使用的类,也就没有办法构造出调用链,自然也就很难利用反序列化漏洞了

e3)RASP检测。

(Runtime Application Self-Protection,实时程序自我保护)。RASP 通过 hook 等方式,在这些关键函数(例如:序列化,反序列化)的调用中,增加一道规则的检测。这个规则会判断应用是否执行了非应用本身的逻辑,能够在不修改代码的情况下对反序列化漏洞攻击实现拦截.


参考资料