网安学习第24天 PHP安全——PHP反序列化
一、序列化与反序列化1、序列化serialize()序列化是什么序列化就是把程序中的对象、数组、结构体等复杂数据转换成可以存储或传输的格式。简单说把“内存里的对象”变成“字符串/字节流”。例如 PHP 中有一个对象$user [ name admin, age 18 ];序列化后可能变成a:2:{s:4:name;s:5:admin;s:3:age;i:18;}其中各个字段代表的含义常见序列化格式包括JSON XML YAML PHP serialize Java Serializable Python pickle2、反序列化unserialize()序列化是什么反序列化就是把序列化后的字符串/字节流还原成程序中的对象或数据结构。简单说把“字符串/字节流”重新变回“内存里的对象”。例如$data a:2:{s:4:name;s:5:admin;s:3:age;i:18;}; $user unserialize($data);反序列化后$user又变成了数组[ name admin, age 18 ]所以序列化与反序列化 的操作可以直观地看作为3\3、为什么反序列化会有漏洞问题核心在于如果程序反序列化了用户可控的数据就可能被攻击者构造恶意对象。危险点不是“反序列化”本身而是反序列化的数据来自用户输入 程序中存在危险的魔术方法/特殊方法 这些方法里有文件操作、命令执行、SQL操作等危险逻辑就可能形成反序列化漏洞。4. PHP 反序列化漏洞例子例如 PHP 中有一个类?php class Test { public $cmd; function __destruct() { system($this-cmd); } } ?这里的__destruct()是魔术方法对象销毁时会自动执行。如果程序这样写$data $_GET[data]; unserialize($data);那么攻击者可以构造一个恶意序列化字符串O:4:Test:1:{s:3:cmd;s:2:id;}反序列化后程序会创建Test对象并且$cmd id。脚本结束时触发__destruct()于是执行system(id);这就可能造成RCE远程命令执行。二、常见的魔术方法__construct(): //当对象new的时候会自动调用__destruct()//当对象被销毁时会被自动调用__sleep(): //serialize()执行时被自动调用__wakeup(): //unserialize()时会被自动调用__invoke(): //当尝试以调用函数的方法调用一个对象时会被自动调用__toString(): //把类当作字符串使用时触发__call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。__callStatic(): //在静态上下文中调用不可访问的方法时触发__get(): //读取对象属性时,若存在,则返回属性值;若不存在则会调用__get函数__set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用__set函数。__isset(): //在不可访问的属性上调用isset()或empty()触发__unset(): //在不可访问的属性上使用unset()时触发__set_state()调用var_export()导出类时此静态方法会被调用__clone()当对象复制完成时调用__autoload()尝试加载未定义的类__debugInfo()打印所需调试信息三、反序列化漏洞利用1、POP链POP 链就是 PHP 反序列化漏洞里的“调用链”。全称Property-Oriented Programming Chain面向属性编程链。它的核心是攻击者通过控制反序列化对象的属性让程序自动触发魔术方法然后一步步调用已有代码最后到达危险函数。POP常用于上层语言构造特定调用链的方法序列化攻击都在PHP魔术方法中出现可利用的漏洞因自动调用触发漏洞但如关键代码没在魔术方法中而是在一个类的普通方法中。这时候就可以通过构造POP链寻找相同的函数名将类的属性和敏感函数的属性联系起来。2. POP 链的基本结构一条 POP 链一般有三部分入口点 Trigger ↓ 中间跳板 Gadget ↓ 危险终点 Sink入口点 Trigger一般是魔术方法例如__destruct() __wakeup() __toString()中间跳板 Gadget就是普通业务代码中的方法比如$this-obj-run(); $this-handler-save(); $this-logger-write();这些方法本来可能不是漏洞但可以被攻击者利用来继续调用下一个对象。危险终点 Sink最终危险操作例如system() eval() assert() include() require() file_put_contents() unlink()3. 一个简单例子看这三个类class A { public $b; public function __destruct() { $this-b-run(); } } class B { public $c; public function run() { $this-c-write(); } } class C { public $filename; public $content; public function write() { file_put_contents($this-filename, $this-content); } }单独看A::__destruct() B::run() C::write()都像正常代码。但是如果程序中有unserialize($_COOKIE[user]);攻击者就可能构造对象关系A 对象 └── b B 对象 └── c C 对象 ├── filename 可控文件名 └── content 可控内容执行流程变成unserialize() ↓ 恢复 A 对象 ↓ 脚本结束触发 A::__destruct() ↓ 调用 $this-b-run() ↓ 进入 B::run() ↓ 调用 $this-c-write() ↓ 进入 C::write() ↓ file_put_contents($filename, $content)这就是一条 POP 链。三、属性类型1、对象变量属性public(公共的):在本类内部、外部类、子类都可以访问protect(受保护的):只有本类或子类或父类中可以访问private(私人的):只有本类内部可以使用2、序列化数据显示public属性序列化的时候格式是正常成员名private属性序列化的时候格式是%00类名%00成员名protect属性序列化的时候格式是%00*%00成员名3、例?php header(Content-type: text/html; charsetutf-8); //public private protected说明 class test{ public $namexiaodi; private $age31; protected $sexman; } $anew test(); $aserialize($a); print_r($a); ?输出为O:4:test:3:{s:4:name;s:6:xiaodi;s:9: test age;s:2:31;s:6: * sex;s:3:man;}其中age —— test age sex —— * sex四、漏洞绕过1、__wakeup绕过为什么要绕过__wakeup()因为有些反序列化利用链中__wakeup()会破坏攻击者控制的属性。比如class Test { public $cmd; public function __wakeup() { $this-cmd echo safe; } public function __destruct() { system($this-cmd); } }攻击者本来想控制$cmd id;但是反序列化时会先触发__wakeup()把$cmd改成echo safe这样 POP 链就被破坏了。所以绕过__wakeup()的目的就是让对象恢复出来 但是不执行 __wakeup() 保留攻击者控制的属性 等到 __destruct() 或其他方法触发当属性数量不一致时会绕过__wakeup()正常序列化对象O:4:Test:1:{s:3:cmd;s:2:id;}这里属性数量是1。因为只有一个属性$cmd如果把属性数量故意改大O:4:Test:2:{s:3:cmd;s:2:id;}注意这里属性数量从 1 改成了 2。但实际后面只有一个属性。在部分老版本 PHP 中这种属性数量不一致会导致对象仍然可能被反序列化 但是 __wakeup() 不会被调用于是就达到了绕过效果。2、字符逃逸字符逃逸在 PHP 反序列化里核心意思是原本应该被当成“普通字符串内容”的字符因为长度变化或过滤处理逃出了字符串边界被unserialize()当成了新的序列化结构来解析。它经常出现在serialize() ↓ 字符串过滤 / 替换 ↓ unserialize()这个流程里。参考php反序列化-字符逃逸看这一篇就够了_php 反序列化-CSDN博客五、反序列化链项目1、-NotSoSecurehttps://github.com/NotSoSecure/SerializedPayloadGenerator为了利用反序列化漏洞需要设置不同的工具如 YSoSerial(Java)、YSoSerial.NET、PHPGGC 和它的先决条件。DeserializationHelper 是包含对 YSoSerial(Java)、YSoSerial.Net、PHPGGC 和其他工具的支持的Web界面。使用Web界面您可以为各种框架生成反序列化payload.Java – YSoSerial NET – YSoSerial.NET PHP – PHPGGC Python - 原生2、-PHPGGChttps://github.com/ambionics/phpggcPHPGGC是一个包含unserialize()有效载荷的库以及一个从命令行或以编程方式生成它们的工具。当在您没有代码的网站上遇到反序列化时或者只是在尝试构建漏洞时此工具允许您生成有效负载而无需执行查找小工具并将它们组合的繁琐步骤。 它可以看作是frohoff的ysoserial的等价物但是对于PHP。目前该工具支持的小工具链包括CodeIgniter4、Doctrine、Drupal7、Guzzle、Laravel、Magento、Monolog、Phalcon、Podio、ThinkPHP、Slim、SwiftMailer、Symfony、Wordpress、Yii和ZendFramework等。七、反序列化框架利用-ThinkPHPYiiLaravel通过上述工具构造payload实现反序列化