sqlmap time-based inject 分析

1.前言

之前一次会议分享了sql注入检测方法,所以@chengable大牛问我sql注入咋检测的?我说在甲方做安全,sql注入检测还是比较好做的。
1)报错注入检测。
2)别做bool的报错注入,误报比较高。
3)做基于time-based的时间注入,联系运维做上慢日志db记录,监控sleep,benchmark的关键字监控,可以在sleep的时间小数点上加上扫描任务的id号,方便定位(ps,这种方法能找到99%的sql注入了)。

 

所以我在做3)的时候把时间误差限制非常苛刻,但是@chengable做乙方的,3)做不了。。问了一下@chengable,他大概提到了自己先过滤存在注入点的情况再加上sqlmapapi.py检测。

早之前我也用了sqlmap做检测,遇到的问题就是误报多,扫描时间久,然后尝试了sqlmapapi.py,发现还是扫描时间过久的问题,而且还有一点就是他不支持json格式的注入(详情:https://github.com/sqlmapproject/sqlmap/issues/2576)

但是sqlmap的时间注入还是准的呀,又不像用sqlmapapi.py怎么办??想着把sqlmap的time-based注入的逻辑搬出来。。

2.简单分析 sqlmap的time-based注入

吐槽sqlmap的代码不规范,难看,量又大。之前貌似有大佬推荐我读一下sqlmap源码,学习一波,现在想想还好我放弃的早。。。
所以,偷懒想不看源码加上--technique=T -v 3 先看看sqlmap检测payload。

python sqlmap.py -u "http://127.0.0.1/sqli_get.php?id=1050320" --technique=T -v 3

WechatIMG4.jpeg
WechatIMG5.jpeg

貌似偷懒找到一些门道了,截图中可以看到
首先第一步sqlmap塞了sleep的注入payload:
先是塞入了 sleep(5),发现执行了之后;又塞入sleep(0),最后又塞入sleep(5)。
猜了一下大概的检查思路就是先sleep(5)秒延时成功的话,再sleep(0)如果没有发现延时现象,继续sleep(5),此时如果再次延时成功就出现认为可能有注入的提醒:

xxx appears to be `mysql >=5.0.12 and time-base blind` injenctable

最后很巧妙的是,sqlmap为了防止出现误报使用了if 的判断条件来排除误报,截图里看到sqlmap分别让等式成立测试两次,又让等式不成立测试两次,根据秒延时情况来判断误报。

3.深入分析一下源码

好了回归源码看看吧,根据前面的一些关键字,我们直接到代码里面去看看吧。比如搜索之前出现appears to be 看到第一步的代码:
sqlmap/lib/controller/checks.py: 
Screen Shot 2018-04-02 at 3.18.00 PM.png

看了一下发现和前面猜测的八九不离十。。

好了,再找一下payload在哪里,特别是if条件的payload,还是用关键字查询,发现在这:
sqlmap/xml/payloads/time_blind.xml:
Screen Shot 2018-04-02 at 3.28.47 PM.png
可以看到每个if条件的payload都在vector这个字段中

4.闭合

闭合注入点前面的字符是能否注入的关键。
观察到tools/sqlmap/xml/boundaries.xml,所以我们还需要参考这里面的多种闭合情况:
Screen Shot 2018-04-02 at 3.42.06 PM.png

5.是否延时判断

5.1)方法一
参考之前awvs的注入,我也想到一个自己比较能理解的检测方法。
取6次的无注入payload正常测试的消耗时间,计算平均值为原生请求时间(ori_time)

当注入时间为sleep(5),将当前时间减去ori_time,作为sleep_time。如果sleep_time小于4认为延时没有发生。(这里考虑到ori_time 收到网络影响导致变大,所以把阀值调到了四秒)

当注入时间为sleep(0),将当前时间减去ori_time,作为sleep_time。如果sleep_time大于2,说明延时有误报。

5.2)方法二
再看一下sqlmap的代码,人家用了一个我搞不懂的数学漏洞(https://blog.csdn.net/think_ycx/article/details/52083782)
跟进:Request.queryPage --->wasLastResponseDelayed 就可以看到逻辑为:
取30次的无注入payload正常测试的消耗时间,将他们放到kb.responseTimes中。
计算30次的标准差为deviation,根据deviation计算出一个最慢的响应时间为lowerStdLimit:

lowerStdLimit = average(kb.responseTimes[kb.responseTimeMode]) + TIME_STDEV_COEFF * deviation

它的值为30次的平均值加上TIME_STDEV_COEFF*标准差(deviation),至于TIME_STDEV_COEFF设置为7可以使得判断的准确度在99.9999999997440%这个对于数学不好的我确实不太懂为什么。。。。- -||

最后判断当前这次的请求的消耗时间是不是大于lowerStdLimit,大于说明延时发生,小于说明没有(另外当lowerStdLimit小于0.5秒时候,lowerStdLimit取0.5秒)

感性告诉我该选方法一,理性告诉我该选方法二。。。我还是选择方法二,测了一下这个注入点:http://testphp.vulnweb.com/listproducts.php?cat=1。很稳定的扫描出注入漏洞。

6.结尾

我自己写的代码我就不贴了,话说的很多很杂,但是整个检测逻辑还是很清晰的,sqlmap的基于时间的盲注入还是学习和参考的。

您可能还会对下面的文章感兴趣: