在二进制安全领域,Return-oriented programming(ROP)
是一种常用的绕过防护攻击方式。攻击者可以利用内存中已有的程序片段(称作gadgets
),通过汇集这些程序片构建一个攻击途径(称作gadget chains
)。 在2009年,Esser提出这种代码复用产生攻击的思想在PHP系统中同样适用[1][2]。
0x01 PHP对象注入攻击
PHP对象注入攻击本质上属于一种代码复用技术。那么为何代码可以复用?得益于魔术方法,在一定的条件下魔术方法可以不被调用直接触发,得以代码复用。 这种触发形式层层相扣,在魔术方法中触发另一个类的魔术方法,这便形成了POP链。
POP链的入口点在哪? 触发魔术方法,我们需要这个类在内存中的对象值。而像数组、对象这种复杂数据结构在非内存使用的情况下通常是以序列化形式存在。我的理解是将复杂数据结构转化成另一中便于存储或者传输的形式存在,比如存入数据库,显然一个字符串更合适。当程序调用时在还原成原来的形式调入内存,这就是反序列化。 反序列化的对象当然能触发类中的魔术方法,从而触发POP链。
string serialize ( mixed $value ) serialize() 返回字符串,此字符串包含了表示 value 的字节流,可以存储于任何地方。 这有利于存储或传递 PHP 的值,同时不丢失其类型和结构。
0x02 TypechoPOP链
typecho/install.php **第230行反序列化操作,在内存中还原数据。 第232行new了一个Db类,触发指定__construct函数,参数可控 第120行进行了字符串连接操作,如果$adapterName是一个object结构数据,触发__toString函数。**
在项目中搜索包含__toString方法的任意类
搜索项目发现了3处包含__toString方法的 找到下一个可利用的魔术方法点或者漏洞触发点
Typecho/Feed.php->__toString() ... **在第290行,出现了object->attr结构,对象名可控,此时,只要类内不包含screenName这个属性,便会触发__get方法。**
在项目中搜索包含__get方法&&没有screenName属性的任意类
**搜索项目发现了13处包含__get方法的类(盗图)**
找到下一个可利用的魔术方法点或者漏洞触发点
/Typecho/Request.php 调函数 再调函数 最终跳到了第164行,两个参数都可控,命令执行。 简单总结一下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//install.php
$config=unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
$db=new Typecho_Db($config['adapter'], $config['prefix'\]);
//Db.php
Public function __construct($adapterName,$prefix='typecho_')
$adapterName='Typecho_Db_Adapter_'.$adapterName;
//Feed.php
Public function__toString()
<description>'.htmlspecialchars($this->_subTitle).'</description>
//Request.php
Public function __get($key)
Return $this->get($key);
Public function get($key,$default=NULL)
Return $this->_applyFilter($value);
Private function_applyFilter($value)
call_user_func($filter,$value);
[1] Esser, S. Shocking News in PHP Exploitation. In Power of Community (POC) (2009). [2] Esser, S. Utilizing Code Reuse Or Return Oriented Programming in PHP Applications. In BlackHat USA (2010).