[送0day]代码审计就该这么来2 Mlecms 注入【系列二】【转】

本文原创作者:索马里的海贼,本文属春秋原创奖励计划,未经许可禁止转载!

前言
文章都是边审边写的,尽量拿论坛里前人分析过的cms来做例子,看到什么问题说什么,能不能系列下去主要看能不能挖到新的漏洞。。。
上一篇说了一般审计中的流程,有的时候我们的目的并不是完整的审计一套系统,而是在搞事的过程中发现目标站或者旁站或者关联站点为开源cms,这时候的目的是尽快拿到可利用的漏洞。这时候当然也可以按部就班慢慢从框架流程看起,不过估计大伙都没这个心思,那么再来说说一些快速发现的漏洞挖掘方法。

一、关注重点
为了搞事,所以当然是从比较严重的问题开始看,xss csrf这些留到最后,先看命令执行,文件操作,sql这3块。这次主要说sql(因为发现的就是sql注入)
sql注入快速挖掘,第一步先看有没有全局的防御机制 比如很多cms都会在全局配置文件里有这么一块

[AppleScript] 纯文本查看 复制代码
1
2
3
foreach(array('_GET','_POST','_COOKIE') as $key=>$value){
    $$key = daddslashes($value);//daddslashes是addslashes函数的扩展版 增加了对数组的循环转义
}


比如这次目标Mlecms的代码 inc/include/globals.php行24

[PHP] 纯文本查看 复制代码
1
2
3
4
5
foreach(array('_GET','_POST','_COOKIE') as $_request){
    foreach($$_request as $i => &$n){
        ${$i} = daddslashes($n);
    }
}


类似这样的代码,把GPC的内容都转义了一遍 碰上这样的情况 就不要再去想简单的跟踪请求参数来注入了
直接去找能重新引入单引号或者反斜杠的地方 比如下面的一些例子

substr() //取到转义处,留下反斜杠
$sth[‘xx’] //当变量为字符串时 取的是xx位置的一个字符
stripslashes() //这三个就不解释了
urldecode() //这三个就不解释了
base64_decode() //这三个就不解释了
parse_str() //parse_str之前会先urldecode

或者直接找除了GPC之外的能提交的内容 比如常见的X-Forwarded-For的注入 referer的注入
它们都来自$_SERVER数组 而且经常被认为是安全的而不进行处理。当然现在大伙儿(码农)意识都高了(其实是被搞怕了)
再不济也知道从X-Forwarded-For取到IP之后做个转义,更多的是拿到IP之后用正则校验一下是不是正常的IP
比如目标CMS

[PHP] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
function get_ip(){
    if(!empty($_SERVER["HTTP_CLIENT_IP"])){
        $cip = $_SERVER["HTTP_CLIENT_IP"];
    }else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"])){
        $cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
    }else if(!empty($_SERVER["REMOTE_ADDR"])){
        $cip = $_SERVER["REMOTE_ADDR"];
    }else{
        $cip = '';
    }
    preg_match("/[\d\.]{7,15}/",$cip,$cips);
    $cip = isset($cips[0]) ? $cips[0] : 'unknown';
    unset($cips);
    return $cip;
}


可以看到最后用”/[\d\.]{7,15}/”这条正则匹配了一下 这里不说这样判断IP是否正确,至少除了数字和点之外 混不进其他字符了  没有了单引号也就几乎没有了注入的可能。

二、实战
上一段说了不少能引入单引号或者绕过全局转义的地方,那么就来实战一下
这里从$_SERVER开始,为了快速发现问题 一些搜索技巧必须熟练 比如这里我用的一条正则

[PHP] 纯文本查看 复制代码
1
(SELECT|INSERT|UPDATE|DELETE).*\$_SERVER


简单
给不懂正则的童鞋解释一下这条正则的意思是
有(SELECT|INSERT|UPDATE|DELETE)其中之一且后面出现了$_SERVER
如果能在sql语句拼接中出现不会被转义的$_SERVER内容的话,很有可能就是一个注入了
不得不说,审代码 运气也很重要,还真被我搜到一处
inc/lib/admin.lib.php 行46

[AppleScript] 纯文本查看 复制代码
1
2
3
4
5
6
public static function logs($type,$info){
    global $db,$admin_config,$gmt_time;
    if($admin_config['logs_open'] == 1){
        $sql = "INSERT INTO `{$db->prefix}logs` (`type`,`info`,`pageurl`,`lang`,`username`,`ip`,`ipaddress`,`addtime`) VALUES ('{$type}','{$info}','http://{$_SERVER['SERVER_NAME']}".substr(get_url(),0,200)."','".LANG."','{$_SESSION['admin']['login']['username']}','".get_ip()."','".ip::get_address(get_ip())."','".$gmt_time."');";
        $db->execute($sql);
    }

$_SERVER[‘SERVER_NAME’] 被拼进了sql语句,$_SERVER[‘SERVER_NAME’]是什么鬼,能吃么?能伪造么?
答案是肯定的 来看看php官方对
SERVER_NAME的定义
中文版

‘SERVER_NAME’
当前运行脚本所在的服务器的主机名。如果脚本运行于虚拟主机中,该名称是由那个虚拟主机所设置的值决定。

英文版

‘SERVER_NAME’
The name of the server host under which the current script is executing. If the script is running on a virtual host, this will be the value defined for that virtual host.

Note: Under Apache 2, you must set UseCanonicalName = On and ServerName. Otherwise, this value reflects the hostname supplied by the client, which can be spoofed. It is not safe to rely on this value in security-dependent contexts.

有没有发现 英文版多了点东西
简单翻译一下:
在apache2 下 如果你没有设置ServerName或者没有把UseCanonicalName 设置为 On的话,这个值就会是客户端提供的hostname  不安全哟
所以老师说英语学得好 爸爸回家早 噢不 渗透搞得好
那么这个客户端提供的hostname是什么鬼呢,其实就是http包中的Host: 字段的值
那么又有人说了,如果修改了Host字段的值,那配置了ServerName的web容器怎么判断是哪个虚拟主机呢
这里就要说另一个有意思的事了,如果我们的http包中有两个或者多个Host值 会是什么情况呢
对于apache来说,不管你有多少个 它只取第一个
对于php来说,不管你有多少个,它全要了 是的 全要了。。
比如
Host:a.com
Host:b.com
php的$_SERVER[‘SERVER_NAME’]会取到
a.com,b.com
用逗号分隔的两个host值 这下就好办了

三、利用
logs函数在后台登陆的时候会被调用,不管登录成功与否,都会调用logs()函数写入登录记录,利用的话  随便填账号密码   burp或者fiddler拦下来 添加一个Host头 然后查看结果就好了
如图



总结
写文章好累。。。
快速漏洞挖掘需要积累常见漏洞触发点,函数,语言特性等等。
顺便建议不会正则的看看正则表达式入门,提高搜索效率、挖漏洞也能碰上不少正则,到时候看不懂错失0day就亏了。

此条目发表在经验技术分类目录。将固定链接加入收藏夹。