本文原创作者:索马里的海贼,本文属春秋原创奖励计划,未经许可禁止转载!
前言
文章都是边审边写的,尽量拿论坛里前人分析过的cms来做例子,看到什么问题说什么,能不能系列下去主要看能不能挖到新的漏洞。。。
上一篇说了一般审计中的流程,有的时候我们的目的并不是完整的审计一套系统,而是在搞事的过程中发现目标站或者旁站或者关联站点为开源cms,这时候的目的是尽快拿到可利用的漏洞。这时候当然也可以按部就班慢慢从框架流程看起,不过估计大伙都没这个心思,那么再来说说一些快速发现的漏洞挖掘方法。
一、关注重点
为了搞事,所以当然是从比较严重的问题开始看,xss csrf这些留到最后,先看命令执行,文件操作,sql这3块。这次主要说sql(因为发现的就是sql注入)
sql注入快速挖掘,第一步先看有没有全局的防御机制 比如很多cms都会在全局配置文件里有这么一块
1
2
3
|
foreach ( array ( '_GET' , '_POST' , '_COOKIE' ) as $ key = > $ value ) { $$ key = daddslashes ( $ value ) ; / / daddslashes是addslashes函数的扩展版 增加了对数组的循环转义 } |
比如这次目标Mlecms的代码 inc/include/globals.php行24
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
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开始,为了快速发现问题 一些搜索技巧必须熟练 比如这里我用的一条正则
1
|
(SELECT|INSERT|UPDATE| DELETE ).*\ $_SERVER |
简单给不懂正则的童鞋解释一下这条正则的意思是
有(SELECT|INSERT|UPDATE|DELETE)其中之一且后面出现了$_SERVER
如果能在sql语句拼接中出现不会被转义的$_SERVER内容的话,很有可能就是一个注入了
不得不说,审代码 运气也很重要,还真被我搜到一处
inc/lib/admin.lib.php 行46
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就亏了。