sqlmap payload学习


他的心灵上已被脂肪包围,久不使用的天赋,终已泯灭。
—–海明威


sqlmap的探测注入类型

1.UNION query SQL injection(联合查询注入)

联合查询就是通过union关键字将多条语句合并在一起执行,让前一条语句报错从而让后一条语句的执行结果输出到前一条语句的显位中。需要注意的是查询语句要符合1.相同列数2.相似的数据类型3.相同的编码。

示例语句:id=-1’ union select 1,(select user()),(select version())#

2.Error-based SQL injection(基于报错型注入)

报错型注入是利用了MySQL的第8652号BUG,但是用echo mysql_error();输出了错误信息时使用,

示例语句:id=’ and (extractvalue(1,concat(0x7e,(select user()),0x7e)))#

当然如果报错的数据不能回显到页面上来,就无法使用报错注入,这时候我们就可以考虑是否存在盲注。

3.Boolean-based blind SQL injection(基于布尔型的盲注)

布尔盲注的核心思想就是通过判断两种不同的页面状态来进一步推断注入语句是否被执行以及数据是否存在。
示例语句:id=1’ and length(database())>1 #
当我们输入的注入语句无法通过回显以及报错的方式来获取数据,这时候就可能存在盲注,通过判断有跟没有、对或错来判断regexp 是否匹配到数据了。

4.Time-based blind SQL injection(基于时间的盲注)

如果在测试的时候发现都不存在前面三种所说的注入,那就有可能是时间盲注,时间盲注的特点是无回显,无报错,也没有多种页面状态。

这时候就需要通过增加sleep()函数来判断注入语句的执行,而布尔则是根据页面的对错来判断。

示例语句:id=1’ and sleep(5)# 如果sleep则可以初步判断可能存在时间盲注

5.Stacked queries SQL injection(可多语句查询注入)

多语句查询注入也叫做堆叠查询,与联合查询有点相似,都可以多条语句查询,堆叠查询的关键是分号(;)比较直观的就是如果分号被过滤或者无法绕过就无法注入,当然还有一些数据库引擎不支持,权限不足等限制,实际工作中遇到的也很比较少,这里就不做过多讨论。

6.Inline queries(内联查询注入)

碰到的比较少,这里暂不做过多讨论。

sqlmap的payload文件

sqlmap根据6大类型探测技术生成的payload就放在sqlmap源码中\xml\payloads文件中

xml

可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言。

payloads相关文件解释

sqlmap通过xml语言来定义多个不同的标签组成一个payload的模板,每个标签有设置不同的等级,通过这样的方式可以实现灵活组合和调用payload。

标签的意思和作用可以在sqlmap源码的\xml文件夹中的boundaries.xml以及\xml\payloads文件夹下的.xml文件的注释中查看。

boundaries.xml
<//boundary>标签定义了sqlmap诸如语句的边界问题
level:注入的发包等级,也就是–level,共五个等级,默认是1
clause:使用的查询从句,比如having、where、order by…
where:指定如何添加前缀、payload、comment、后缀
ptype:payload的类型
prefix:payload之前输入的
suffix:payload之后输入的

payloads文件夹
test标签定义了比较详细的测试案例,这也就是我们的payload

title:标题
stype:注入的类型
level:发包等级,与boundary中的level一致
risk:风险等级,默认1,总共3(1.测试大部分测试语句,2.增加基于事件额度测试语句,3.增加OR语句的SQL注入测试)
clause:指定为每个payload使用的SQL查询从句,与boundary中一致
where:与boundary中一致
vector:指定将使用的注入模版
request:这次注入都要进行些什么
payload:测试使用的payload
comment:在payload之后,后缀之前的语句
char:联合查询中爆破的字符
columns:联合查询测试的列数范围
response:根据回显辨别这次注入的payload是否成功
comparison:使用字符串作为payload执行请求,将响应和负载响应进行对比,在基于布尔值的盲注中有效。
grep:使用正则表达式去匹配响应的主体,在显错注入中有效。
time:在响应返回之前等待的秒数。在时间盲注和堆查询注入中有效。
union:调用unionTest()方法,在联合查询中有效。
details:哪些细节可以推断出来如果这个载荷成功
dbms:系统数据库类型
dbms_version:系统数据库版本
os:操作系统类型

