sqlmap源码通读

释放双眼,带上耳机,听听看~!
一、前情提要最近实验室大佬在写一个渗透测试框架,目的是在出去做渗透测试项目的时候能够快速发现漏洞,以及实施快速打击。但无论大小型的扫描器、扫描框架等,结果的真实性、内容丰富性和效率、效用都会形成一个矛盾,因此,能够性价比最高的发现问题都是扫描框架首要的任务。对于检测注入这个位置,我们希望能够快速发现

一、前情提要

最近实验室大佬在写一个渗透测试框架,目的是在出去做渗透测试项目的时候能够快速发现漏洞,以及实施快速打击。

但无论大小型的扫描器、扫描框架等,结果的真实性、内容丰富性和效率、效用都会形成一个矛盾,因此,能够性价比最高的发现问题都是扫描框架首要的任务。

对于检测注入这个位置,我们希望能够快速发现,不需要实际注入得出数据,然后想着正好学习一下sqlmap的源码,能够使用的部分直接拿过来,再根据实际作用改一改。于是就有了这篇文章。对于我的水平应该是99%都看不懂的,但是好在网上相关文章不少。

SQLMAP不用过多介绍,是一款检测和利用SQL注入的工具,基于python编写。

SQL注入 5 种类型:布尔注入,时间注入,报错注入,联合查询注入,堆查询注入。还有一种SQLMAP支持的,内联注入。

二、SQLMAP使用方法

先来看一下SQLMAP的常见使用方法和参数。

帮助信息:-h/–hh/–version

-h    能够显示帮助信息,–hh 能够显示更多的信息

–version    能够显示程序版本

日志显示等级:-v

  • 0:只显示Python的回溯、错误和关键消息;

  • 1:同时显示基本信息和警告信息;

  • 2:同时显示调试信息;

  • 3:同时显示注入的payload;

  • 4:同时显示HTTP请求;

  • 5:同时显示HTTP响应头;

  • 6:同时显示HTTP响应页面页面。

指定目标(GET方法):-d/-u/–url/-g

-d      表示sqlmap将自己作为客户端去连接数据库

-u/–url     指定目标系统的 URL

-g     对Google的搜索结果进行SQL注入探测,例如:sqlmap.py -g “inurl:”.php?id=1.”

指定参数(GET方法):-p/–batch/–force-ssl

-p     对指定的参数进行SQL注入检测

–batch     不与使用者进行信息交互,直接执行

–force-ssl     强制使用 SSL/HTTPS 协议

指定目标(POST方法):-r/-l/-c

-r     将HTTP请求文件保存到文本文档中,使用-r参数读取文本文件的参数进行SQL注入

-l     将burpsuite log文件保存到文本文档中,使用参数-l读取文本文档的参数进行SQL注入

-c     对配置ini文件进行SQL注入探测

指纹信息:-f/–fingerprint/-b/–banner

-b/–banner     获取数据库版本信息和数据库类型

-f/–fingerprint     查询目标系统的数据库管理系统的指纹信息

枚举:-a/–all/–dbs/–current-user/–privileges-U/–roles/–current-db/–users/–passwords/–tables/–columns/–schema/–dump/–dump-all/-D/-T/-C/–exclude-sysdbs/–count/–schema/–batch/–sql-query/–sql-shell

-a/–all     获取所有信息

–dbs     枚举DBMS中所有的数据库

–current-user     获取当前用户

–privileges-U     username(当前账号/CU) 查看当前账号的权限

–roles     列出数据库管理员角色,仅适用于当前数据库是Oracle

–current-db     获取当前数据库

–users     枚举DBMS用户

–passwords     枚举DBMS用户密码hash值

–tables     枚举DBMS数据库管理系统中的表

–columns     枚举DBMS数据库管理系统中的列

–schema     枚举DBMS数据库管理系统的模式

–dump     转储DBMS数据库表项,后面加-C表示转储某列,-T转储某表,-D转储某数据库,–start,–stop,–first,–last指定开始结束,开头结尾。

–dump-all     转储所有的DBMS数据库表项

-D     指定枚举的DBMS中的数据库

-T     指定要枚举的表

-C     指定要枚举的列

-D 数据库名 –tables     查找指定数据库中的表

-D 数据库名 -T 表名 –columns     查找指定数据库的某个表中的列

–exclude-sysdbs     忽略掉系统数据库

–count     查找表中的记录数

–schema     查找数据库的架构,包含所有的数据库,表和字段,以及各自的类型,一般与–exclude-sysdbs共用

–batch     默认每次自动执行

–sql-query/–sql-shell     运行自定义的SQL语句,例:–sql-query=”select * from users;”所得到的内容被保存到dump目录中

指定数据:–data/–cookie/–param-del/–user-agent/–random-agent/–host/–referer/–method

–data=DATA     指定post数据包中被传输的值,例:sqlmap.py -u “http//www.xxx.com” –data=”name=123&pass=456″ -f

–cookie=COOKIE     指定cookie值登录web程序,并且会尝试自动注入cookie值,需要在level 2或者大于level 2等级才会进行cookie注入。如果cookie被服务器端更新,那么sqlmap也会自动更新cookie值。例:sqlmap -u “http://www.xxx.com/index/?id=1&Submit=Submit#” -p id –cookie=”security=low; PHPSESSID=d806c1f76f24a9687640ce497afc8f20″ –batch

–param-del      告知sqlmap变量分隔符。web程序一般默认是&符号作为分隔符,如果并非&,则需要指定变量分隔符,例:sqlmap.py -u “http://www.xxx.com” –data=”user=123;pass=456″ –param-del=”;” -f

–user-agent     指定UA头部信息。sqlmap默认使用UA为:sqlmap/1.0-dev-版本号 http://sqlmap.org

–random-agent     使用sqlmap/txt/user-agents.txt字典中的UA头部进行随机替换

–host=host header     指定host头部信息,当level为5的时候才会检测host值

–referer=REFERER     指定Referer头部信息,当level大于等于三 ,才回去检测referer头部是否存在注入

–method=GET/POST     指定使用get或者POST方式发送数据,默认以get方式发送

注入模块配置:-p/–skip/-u/–dbms/–os/–invalid-bignum/–invalid-logical/–no-cast/–tamper

-p     指定扫描的参数,也可以指定HTTP头部字段,例:sqlmap.py -u “http://www.xxx.com/?id=1” -p “User-Agent,Referer,id”

–skip     跳过对某些参数进行测试。当使用–level的值很大但是有个别参数不想去测试的时候使用–skip去跳过 例:sqlmap.py -u “http://www.xxx.com/?id=1” –skip “User-Agent,Referer,id”

-u     设置URL注入点。当有些网站将参数和值一起加入到URL链接中,sqlmap是默认不对其进行扫描的,所以我们需要去指定对某个参数值进行注入,例:sqlmap.py -u “http://www.xxx.com/param1/value1*/param2/value2*” –dbms 设置目标服务器所使用的DBMS,例:–dbms=”mysql”

–os     指定目标的操作系统,例:–os=”linux”

–invalid-bignum     给参数值给与最大值让其失效

–invalid-logical     使用布尔判断使取值失效

–no-cast     榨取数据时,sqlmap将所有的结果转换成字符串,并用空格替换null值(老版本mysql数据库需要开启此开关)

–tamper=TAMPER     使用给定的脚本去混淆绕过应用层的过滤,比如waf/ids等。该文件存放在/sqlmap/tamper文件下,例:sqlmap.py -u “www.xxx.com/?id=1” -p “id” –tamper=”between.py,overlongutf8more.py,lowercase.py “

身份认证:–auth-type/–auth-cred/–auth-file

–auth-type=AUTH      指定HTTP认证类型(Basic, Digest, NTLM or PKI)

–auth-cred=AUTH     指定HTTP认证证书(格式为:name:password),例:sqlmap.py -u “http://www.xxx.com/?id=1” –auth-type=Basic –auth-cred “user:pass”

–auth-file=AUTH     指定HTTP认证PEM格式的证书/私钥文件

代理类:–proxy/–proxy-cred/–ignore-proxy/–tor/–tor-type

–proxy=”http://127.0.0.1:8081″     设置代理服务器

–proxy-cred=”name:pass”     例:sqlmap -u “http://www.xxx.com/?id=1″ –proxy=”http://127.0.0.1:8081″ –proxy-cred=”user:pass” -f

