0x00 TL;DR
sqlmap 使用一种极其巧妙的方式组合生成一个完整的 payload,一个完整的 payload 由如下几个部分组成:
<prefix> <test> <comment> <suffix>
其中 prefix、comment、suffix 作为 boundary,boundary 用于闭合注入点的前后部分;test 则是最终如果闭合成功后必然执行的语句。
以 ' AND EXTRACTVALUE(9559,CONCAT(0x5c,0x716b626b71,(SELECT (ELT(9559=9559,1))),0x717a626b71)) AND 'WBCc'='WBCc
为例,其中 '
为 prefix、AND 'WBCc'='WBCc
为 suffix,这条语句中不存在 comment,AND EXTRACTVALUE(9559,CONCAT(0x5c,0x716b626b71,(SELECT (ELT(9559=9559,1))),0x717a626b71))
为 test。
从上面的解析可以发现 prefix 与 suffix 是具有通用性的,与 target 使用哪个数据库或测试时使用哪种 sql 注入方式无关,而 test 是与目标数据库、注入类型强关联的。
因此 sqlmap 将 prefix 与 suffix 单独作为 boundaries 保存,而 test 和 comment 则根据注入方式和数据库的不同被划分为六个文件,对应如下:
0x01 test
一个完整的 test 具有的字段如下:
<test>
<title></title>
<stype></stype>
<level></level>
<risk></risk>
<clause></clause>
<where></where>
<vector></vector>
<request>
<payload></payload>
<comment></comment>
<char></char>
<columns></columns>
</request>
<response>
<comparison></comparison>
<grep></grep>
<time></time>
<union></union>
</response>
<details>
<dbms></dbms>
<dbms_version></dbms_version>
<os></os>
</details>
</test>
1.0 title
当前测试的标题,需要能够体现这个测试的注入类型、注入位置等。
1.1 stype
当前测试对应的注入方式,共有五个可选值。
- 1 – 布尔注入
- 2 – 报错注入
- 3 – 内联注入
- 4 – 堆叠注入
- 5 – 延时注入
- 6 – 联合注入
1.2 level
当前测试的等级,级别越高发送的请求数越多,共有五个可选值。
- 1 – 请求量小于100
- 2 – 请求量在100到200之间
- 3 – 请求量在300到500之间
- 4 – 请求量在500到1000之间
- 5 – 请求量大于1000
1.3 risk
当前测试的威胁级别,级别越高产生的威胁越大,共有三个可选值。
- 1 – 低级威胁(无威胁)
- 2 – 中级威胁(使用延时的 payload 会造成数据库性能损失)
- 3 – 高级威胁(使用带有 OR 的 payload 可能会造成数据库数据损失)
1.4 clause
当前测试的 payload 在哪个字段生效,共有十个可选值。
- 0 – 总是生效
- 1 – 仅在 where 和 having 语句中生效
- 2 – 仅在 group by 语句中生效
- 3 – 仅在 order by 语句中生效
- 4 – 仅在 LIMIT 语句中生效
- 5 – 仅在 OFFSET 语句中生效
- 6 – 仅在 TOP 语句中生效
- 7 – 仅在 table 字段上生效
- 8 – 仅在 column 字段上生效
- 9 – Pre-WHERE (non-query)
1.5 vector
当前测试的 payload 模板,没有实际作用(?),只是说明 payload 的具体格式。
1.6 request
注入测试时所发送的请求中的具体信息。
填充到 <test>
中的具体 payload,其中具有一些模板值,后续会在发包前进行替换。
填充到 <comment>
中的注释。
union注入测试暴力破解列数时所使用的字符。
union 注入测试时对应的列范围。
1.7 response
此字段指定当前 test 根据什么来判定是否注入成功。
使用相似度比对算法来判定注入是否存在,False Page 会使用 comparison 指定的 payload 进行发包后与 True Page 进行相似度比对从而判定是否存在注入。
使用正则匹配的方式来判定是否注入成功。
使用响应延时的方式来判断是否注入成功。
在 union 注入时使用 unionTest 函数来判断是否注入成功。
1.8 details
此字段用于指定当前 test 如果注入成功了可以推断出的信息。
指定可以推断出的数据库名称,即当前注入成功了可以推断出后端使用的是什么数据库。
指定可以推断出的数据库版本,即当前注入成功了可以推断出后端数据库的版本(一个范围而非固定值)。
指定可以推断出的操作系统名称,即当前注入成功了可以推断出后端的操作系统名称。
0x02 boundary
一个完整的 boundary 具有的结构如下:
<boundary>
<level></level>
<clause></clause>
<where></where>
<ptype></ptype>
<prefix></prefix>
<suffix></suffix>
</boundary>
其 level、clause、where 三个字段与 test 的意义相同,下面不再重复解释。
2.0 ptype
测试参数的类型,共有六个可选值。
- 1 – 没经过转义的数字(int 型)
- 2 – 被单引号包裹的字符串
- 3 – 被单引号和其它符号(
)、%
)包裹的字符串 - 4 – 被双引号包裹着的字符串
- 5 – 标识符(比如 table-name、column-name…)
2.1 prefix
填充到 <prefix>
中的具体值,用于闭合前面的语句。
2.2 suffix
填充到 <suffix>
中的具体值,用于闭合后面的语句。
0x03 示例解析
上面介绍了 test 与 boundary 的具体结构以及各个字段的具体含义,也了解了在 sqlmap 中是由这两部分组合生成的完整 payload,实际进行注入测试时也是如此,下面笔者通过一个具体的示例演示两者是如何进行组合的。
示例 boundary:
<boundary>
<level>1</level>
<clause>1</clause>
<where>1,2</where>
<ptype>2</ptype>
<prefix>'</prefix>
<suffix> AND '[RANDSTR]'='[RANDSTR]</suffix>
</boundary>
示例 test:
<test>
<title>AND boolean-based blind - WHERE or HAVING clause</title>
<stype>1</stype>
<level>1</level>
<risk>1</risk>
<clause>1,8,9</clause>
<where>1</where>
<vector>AND [INFERENCE]</vector>
<request>
<payload>AND [RANDNUM]=[RANDNUM]</payload>
</request>
<response>
<comparison>AND [RANDNUM]=[RANDNUM1]</comparison>
</response>
</test>
笔者这里选取的 test 是 boolean-blind 中的,在进行注入测试时会发出两个请求分别对应 True Page 与 False Page。
其中 True Page 所使用的完整 payload 如下:
' AND 1122=1122 AND 'xxx'='xxx
False Page 所使用的完整 payload 如下:
' AND 1122=8923 AND 'xxx'='xxx
从上面这一部分结合 boundary 与 test 即可看出每个部分是由哪个字段进行填充的。
上面这个 payload 可以用于测试单引号包裹的 SQL 注入点,比如:
SELECT * FROM users where username='$_GET["injection"]';