开始测试

测试所使用的语句:

boundary和test相结合就可以得到以下测试语句

可以看到,在boundary中指定前缀和后缀,在union_query.xml相应的位置指定测试所使用的字符以及数量

中间的union测试语句,通过使用vector标签指定了使用union模版,而构造union查询的语句来自于sqlmap\lib\core\agent.py

同时从这张图我们可以看到,查询语句之后先是comment后是suffix,这时候我们就可以对boundary中的suffix和test中的comment有一个了解。

这里不知道大家有没有注意一个问题?
在sqlmap测试语句中,我使用的level和risk是1,但在上边的boundary,payload,sqlmap相对应的那张图片中,右边的test中level是1,risk是1,标题也对应,这个正确。
但是在左边boundary中level是3,虽然level不对应但是实际产生的效果却是一致(我找遍了boundaries.xml里边没有level1并且前缀是)后缀是–随机字符串的)

我们再来看一张图片

这时候就可以对应了,没有前缀,后缀是–随即字符串


….这张图片显示的是level=4,但是我指定的是level=1,按道理来说应该不会测试更高等级的,而且经过实际测试发现虽然我指定的level=1,但是boundaries.xml里边的基本大部分(不管level是多少)都会测试。

理解一下level参数,–level命令有5个等级,默认是1,表示做最少的检测,level=2或2以上时会对cookie进行测试,3或3以上时会对user-agent和referer进行测试。

这里可以先假定这里也就是boundary中的level和我们实际使用的level并不是同一个东西(不是同一个那这个level是干嘛用的..),实际指定的level是发包数量,2及其以上会测试cookie,3及其以上会测试头,这个也就是说他其实指定了sqlmap测试的位置,当然也会发更多的包。但是在这,也就是boundaries.xml中的level和位置并没有什么别的关系,那应该就是和别的搭配有关系,这里就先暂且不分析了。

原作者的说明这里的level标签就是我们sqlmap注入时候–level命令,我感觉boundaries.xml中的level和测试指定的level关系并不大,当然这是特指测试语句这个例子,别的还得另说。

这里看一下原作者的分析:

level标签就是我们使用sqlmap注入时候–level的命令,这个标签分别定义了1-5个等级的发包数量,默认是1,表示做最少的检测,level越高,检测的数量越高。level等于2时会检测cookie字段是否有注入,等于3时会检测User-Agent、Host、Referer等HTTP的头部字段是否有注入。

最终的payload = where + boundary.prefix+test.payload+boundary.suffix,影响最终payload的生成主要由clause,where标签决定的。

当且仅当某个boundary元素的where节点的值包含test元素的子节点where的值,clause节点的值包含test元素的子节点的clause的值时候,该boundary才能和当前的test匹配生成最终的payload。经过测试发现除了这两个文件决定最终payload的生成,还包括sqlmap使用的注入方式以及payload使用的查询语句有关。如果把这两个文件比作两个集合,生成的payload比作两个集合的映射,则这两个集合之间的映射关系是多对多的关系。


在他的图片中是测试已经实际有了payload的两个标签中的东西,而作者写出来的话以及所展示的图片中:
boundary的where的值(1,2)包含test子节点where的值(1), test[where]含于boundary[where],这个是正确的。
clause节点的值(1)包含test子节点clause(1,2,3,4,5),test[clause]含于boundary[clause] 这个明显不对,1有,那2345呢? 图片和话并不对应,如果说是包含某个值的话就应该正确的,取交集。

相关问题可能还是得把sqlmap源码多读一读才能看得明白和透彻。

搭配实际案例的payload修改