–ignore-proxy     忽略系统级代理设置,通常用于扫描本地网络目标

–tor     使用匿名网络进行注入

–tor-type     指定tor网络连接类型

延时类:–delay/–timeout/–retries/–randomize/–scope

–delay=DELAY     每次HTTP(S)请求之间延迟时间,值为浮点数,单位为秒,默认无延迟

–timeout=TIMEOUT     设置超时时间,默认30秒

–retries=RETRIES     设置重连次数,默认3次

–randomize     设置随机改变的参数值

–scope      利用正则表达式过滤日志内容

技术类型:–technique/–time-sec/–union-cols/–union-char/–union-from/–dns-domain/–second-order

SQLMAP对于以下参数具有默认值:

–technique=TECH     指定sqlmap使用的检测技术,默认情况下会测试所有的方式。

–time-sec=TIMESEC     设置延迟时间,基于时间的注入检测默认延迟时间是5秒

–union-cols=UCOLS      联合查询时默认是1-10列,当level=5时会增加到测试50个字段数,可以使用此参数设置查询的字段数。

–union-char=UCHAR     默认情况下sqlmap针对UNION查询的注入会使用NULL字符;

–union-from=UFROM     在UNION查询SQL注入的FROM部分中使用的表

–dns-domain=DNS     攻击者控制了某DNS服务器,使用此功能可以提高数据查询的速度

–second-order=URL     使用此参数指定到哪个页面获取响应判断真假,–second-order后面跟一个判断页面的URL地址。

CSRF TOKEN 绕过:–csrf-url/–csrf-token

–csrf-url=URL     获取token的链接

–csrf-token=param     你提交注入包中,token参数

检测类:–level/–risk/–string/–not-string/–Regexp/–code

–level     共有5级,默认等级1

–risk     共有4级,默认等级1,risk升高将使用update等方法测试,将造成数据被篡改等风险

–string     指定页面返回某个字符串则为真

–not-string     指定页面不返回某个字符串则为真

–Regexp     当查询的值为真时,使用正则表达式去匹配

–code     当查询的值为真时,执行HTTP code

系统文件操作:–file-read/–file-write

–file-read=RFILE     从后端DBMS文件系统中读取文件(读取系统文件),例:–file-read=”/etc/passwd”

–file-write=SHELL.PHP –file-dest=DFILE     把当前系统的文件写入到目标服务器的某个目录下去

OS系统访问:–os-cmd/–os-shell/–os-pwn/–os-smbrelay/–os-bof

–os-cmd     运行任意操作系统命令(适用于数据库为mysql,postgresql,或Sql Server,并且当前用户有权限使用特定的函数),例:–os-cmd id :执行id命令,后期是与sqlmap进行交互,生成UDF函数在操作系统下执行命令

–os-shell     获取一个shell(目标系统为管理员权限,并且得知绝对路径)

–os-pwn      获取一个OOB shell,meterpreter或VNC

–os-smbrelay     一键获取一个OOB shell,meterpreter或VNC

–os-bof     存储过程缓冲区溢出利用

UDF注入:–udf-inject/–shared-lib

UDF:自定义函数,利用UDF函数达到执行操作系统命令

–udf-inject     注入用户自定义函数

–shared-lib=SHLIB     指定共享库的本地路径

两条命令需一起使用

爆破表/字段名:–common-tables/–common-columns

使用场景:

  • mysql版本<5.0的时候,没有information_schema库

  • mysql版本>=5.0,但无权读取information_schema库

  • 微软的access数据库,默认无权读取MSysObjects库。

–common-tables     爆破表名,例:sqlmap.py -u “http://www.baidu.com/?id=1” –common-tables

–common-columns     暴力破解列名

Windows注册表:–reg-read/–reg-add/–reg-del/–reg-key/–reg/value/–reg-data/–reg-type

–reg-read      读取注册表的值

–reg-add      写入注册表值

–reg-del     删除注册表值

–reg-key,–reg-value,–reg-data,–reg-type     注册表辅助选项

一般性参数:-s/-t/–charset/–crawl/–csv-del/–dbms-creb/–flush-session/–fresh-queries/–hex/–save

-s     指定sqlite会话文件保存位置

