一、什么是序列化与反序列化?序列化:把内存中的对象转换成可存储或传输的格式(字节流、JSON、XML等等)反序列化:把这种格式的数据还原成程序中的对象。漏洞就出在“反序列化不可信数据”这一步。程序还在还原对象的时候,可能会自动触发某些危险操作,攻击者利用这点来构造恶意数据。1.1 反序列化简介就是把一个对象,变成可以传输的字符,目的是为了方便传输。假设:我们写了一个class,这个class里存着一些变量。当这个class被实例化之后,在使用的过程中里面的一些变量的值发生了改变。以后在某些时候还会用到这个变量,如果我们让这个class一直不销毁,等着下一次要用这个class的时候,再次被调用的话会浪费系统。当我们写一个小型的项目可能没什么太大影响,但随着项目的壮大,一些小问题就会被放大产生很多麻烦。这个时候PHP跟我们说,你可以把这个对象序列化,存储成一个字符串,当你要用的时候在放他出来不就好了。这就是说把对象进行序列化存储。在PHP反序列化中,围绕sreialize()和unserialize() 这两个函数讲解。二、反序列化漏洞产生的原理sreialize()和unserialize()在PHP内部实现上是没有漏洞的,之所以会产生反序列漏洞是因为应用程序,在处理对象、魔术函数以及序列化相关问题时导致的。当传给unserialize()的参数可控时那么用户就可以注入精心构造的Payload。当进行反序列化的时候就有可能触发对象中的一些魔术方法,造成意想不到的结果。三、不同语言的典型情况很多语言在反序列化时会自动调用对象的一些生命周期方法例:Java:readObject()、finalize();PHP:__wakeip()、__destruct()、__toString()Python:__reduce__()、__setstate__()攻击者无法随意执行代码,但可以利用程序本身或第三方库中现有的类,像搭积木一样拼出一条调用链(Gadget Chain),最终实现任意命令执行。因为数据完全由攻击者控制,连类名,字段值都可以被篡改。Java:最常见。利用ObjectInputStream反序列化恶意的Serialize()对象,结合Commons Collections、Fastjson等组件完成攻击。PHP:unserialize()处理用户输入时,利用魔术方法构造POP链。Python:pickle模块的loads()明确警告不要反序列化不可信数据,因为其虚拟机可以被直接注入系统命令。.NET:BinaryFormatter、SoapFormatter已经被官方标记为危险,不应用于不可信数据。Node.js:node-serialize等第三方包如果直接反序列化用户传入的数据,也可能导致代码注入。四、危害远程代码执行:拿到服务器权限文件读写:窃取配置、源码或写入WebShell拒绝服务:通过嵌套数组、循环引用等构造“反序列化炸弹”,耗尽CPU和内存五、怎样防御禁止反序列化不可信数据(最根本)。优先使用纯数据格式如JSON,且只用库的纯数据映射模式。必须反序列化时,进行完整性检查:对反序列化数据加数字签名或加密,确保未被篡改。类白名单:严格限制允许反序列化的类(如:Java的ObjectInputFilter,PHP7.3+的allowd_classes选项)更新依赖:及时升级基础库和框架,避免使用已知含Gadget的旧版本隔离:在沙箱或者低权限进程中执行反序列化操作。总结:永远不要信任来自客户端的序列化对象,如果业务必须使用,一定要加上签名和白名单校验。六、PHP反序列化6.1 结构要理解攻击,先得看得懂序列化后的字符串。当你对一个Person类实例进行序列化,你会得到类似下面的结构。PHPclass Person { public $name = "张三"; private $age = 20; producted $sex = "boy" }TEXTO:6:"Person":3:{s:4:"name";s:6:"张三";s:11:" Person age";i:20;s:6:" * sex";s:3:"boy";}对象声明:O:6:"Person":3 表示一个对象(O),对象名长度6,类名Person,有3个属性。O :这是一个对象(Object)6:对象名称Person的长度是6个字节(P-e-r-s-o-n)"Person":对象3:这个对象有3个属性第一个属性(公开):s:4:"name";s:6:"张三";s:4:"name":数据类型是字符串,长度4,内容是name。s:6:"张三":数据类型是字符串,长度是6,内容是张三。因为张三在UTF-8编码下占6个字节(每个中文字符占3字节)如果写成s:2就会阶段报错。第二个属性(私有):s:11:" Person age";i:20这段是全串最坑、最重要的部分!表面看是:" Person age"(前面一个空格,中间一个空格)。但长度却是11,数一下隐藏字符:实际存储为:\x00+Person+\x00+age拆开数:\x00(1字节)+Person(6字节)+\x00(1字节)+age(3字节)在文本显示用\x00(空字节不可见),通用显示为空格,所以才写成" Person age"。i:20:值是整数20第三个属性(受保护):s:6:" * sex";3:"boy";表面看是" * sex",长度似乎只有4但实际上长度却是6:实际存储:\x00(1字节)+*(1字节)+\x00(1字节)+sex(3字节)属性名中带有*号,它就是受保护的属性。6.2 危险的魔术方法unserialize()执行的过程中,如果类中定义了特定方法,PHP会自动调用它们,这就成了攻击的“入口”:__wakeup():反序列化时最先自动调用,常作用作对象资源初始化。__destruct():对象被销毁时自动调用(如:脚本结束时),常被用来做“收尾”工作。__toString():对象被当做字符串使用时触发(如:echo $obj)。__call():调用对象中不存在或不可访问的方法时触发。__get()/__set():读取/赋值对象中不存在或不可访问的属性时触发。6.3 利用方式与POP链构造6.3.1 初级利用直接利用危险魔术方法。比如,控制__destruct()里的文件删除路径,实现任意文件删除。6.3.2 高级利用(POP链)通过魔术方法作为起点,调用其他类的方法,像链条一样一环扣一环,最终执行高危操作。6.3.3 自动化工具https://github.com/ambionics/phpggc6.3.4 特殊绕过__wakeup()绕过(CVE-2016-7124):当序列化字符串中表示属性个数的值大于实际个数时,可绕过__wakeup()的执行。Phar反序列化:利用phar://伪协议,在file_exists()等文件操作函数中触发元数据的反序列化,属于“无unserialize()”的利用。
一、什么是序列化与反序列化?
漏洞就出在“反序列化不可信数据”这一步。程序还在还原对象的时候,可能会自动触发某些危险操作,攻击者利用这点来构造恶意数据。
1.1 反序列化简介
二、反序列化漏洞产生的原理
sreialize()和unserialize()在PHP内部实现上是没有漏洞的,之所以会产生反序列漏洞是因为应用程序,在处理对象、魔术函数以及序列化相关问题时导致的。
三、不同语言的典型情况
很多语言在反序列化时会自动调用对象的一些生命周期方法
例:
攻击者无法随意执行代码,但可以利用程序本身或第三方库中现有的类,像搭积木一样拼出一条调用链(Gadget Chain),最终实现任意命令执行。因为数据完全由攻击者控制,连类名,字段值都可以被篡改。
四、危害
五、怎样防御
总结:永远不要信任来自客户端的序列化对象,如果业务必须使用,一定要加上签名和白名单校验。
六、PHP反序列化
6.1 结构
要理解攻击,先得看得懂序列化后的字符串。当你对一个Person类实例进行序列化,你会得到类似下面的结构。
6.2 危险的魔术方法
unserialize()执行的过程中,如果类中定义了特定方法,PHP会自动调用它们,这就成了攻击的“入口”:
6.3 利用方式与POP链构造
6.3.1 初级利用
直接利用危险魔术方法。比如,控制__destruct()里的文件删除路径,实现任意文件删除。
6.3.2 高级利用(POP链)
通过魔术方法作为起点,调用其他类的方法,像链条一样一环扣一环,最终执行高危操作。
6.3.3 自动化工具
https://github.com/ambionics/phpggc
6.3.4 特殊绕过