测试代码

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
<?php
$mysql_server="10.10.10.136";
$mysql_username="root";
$mysql_userpass="xxxxx";
$mysql_select_db="test";
$config=mysql_connect($mysql_server,$mysql_username,$mysql_userpass)or die (mysql_error());
$db=mysql_select_db($mysql_select_db)or die (mysql_error());
if( isset( $_REQUEST[ 'evil' ]) ) {
$evil = $_REQUEST[ 'evil' ];
$query = "select * from test order by user_id $evil;";
//$query = "(select * from test order by user_id $evil);";
$result = mysql_query( $query,$config) or die( $query.'<pre>' . mysql_error() . '</pre>');
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
$user_id = mysql_result( $result, $i, "user_id" );
$user = mysql_result( $result, $i, "user" );
$password = mysql_result( $result, $i, "password" );
$html .= "<pre>user_id: {$user_id} user: {$user} password: {$password}</pre>";
$i++;
}
mysql_close();
echo $query;
echo $html;
}
?>

这是一种order by的排序注入

正常页面

可以手注的方法

显错注入

这里利用的是updatexml进行显错注入。

updatexml总共接收三个参数,第一个是xml文档的对象,第二个是xpath形式的字符串,最后一个是字符串格式,因为第二个参数是需要xpath格式的字符串,这里并不满足,从而导致报错。

盲注

这里盲注的方法是使用位运算符^(异或,当然别的也有别的妙处),通过构造返回的真假值来对user_id进行异或,从然根据返回的数据的顺序来判断是否匹配。

order by的结果默认是升序的。

查询语句是 select * from test order by user_id $evil;

因为evil变量我们可以自己构造,所以我们通过异或来进行构造,可以看到,0与user_id的二进制异或返回的值是正常升序,而1返回的是略有变异的升序。

0与任何数字异或是数字本身,所以排序不变。
1与user_id的二进制进行异或,然后按照异或的结果升序排列,但是显示的排列会发生变化。

所以我们通过构造条件,来判断我们构造的语句是真(返回1)还是假(返回0),从而根据页面返回的排列顺序来判断是否我们所判断的数据的正确性。

这里利用mysql的正则表达式regexp与select取到的值进行匹配。
当前用户是root@localhost

先随便输入一个用户admin,排序是错误结果输出的排序,admin不对

用正则表达式判断一下数据的开头是不是root,正确


当前用户就是root@localhost

联合查询

这里需要修改代码,将$sql = “select * from test order by user_id $evil;”;注释,将下边带括号的注释给去掉。

因为没有用括号进行包裹时,无法直接使用union进行查询。

自己网上找了一些文章,union子句中使用order by,就是先将select子句的结果先排序,然后把这些子句查询的结果进行整合,并且需要将整个子句加括号。

作者原文中贴出了MySQL官方文档中的话,文档中说并把ORDER BY或LIMIT放到最后一个的后边,其实放到前边也可以,但是不常见。

然后再进行注入构造

http://127.0.0.1/1.php?evil=desc)%20union%20(select%201,(version()),3)%23

sqlmap payload修改

使用sqlmap跑一跑先
注入语句:sqlmap -u “http://127.0.0.1/1.php?evil=desc" –technique=U –dbms=mysql –current-user -v 3

提示不可以注入,看一下跑着时候的payload

payload之前有一个括号是对的,后缀是)加注释,我们这用)#
作者是直接在payload文件中添加了这个前缀和后缀,sqlmap有参数的,–prefix指定前缀,–suffix指定后缀

select前边还有一个括号,我们在文件中寻找,前边我们已经知道lib\core\agent.py文件中有一个forgeUnionQuery函数是构造union查询的,把unionQuery里边的UNION ALL SELECT,在里边添加一个括号就可以了。

记得用–purge-output先删除以前的缓存文件,防止注入失败

注入成功。

参考文章:
sqlmap payload修改之路 上
sqlmap payload修改之路 下
玩的一手好注入之order by排序篇

文章来自于漏斗社区,前两天无意在公众号看到了这几篇文章,就自己搭起来琢磨与学习,在感谢社区的同时也感谢文章作者,让自己又有了进一步的提高。

也顺便把博客的标签整理了一下,精简一些。