-t     记录流量文件保存位置

–charset     强制字符编码,例:–charset=GBK

–crawl     从开始位置爬站深度,例:–crawl=3

–csv-del dump     下来的数据以CVS格式保存

–dbms-creb     指定数据库账号

–slush-session     清空session

–fresh-queries     忽略session查询结果

–hex     当dump下非ASCii字符内容时,将其编码成16进账形式,收到后解析还原

–save     将命令保存成配置文件

WAF对抗类:–check-waf/–identify-waf/–hpp

–check-waf     检测WAF/IPS/IDS

–hpp     绕过WAF/IPS/ISD,尤其是对ASP/IIS和ASP.NET/IIS有效

–identify-waf     彻底的WAF/IPS/IDS检测,支持三十多种产品

优化性能:-o/–predict-output/–keep-alive/–null-connection/–threads

-o     开启所有优化开关

–predict-output     预测常见的查询输出

–keep-alive     使用持久的HTTP(S)连接

–null-connection     从没有实际的HTTP响应体中检索页面长度

–threads=THREADS     设置最大的HTTP(S)请求并发量(默认为1)

其他参数:–safe-url/–safe-freq/–skip-urlencode/–eval/–mobile/–purge-output/–smart/–wizard

–safe-url=SAFEURL     指定需要去重复扫描的地址

–safe-freq     指定每发送多少次的注入请求之后接着发正常请求,有些web应用程序会在攻击者多次访问错误的请求时屏蔽掉以后的所有请求,所以设置这两个参数防止以后无法进行注入 –skip-urlencode 跳过URL编码的载荷,默认在get请求中是需要对传输数据进行编码,但是有些web服务器不遵守RPC标准编码,使用原始字符提交数据,所以使用这个参数使sqlmap不使用URL编码的参数进行测试

–eval=EBALCODE     在请求之前执行提供的python代码,例:sqlmap.py -u “http://www.xxx.com/?id=1&hash=c4ca4238a0b923820dcc509a6f75849b” –evel=”import hashlib;hash=hashlib.md5(id).hexdigest()”

–mobile     模拟智能手机设备,修改User-Agent为手机端的UA

–purge-output     清空output文件夹

–smart     当有大量检测目标时,只修改基于错误的检测结果

–wizard     设置用户向导参数,教你一步步针对目标注入

三、源码阅读

基本上比较常用的参数都在上面列了一下,当个速查表吧,然后就开始学习sqlmap源码之路吧。

本次学习的源码版本为 1.3.6.50#dev

目录结构

首先看一下目录结构,我们将文档类的文件排除掉,只看系统类的:

主目录

文件/文件夹 说明
data/ 数据库注入检测载荷、用户自定义攻击载荷、字典、shell命令、数据库触发顺序等
extra/ 一些额外功能,例如发出声响(beep)、运行cmd、安全执行、shellcode等
lib/ 包含了sqlmap的多种连接库,如五种注入类型请求的参数、提权操作等。
plugins/ 数据库信息和数据库通用事项
tamper/ 绕过脚本
thirdparty/ sqlmap使用的第三方的插件
sqlmap.conf sqlmap的配置文件,如各种默认参数(默认是没有设置参数、可设置默认参数进行批量或者自动化检测)
sqlmap.py sqlmap主程序文件
sqlmapapi.py sqlmap的api文件,可以将sqlmap集成到其他平台上
swagger.yaml api文档

程序初始化

main()函数首先是执行5个函数,我们先依次看下每个函数的作用。

补丁包:dirtyPatches()

对于程序的一些问题及修复,写成了补丁函数,优先执行。

首先设定了 httplib 的最大行长度,接下来导入第三方的 windows 下的 ip地址转换函数模块,然后对编码进行了一些替换,把 cp65001 替换为 utf8 避免出现一些交互上的错误,这些操作对于 sqlmap 的实际功能影响并不是特别大,属于保证起用户体验和系统设置的正常选项,不需要进行过多关心

解决交叉引用:resolveCrossReferences()

为了消除交叉引用的问题,一些子程序中的函数会被重写,在这个位置进行赋值

检查环境:checkEnvironment()

如名,函数的主要作用是检查环境

