0x00 前言

CVE-2016-5734在exploit-db上也就是 phpMyAdmin 4.6.2 - Authenticated Remote Code Execution ,意即phpMyAdmin认证用户的远程代码执行,根据描述可知受影响的phpMyAdmin所有的 4.6.x 版本(直至 4.6.3),4.4.x 版本(直至 4.4.15.7),和 4.0.x 版本(直至 4.0.10.16)。 CVE的作者利用在php 5.4.7之前的版本中preg_replace函数对空字节的错误处理Bug,使注入的代码可远程执行。本文将会对此CVE进行相关分析。

0x01 环境搭建

init

在这里我会使用LAMP进行漏洞的调试和复现,首先在VirtualBox上安装Ubuntu 14.04 Server,安装成功后我们替换一下apt的源方便之后环境的安装与下载,命令如下:

$ cd /etc/apt
$ sudo mv sources.list sources.list.bak
$ sudo vim sources.list

#我们写入aliyun的源


deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse

#最后再update一下


$ sudo apt-get update
$ sudo apt-get upgrade

apache

紧接着我们apt安装apache

$ sudo apt-get install apache2 apache2-dev

mysql

再安装mysql

$ sudo apt-get install mysql-server mysql-client

php

最后来啃啃php。由于php Bug的报告者使用的是php 5.4.0版本,我们就也安装5.4.0版本进行测试,apt-get直接安装肯定是不行了,我们就下载源码进行安装吧,首先安装一下必要的编译环境

$ sudo apt-get install build-essential

再安装在编译过程中对应选项所需要的库(你也可以根据自己的需求进行选择)

$ sudo apt-get install libcurl4-openssl-dev libedit-dev libsqlite3-dev libssl-dev libxml2-dev

然后我们就来下载源码进行编译吧

$ wget http://museum.php.net/php5/php-5.4.0.tar.bz2
$ tar jxf php-5.4.0.tar.bz2
$ cd php-5.4.0/
$ ./configure --with-apxs2 --enable-ftp --enable-mbstring --with-curl --with-openssl --with-zlib --with-libedit --with-mysql --enable-embedded-mysqli
$ sudo make

可是在编译过程当中出现了make: *** [ext/dom/node.lo] Error 1的错误,在github上也有人给了解决方案

$ curl -s https://mail.gnome.org/archives/xml/2012-August/txtbgxGXAvz4N.txt | patch -p0
patching file ext/dom/node.c
patching file ext/dom/documenttype.c
patching file ext/simplexml/simplexml.c

$ sudo make test
$ sudo make install

至此编译的过程已经完成,这里还需要配置一下apache来解析php。在/etc/apache2/apache2.conf的最后加上AddHandler application/x-httpd-php .php,然后sudo service apache restart重启apache,查看是否能够解析php

0x02 核心剖析

我们LAMP的环境搭建好了,让我们来看看这个PHP版本的Bug是怎么一回事。漏洞描述中有说php是在5.4.7中修复了该Bug,我们可以在php的changelog中查找其修复的Bug,据此定位到和该CVE相关的#55856 preg_replace should fail on trailing garbage问题。

在php代码中,和mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )函数相关的代码执行漏洞通常是使用了e修饰符,我们的demo如下:

<?php
    $raw = $_POST['raw'];
    $replace = $_POST['replace'];
    $text = $_POST['text'];

    $text = preg_replace('/'.$raw.'/e', $replace, $text);
?>

preg_replace函数当中,加了e修饰符之后,在subject中匹配到pattern后会执行replacement中的代码,将执行结果进行替换,我们这里可以使用php的system函数,可以直接输出命令执行的结果,如图

如果我们的demo变成了如下的代码,还会有漏洞吗

<?php
    $raw = $_POST['raw'];
    $replace = $_POST['replace'];
    $text = $_POST['text'];

    $text = preg_replace('/'.$raw.'/i', $replace, $text);
?>

55856这个Bug就在这里发挥了作用,当php版本小于5.4.7时,可以向pattern中注入空字符产生截断,并传入e修饰符,使得我们可控的replacement代码执行,如图

0x03 漏洞分析

exploit-db中提供的Vulnerable App是phpMyAdmin 4.6.2,下载下来直接解压后访问的结果如下

/libraries/common.inc.php的57行也可以知道4.6.x的版本要求php的版本是要大于5.5.0的

而在phpMyAdmin 4.4.15.6中则要求php版本大于5.3.0即可,我们就下载4.4.15.6版本的进行分析测试

根据exp我们可以知道此脚本利用的要求有两点

  1. 知道phpMyAdmin的路径,并且可以使用账号密码登录成功
  2. 知道对应db的table,或者在db中有创建table的权限

其次,在exp的以下代码中我们能够知道漏洞的触发点在于tbl_find_replace.php脚本中

    # build exploit
    exploit = {
        "db": db,
        "table": table,
        "token": token,
        "goto": "sql.php",
        "find": "0/e\0",
        "replaceWith": payload,
        "columnIndex": "0",
        "useRegex": "on",
        "submit": "Go",
        "ajax_request": "true"
    }
    resp = s.post(
        url_to_pma + "/tbl_find_replace.php", exploit, cookies=requests.utils.dict_from_cookiejar(s.cookies)
    )

tbl_find_replace.php的24行中,将POST的findreplaceWwith传入到了$table_searchgetReplacePreview方法中

$table_searchPMA_TableSearch对象实例化的结果,而PMA_TableSearchlibraries/TableSearch.class.php中进行了定义,我们定位到libraries/TableSearch.class.php的1430行

getReplacePreview方法中,由于exp中设定了$useRegex的值,所以参数继续传递到_getRegexReplaceRows方法中,定位到1388行

在1408~1413行当中,preg_replace("/" . $find . "/", $replaceWith, $row[0]);很经典地还原了55856 Bug的场景,exp中通过 "find": "0/e\0","replaceWith": payload,的POST提交,空字节截断并传入e修饰符进而执行我们的payload,也将替换的结果进行了回传,最终的复现如下

0x04 总结

个人感觉此CVE的利用还是有些版本和用户信息的要求,漏洞环境的搭建虽然有些小坑,但是根据exp逆向追踪漏洞根源的过程比较简单,在php Bug下所造成的漏洞还需要多积累相关经验去实践去挖掘