简单提一下,PHP的unserialize()函数采用一个字符串并将其转换回PHP对象。
一般情况下,当需要存储PHP对象或通过网络传输它时,首先要使用serialize()打包。
serialize(): PHP object -> plain old string that represents the obj
之后当再次使用该数据时,可以使用unserialize()函数解包并获取所需的数据。
unserialize(): string containing object data -> original object
我们可以查询PHP官方文档,从中可以了解到unserialize()从存储的表示形式创建PHP值,并采用单个序列化变量,最后将其转换回PHP值。
它带有两个参数:str和options。
str是包含加载反序列化的序列化字符串的参数。
options是包含控制某些功能行为的选项的数组。
特别是在unserialize()中,唯一有效的用户定义选项为allowed_classes. allowed_classes用来指定接收的类名称。
接下来,我将进一步研究allowed_class。
注:事实上,当unserialize()遇到不被接受的类对象时,该对象将被实例化为 _PHP_Incomplete_Class。
运作流程:
1.magic 方法
magic 方法是PHP中具有 magic 属性的函数名称。
具体与之相关的有两种,_wakeup()和_destruct()。此时,如果序列化对象的类实现了以上两种方法之一,则在对类的对象调用unserialize()时,这些方法将自动执行。
接着就要提到反序列化先决条件,在PHP中序列化对象时,serialize() 会将所有属性保存在该对象中。但是它不会存储对象类的方法,而只会存储类的名称。因此,为了取消序列化对象,必须预定义或自动加载对象的类。换句话说,该类的定义需要存在于你将对象反序列化unserialize()的项目文件中。
如果没有在该项目文件中定义该类,则该对象将被实例化为_PHP_Incomplete_Class,此刻它不具备任何方法,并且该对象实际上是无效的。
2.对象实例化
实例化是程序在内存中创建类的实例时,利用unserialize()所实现的。它采用序列化的字符串,该字符串指定要创建的对象的类以及该对象的属性。使用该字符串数据,unserialize()创建原始序列化对象的副本。然后它会检索程序中名为_wakeup()的函数,并在为该类定义的函数中执行相关代码。因为调用_wakeup()可以重建对象可能具有的任何资源,所以它通常被用于重建在序列化过程中可能丢失的数据库链接,并执行其他初始化任务。
3.程序执行
程序可以对对象进行一系列的操作,并使用它执行其他操作。入,宽字节注入依旧会产生:
4.对象销毁
函数的相关执行流程已经大致介绍完毕,那具体的unserialize()中的漏洞是如何发生的呢?
当攻击者控制传递给unserialize() 的序列化对象时,他可以控制所创建对象的属性。然后,通过控制传递给自动执行的方法,如_wakeup()的值,这将使攻击者有机会劫持应用程序流。
这被称为PHP对象注入。根据对象在程序发生的位置,PHP对象注入可以导致代码执行,SQL注入,路径遍历或DoS。
例如,请考虑以下易受攻击的代码片段:
攻击者可以使用此反序列化漏洞来实现RCE攻击,因为用户提供的对象被传递给反序列化,并且Example2类具有在用户提供的输入上运行 eval() 的魔术函数。
要利用此RCE,攻击者只需将其数据 cookie 设置为一个序列化的Example2对象,并将hook属性设置为他想要执行的任何PHP代码。他可以使用以下代码片段生成序列化的对象:
然后将上面生成的字符串传递到数据 cookie中将导致phpinfo() 被执行。一旦攻击者将序列化的对象传递到程序中,将引起以下问题:
攻击者将序列化的Example2对象作为数据 cookie 传递到程序中;
该程序在数据 cookie 上调用unserialize();
因为数据 cookie是序列化的Example2对象,所以unserialize()实例化一个新的Example2对象;
unserialize() 看到Example2类执行了_wakeup(),因此调用了_wakeup()。
_wakeup() 寻找对象的hook属性,如果它不是NULL,它将运行eval($hook)),但hook不是NULL,并设置为“ phpinfo();”,则执行eval(“phpinfo();”)
可以实现RCE。
那么知道了漏洞的形成,如何防范unserialize()漏洞呢?
为了防止发生PHP对象注入,建议不要将不受信任的用户输入传递给unserialize()。考虑使用JSON与用户之间传递序列化数据,如果确实需要将不受信任的序列化数据传递到unserialize(),请确保实施严格的数据验证,以最大程度地降低严重漏洞的风险。
References:
https://www.php.net/manual/en/function.unserialize.php
https://www.php.net/manual/en/language.oop5.magic.php
https://medium.com/swlh/diving-into-unserialize-3586c1ec97e
https://www.owasp.org/index.php/PHP_Object_Injection