首先调用modulePath(),获取程序路径,判断程序是否由 py2exe 打包成 exe ,因为打包后无法使用 __file__ 获得路径,为了防止乱码,使用getUnicode返回unicode编码的路径

然后使用LooseVersion判断python版本号,版本号小于1.0则报错并退出

接下来是对pip安装环境的补丁,可以看到是一些系统环境变量的设置

这里面定义个贯穿sqlmap全局的三个全局变量,cmdLineOptions,conf ,kb

绝对路径设置:setPaths()

配置程序文件的路径,并且判断.txt, .xml, .zip为扩展名的文件是否存在并且是否可读,这部分将预设的程序全部使用的信息都加载进来了,在后续的调用中就去各个变量、字典里取就可以了

paths值为一个sqlmap自己封装的字典类型 AttribDict

为什么作者要自己封装一个字典类型呢?首先能看到的就是,可以直接使用访问属性的方式访问键值

其次是作者定义了__deepcopy__,为了解决字典赋值传递后浅copy会修改原数据的问题

BANNER展示:banner()

函数判断执行参数中是否包含 –version 或 –api参数,或者在配置中是否将disableBanner中设置为True,如果都没有,那就将BANNER字符串赋值给 “_” ,

跨终端显示颜色则使用了第三方插件: colorama 库。跟一下变量BANNER

使用random.sample随机选择,然后使用正则替换,再跟一下BANNER,最后我们看到的就是SQLMAP的字符画了

实际上这部分看出来SQLMAP中间红色部分是随机的。。用了这么久竟然不知道。。

五个函数执行之后进入之后的流程,在 banner 打印之后,就是对命令行参数的操作了

cmdLineOptions 同样是一个 AttribDict,跟进 cmdLineParser

可以看到,对命令行参数的处理使用了 optparse,首先将获取到的命令行参数选项进行判断和拆分以后转变成dict键值对的形式存入到cmdLineOptions,然后传入initOption进行下一步操作

_setConfAttributes() 函数初始化了一些重要的属性值为空

_setKnowledgeBaseAttributes() 函数同样的初始化了一些重要的属性值到“知识库”中

_mergeOptions(inputOptions, overrideOptions) 函数主要的作用是将配置项中的参数和命令行获得的参数选项以及缺省选项进行合并,函数执行完毕将会将字典 mergedOptions 的值进行更新

检查是否通过中间进行标准输入:

检查是否调用api,如果是将会引入新的包,并重写sys.stdout和sys.stderr

判断过后打印法律声明和时间

然后就是执行 init() 函数进行程序的初始化,对用户定义参数进行配置和初始化。这部分后面再说。

init() 函数执行过后就是执行测试以及start函数的执行了,我们看到了三个测试函数 smokeTest、vulnTest 和 liveTest 用于测试。

在不进行测试的情况下,导入包文件,在这一步才导入包,会使程序的启动更快。

然后判断 conf.profile ,这个程序调用的图形处理,暂时没弄明白是干嘛的。默认为空,程序直接走到else,启用start() 程序,也在后面说。

接下来是一大长串的 except 和最终的 finally

finally中包括了一些提示信息、清除临时目录文件、清除线程、清除配置等等一些文件

程序的最终结尾又是一个 if 判断,如果命令行参数里有sqlmapShell的话重新执行main函数,这实际上用一个死循环来完成的类似长连接的效果

命令行参数处理

文件位置:/lib/core/option.py

init() 函数将命令行参数和配置文件的选项值同时设置为程序配置

具体做了什么如下:

def init():
    _useWizardInterface() # --wizard 用户向导程序,为了给初学者一个友好的界面
    setVerbosity() # -v 设置debug等级
    _saveConfig() # --save 将命令行选项保存到sqlmap配置INI文件中
    _setRequestFromFile() # -r 从文件中设置http请求
    _cleanupOptions() # 清理配置选项
    _cleanupEnvironment() # 清理环境
    _purge() # 安全删除(清除)sqlmap数据目录
    _checkDependencies() # 检测丢失的依赖
    _createHomeDirectories() # 在sqlmap的主目录中创建目录
    _createTemporaryDirectory() # 创建运行的临时目录
    _basicOptionValidation() # 检测选项是否有效
    _setProxyList() # --proxy 设置代理列表
    _setTorProxySettings() # --tor/--tor-type 设置Tor代理
    _setDNSServer() # --dns-domain 设置DNS服务器
    _adjustLoggingFormatter() # 调整日志格式
    _setMultipleTargets() # 如果在多个目标中运行,只定义一种参数
    _listTamperingFunctions()
    _setTamperingFunctions() # --tamper 设置tamper脚本
    _setPreprocessFunctions() # --preprocess 从给定的脚本加载预处理函数
    _setTrafficOutputFP() # 设置记录http日志文件
    _setupHTTPCollector() # 清理http收集
    _setHttpChunked()
    _checkWebSocket() # 检测websocket-client模块调用
 
    parseTargetDirect() # 解析目标DBMS
 
    if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)):
        # 如果设置了上述选项,配置相关http
        _setHostname() # 设置hostname
        _setHTTPTimeout() # 设置超时时间
        _setHTTPExtraHeaders() # 设置Headers
        _setHTTPCookies() # 设置Cookies
        _setHTTPReferer() # 设置Referer
        _setHTTPHost() # 设置Host
        _setHTTPUserAgent() # 设置UserAgent
        _setHTTPAuthentication() # 设置HTTPAuthentication
        _setHTTPHandlers() # 检查并设置所有HTTP请求的 HTTP/SOCKS代理。
        _setDNSCache() # 设置DNS缓存
        _setSocketPreConnect()
        _setSafeVisit() # 检查并设置安全访问选项。
        _doSearch() # 使用搜索平台搜索结果并存储
        _setBulkMultipleTargets()
        _setSitemapTargets() # 解析SitemapTargets中的目标
        _checkTor() # 检测Tor
        _setCrawler() # 设置爬虫
        _findPageForms() # 寻找网页的表单
        _setDBMS() # 强制DBMS选项 
        _setTechnique()
 
    _setThreads() # 设置线程
    _setOS() # 强制OS
    _setWriteFile() # 写入文件
    _setMetasploit() # Metasploit相关设置
    _setDBMSAuthentication() # 设置数据库身份认证,来使用另一个身份执行
    loadBoundaries() # 载入Boundaries
    loadPayloads() # 载入Payloads
    _setPrefixSuffix() # 设置前后缀
    update() # 更新sqlmap
    _loadQueries() # 加载查询的xml /查询

初始和解析HTTP相关内容

文件位置:/lib/controller/controller.py

start() 函数将调用函数,检查URL的连接稳定性,以及对所有的 GET/POST/Cookie/User-Agent 参数进行检查,检查他们是否为动态的,以及是否收到SQL注入的影响

接下来是检测目标点是否存在SQL注入,conf.direct 参数为True,则用户配置直连数据库,将绕过下方的流程,直接检测:

  • initTargetEnv() 用于初始化目标环境

  • setupTargetEnv() 用于设置目标环境

  • action() 函数用于在受影响的URL参数上执行SQL注入攻击,并尝试提取 DBMS 或 操作系统相关信息

正常流程是将配置 conf 字典中的目标URL、方法、data、cookie中添加到 kb.targets 中,然后进一步判断,如果没有就报错,然后打印出目标个数

然后是判断网络连接数

设置HTTP连接的一些参数,都是python数据类型转换,没什么好说的

initTargetEnv() 函数主要就是完成全局变量 conf 和 kb 的初始化工作

parseTargetUrl() 函数主要通过正则完成针对目标网址的解析工作,如获取协议名、路径、端口、请求参数等信息

测试过的 url 参数信息会保存到 kb.testedParams 中,所以在进行test之前,会先判断当前的url是否已经test过,如果没test过的话,则 testSqlInj = True,否则 testSqlInj = False,如果值为False ,到后续就不会再执行注入攻击了,因此接下来这段代码就是在判断目前要测试的点是否测试过,测试过是否为注入点等,并修改 testSqlInj 的值,最后进行判断是否要跳过这个点

接下来是判断是否存在多个目标,依次与用户交互确认是否进行测试

然后执行 setupTargetEnv() 函数,该函数主要包含3个子功能:

  • 创建保存目标执行结果的目录和文件

  • 将 get 或 post 发送的数据解析成字典形式,并保存到 conf.paramDict 中

  • 读取session文件(如果存在的话),并读取文件中的数据,保存到 kb 变量中

检查连接、是否有用户自定义的 String(字符)或 Regexp(正则):

然后是 checkWaf() 函数,检测是否有WAF,之后再说

检查空连接(nullConnection)、检查页面稳定性,以及对参数、测试列表进行排序

什么是 nullConnection?根据官方手册,是一种不用获取页面内容就可以知道页面大小的方法,这种方法在布尔盲注中有非常好的效果,可以很好的节省带宽。

如果启用 –null-connection,计算页面相似率就只是很简单的通过页面的长度来计算,这部分后面会提到

然后根据参数中的 level 级别判断是否进行 cookie 、referer、ua的注入

使用函数 checkDynParam() 判断参数是否是动态,如果不是动态则需要选取其他参数。其核心是给参数另外一个随机值,然后通过选择参数及对于页面的各种规则的判断,计算出两个页面的相似比率,来判定是否为动态。

根据level等级不同的情况判断是否应该跳过参数

如果参数为动态,将接下来进入检测SQL注入测试环节,程序将调用 heuristcCheckSqlInjection() 函数进行启发式检测,再得到 POSITIVE 的结果后,再调用 checkSqlInjection() 进行注入点检查,再次确认有漏洞且不是 FALSE_POSTTIVE后,将injectable 设为True,中间产生的数据放入相应的数据集中,执行下一步,这两个函数的内容也会稍后详细说。

如果没检查出漏洞点,将根据不同情况提供各种各样的建议参数

否则将结果进行保存

二次确认有注入数据后,将与用户交互确认,并执行 action() 函数,才是开始跑数据了。这部分后续会详细介绍。

中间又是一大堆的 except 异常处理之后,finally 中显示了http错误代码,并提示最大连接限制的情况

最后提示结果文件名

识别waf

文件位置:/lib/controller/checks.py

在看源码之前发现 checkWaf() 函数上使用了装饰器,@stackedmethod,使用pushvalue/popvalue函数的方法(用于堆栈重新对齐的回退函数),这句话就没看明白,无所谓,接着看

如果用户使用了一些自定义的参数,或指定不进行waf检测,将会直接return,不进行waf检测

第一步判断原始状态码,不存在就return。如果之前数据库有数据就取出返回,

payload是一个随机数字和一个专门用来检测WAF的payload——IPS_WAF_CHECK_PAYLOAD

IPS_WAF_CHECK_PAYLOAD 位于 /lib/core/settings.py 中

IPS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,NULL,'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#"

可以看到真的是肆意的想触发WAF规则,然后将payload拼入参数值中,并根据是否重定向进行配置。

然后使用 queryPage() 函数判断前后两次访问的相似度,如果(ratio)比率小于阈值(0.5),那说明访问未被拦截,就认为不存在Waf

结果存入数据库,并根据结果进行用户交互

测试 payload 之后,将会开始检测 WAF 指纹,sqlmap 识别 waf 指纹的插件位于 /thirdparty/identywaf/identYwaf.py

全局搜索一下引入这个插件的文件,发现只有在 /lib/request/basic.py 中的 processResponse() 函数进行了引用,来看一下这个函数

parseResponse() 函数对可能的数据库后端进行检测

是一个 if 判断,processResponseCounter 和 IDENTYWAF_PARSE_LIMIT进行比较,只有前者小于后者的时候才能继续执行

IDENTYWAF_PARSE_LIMIT 在配置文件中默认为 10,而 processResponseCounter 在每次调用 processResponse 时加一 ,可想而知,在计数到时的时候,下面的流程将失效

然后调用了 identYwaf.non_blind_check(),for 循环遍历 non_blind 中的数据,判断是否在 kb.identifiedWafs 中,并打印出识别 waf 结果

我们先来看一下non_blind_check() 函数实现什么功能

中将会拿到原始的相应包,并进行正则匹配,(图里的print是我自己加上去的,汗)

如果正则匹配到了,将会加入到 non_blind 里

而 waf 的指纹在同目录的 data.json 中,通过 load_data() 函数加入到 WAF_RECOGNITION_REGEX 变量中

在断点调试的过程中发现了识别的过程,拿到原始的数据包,使用正则进行匹配,如果匹配到了就将数据写入变量,后续去变量里找数据并打印

data.json 长成这样,这部分又是我们可以自行拓展的部分

但是究竟在哪个位置开始进行waf识别的呢?

再次跟一下 processResponse() 的调用链

发现在 /lib/request/connect.py 中的 getPage() 函数中调用,sqlmap每次获取返回结果都要走这个函数,而在这个函数中是一定能调到 processResponse() 的

也就是说, sqlmap 通过一句 PAYLOAD 检测目标URL是否有waf,通过前十次连接返回的结果进行正则匹配,识别 waf 指纹

启发性检查

文件位置:/lib/controller/checks.py

首先走到的是 heuristcCheckSqlInjection(),翻译过来应该是启发性注入检查,说的好听,实际上就是利用简单的payload根据返回结果尝试性的猜想是否可能出现问题

跟一下这个函数,首先进行判断,我也不知道 heavy dynamic 是啥意思,暂时就叫他强动态吧

从 conf 里拿数据,声明用户自定义的前缀、后缀

按照规则随机生成一些字符

拼接 payload ,并再次调用 queryPage() 计算请求的相似性

同样使用 FORMAT_EXCEPTION_STRINGS 全局配置,通过查找字符串的方式判断几次请求之间的差异

parseFilePaths() 检测页面之中可能出现的绝对路径

wasLastResponseDBMSError() 检测是否出现数据库错误信息

检测在输入非常规的参数值时,服务端是否返回格式化字符串出错的信息

此处出现条件判断,当 casting 为 False ,参数为动态,且参数原始值为数字类型时,代表格式化字符串没有出错信息,可以看到函数中的 payload 是利用数值型运算来判定页面相似度,例如原来的页面是 id=5 ,程序随机出一个数字,比如是 3 ,就再次访问 id=8-3 ,返回结果与 id=5 使用 queryPage() 函数进行相似度判断

如果结果 result 为 False,证明二者页面相似度较低,再次生成非数字 payload,例如 id=5.aaa ,再次访问页面计算页面相似度,并赋值给 casting,如果两者页面相似度较高,则 casting为 True

再次出现条件判断,当 casting 为 True 时,根据 URL split 拆分得到可能的后端语言,并根据不同语言给出可能的处理参数方式,此处有两种可能走到这个位置,一个是格式化参数出现报错信息,一个是 id=5 和 id=5.aaa 页面返回的结果是一样的,这种情况可以基本判断服务端处理参数的方式。

如果 result 为True,也就是数值运算的相似性结果很高,使用 getErrorParseDBMSes() 获取可能的后端数据库类型,否则返回消息

注入检测完毕后按照全局配置重新随机生成随机值,进行检测XSS和文件包含漏洞

简单的检测了下XSS漏洞,原理就是如果我们输入的 value 原封不动的出现在了响应里,可能是服务器直接将我们的字符串放在了html页面的某个位置,这种情况下就可能存在XSS漏洞,不得不说,这种方式检测XSS,误报率真的有点。。。

之后又简单的检测文件包含漏洞,实际上就是如果测试的参数点对应文件,而我们注入的payload找不到文件的话可能会报错

也是使用全局配置中的正则表达式,可以看到这个正则真的是比较随意,而且对中文环境也十分不友好,不过对于sqlmap来讲就是顺便的事,重点也不是检测这个漏洞

验证SQL注入点

文件位置:/lib/controller/checks.py

这个点是检测是否存在SQL注入的核心函数 checkSqlInjection() ,前期检测和验证是否是注入点,这个函数800多行代码,好就好在作者注释写的还不少,我们先来看一下主逻辑

前期是初始化一些参数,首先要把要处理的 payload 和 boundaries 加载进来,boundaries是所谓的边界值,也就是真正payload的前缀和后缀信息,这部分是根据已知的参数来筛选 boundaries

paramType 是注入的类型,如GET/POST

给TA买糖
共{{data.count}}人
人已赞赏
HackerNews

新思科技:开源风险控制仍需加强

2019-7-9 11:19:41

HackerNews

2019北京网络安全大会通过指尖安全购票,立享折上折!6折购票!立省1000元!

2019-7-11 5:59:15

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索