/ Web安全

Sqlmap源码阅读-2

0x01 Init() 第一部分

上一节看到把参数初始化完了,接下去要进行涉及操作的一些初始化动作了,我们继续往下看。
跟进init函数:from lib.core.option import init

def init():
    _useWizardInterface()
    setVerbosity()
    _saveConfig()
    _setRequestFromFile()
    _cleanupOptions()
    _cleanupEnvironment()
    _dirtyPatches()
    _purgeOutput()
    _checkDependencies()
    _createTemporaryDirectory()
    _basicOptionValidation()
    _setProxyList()
    _setTorProxySettings()
    _setDNSServer()
    _adjustLoggingFormatter()
    _setMultipleTargets()
    _setTamperingFunctions()
    _setWafFunctions()
    _setTrafficOutputFP()
    _setupHTTPCollector()
    _resolveCrossReferences()
    _checkWebSocket()

    parseTargetUrl()
    parseTargetDirect()

总感觉贴一大段代码不好,但是这一段还是比较重要的,先把前一段贴出来,方便看。第一个函数,_useWizardInterface()是使用向导接口,也就是向导模式,要启用这个模式需要使用参数wizard,这个函数开头会对conf.wizard进行判断,如果没设置会直接返回,我们大概了解意思是干啥就行了,这个感觉没必要细看。
继续,setVerbosity(),根据-v参数的值设置sqlmap运行过程中打印的日志粒度。

def setVerbosity():
    if conf.verbose is None:
        conf.verbose = 1

    conf.verbose = int(conf.verbose)

    if conf.verbose == 0:
        logger.setLevel(logging.ERROR)
    elif conf.verbose == 1:
        logger.setLevel(logging.INFO)
    elif conf.verbose > 2 and conf.eta:
        conf.verbose = 2
        logger.setLevel(logging.DEBUG)
    elif conf.verbose == 2:
        logger.setLevel(logging.DEBUG)
    elif conf.verbose == 3:
        logger.setLevel(CUSTOM_LOGGING.PAYLOAD)
    elif conf.verbose == 4:
        logger.setLevel(CUSTOM_LOGGING.TRAFFIC_OUT)
    elif conf.verbose >= 5:
        logger.setLevel(CUSTOM_LOGGING.TRAFFIC_IN)

这里要提一下这个logger,logger是放在lib.core.data中,可以看出这个模块是存放sqlmap一些重要全局变量的地方,之前的conf/kb都是从这里来的。logger的具体实现是在lib.core.log模块中,在这个模块中对logger的格式等进行了设置。

接下来,_saveConfig(),这个也简单,把命令行参数存到一个ini配置文件中去。需要用到的参数是--save。

_setRequestFromFile(),这个是从一个txt文件中读取一个http请求来进行注入测试,用过sqlamp的都知道-r参数。这个函数就是在我们设置了-r filename后解析请求并把一些参数存到kb中去。里面调用了两个函数,第一个是safeExpandUser,这个是为了修复python一个bug用的,另外一个才是解析请求的主力,_feedTargetsDict。在这个函数里的主要处理是先检查文件是否存在并且可读,然后分别调用_parseBurpLog和_parseWebScarabLog函数进行解析。其中Burp保存下来的数据包是XML格式的,sqlmap使用了这个正则BURP_XML_HISTORY_REGEX=r'(\d+).+?<![CDATA[([^]]+)'进行提取。另外一种我们复制的一个这个样子的请求

POST /su?wd=wonderkun.cc%2Findex.html%2Findex.php%2F2017%2F01%2F20%2F513%2F&action=opensearch&ie=UTF-8 HTTP/1.1
Host: suggestion.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: BAIDUID=E176E4EC5484890BDA08B7439095DDBB:FG=1; BIDUPSID=E176E4EC5484890BDA08B7439095DDBB; PSTM=1500088979; BDUSS=Xl2ZH5LRVo5LTZrdVhUWGRGU2xxRzVPSGt6UXJ0LUdJc0dmRzZxNUNUWHRTWkZaSVFBQUFBJCQAAAAAAAAAAAEAAAA2uXI4x6210QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO28aVntvGlZa; __cfduid=dd5f2294d0c5941a35ce7f1f4d46c93461501825182; MCITY=-131%3A; PSINO=1; H_PS_PSSID=1437_21106_25177_22159
Connection: close


a=aaaa&b=bbbb

是通过BURP_REQUEST_REGEX = r"={10,}\s+[^=]+={10,}\s(.+?)\s={10,}"正则进行解析完成的。所以总的来说我们常用的是通过_parseBurpLog函数进行解析完成的。解析完成后参数会被存放到kb字典中去。。所以还是不太懂conf和kb的区别是什么

_cleanupOptions()。主要是在对conf字典中的参数进行处理,去掉换行空格什么的

_cleanupEnvironment():检查socket环境

_dirtyPatches():仿佛是修复python的某个配置

_purgeOutput():指定--purge-output参数时,处理sqlmap的输出目录下的内容

_checkDependencies():指定--dependencies参数时,进行依赖检查

_createTemporaryDirectory():指定--tmp-dir参数时,建立临时目录,没指定的话会使用系统临时目录。

_basicOptionValidation():检查命令行参数的有效性

_setProxyList():设置代理池

_setTorProxySettings():设置tor代理

_setDNSServer():启动一个本地DNS server用于dns带外数据传输

_adjustLoggingFormatter():调整日志的格式

_setMultipleTargets():使用-l参数指定一个存放了多个目标的burp/webscrap文件或者文件夹时,使用这个函数解析,内部实现还是对每个文件使用_feedTargetsDict()函数进行解析

_setTamperingFunctions():加载tamper插件,这个函数里面会对加载的插件进行检查,并根据优先级进行排序

_setWafFunctions():指定--identify-waf参数时,加载WAF/IDS/IPS探测脚本

_trafficFile():指定-t参数时,记录所有的探测http请求到一个日志文件,文件路径和名字需要指定

_setupHTTPCollector():指定-har参数的时候,记录所有http探测流量到一个har文件中去,没细看

_resolveCrossReferences():重写一些函数。。。

_checkWebSocket():如果使用websocket需要引入一些库

parseTargetUrl() parseTargetDirect()最后两个函数,一个是解析探测目标url一个是解析连接目标url

0x01 Init() 第二部分

if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)):
        _setHTTPTimeout()
        _setHTTPExtraHeaders()
        _setHTTPCookies()
        _setHTTPReferer()
        _setHTTPHost()
        _setHTTPUserAgent()
        _setHTTPAuthentication()
        _setHTTPHandlers()
        _setDNSCache()
        _setSocketPreConnect()
        _setSafeVisit()
        _doSearch()
        _setBulkMultipleTargets()
        _setSitemapTargets()
        _checkTor()
        _setCrawler()
        _findPageForms()
        _setDBMS()
        _setTechnique()

这里有一个IF语句,条件是url/logFile/bulkFile等等,可以发现这些参数都是与探测目标相关,也就是一旦我们设置了与探测目标相关的选项,就会执行以下的语句。
_setHTTPTimeout():设置socket的超时时间,最低设置为3s,参数--timeout,默认30s

_setHTTPExtraHeaders():设置一些额外的http header,参数--headers

_setHTTPCookies():设置cookies,参数为--cookie

_setHTTPReferer():设置referer,参数为--referer

_setHTTPHost():设置Host,就是http header中的header,参数--host

_setHTTPUserAgent():设置UA,参数--user-agent

_setHTTPAuthentication():设置认证信息,需要指定认证类型和秘钥,认证类型指定参数--auth-type=(Basic, Digest, NTLM or PKI),没指定但是指定了秘钥文件会默认为PKI

_setHTTPHandlers():Check and set the HTTP/SOCKS proxy for all HTTP requests.

_setDNSCache():重写了socket里面的getaddrinfo,在新实现的_getaddrinfo中增加了缓存,避免多次DNS查询

_setSocketPreConnect():Makes a pre-connect version of socket.connect 又重写了Socket.connect。可怕,还可以这么玩

_setSafeVisit():这个是为了避免多次访问错误页面被屏蔽而设置的一个正常页面的URL,在sqlmap探测的过程中会定时请求这个页面,避免被ban

_doSearch():google search用的

_setBulkMultipleTargets():从一个放着一堆url的文件中读取目标,注意和-r指定文件不一样的,目标url会被存在kb字典中

_setSitemapTargets():从一个sitemap.xml文件里面读取目标

_checkTor: 检查Tor代理的设置

_setCrawler():设置爬取url目标的相关参数,比如爬取的深度

_findPageForms():在设置了--forms参数后,查找页面的form表单进行测试

_setDBMS():设置数据库类型

_setTechnique():设置注入类型

参数:--technique
这个参数可以指定sqlmap使用的探测技术,默认情况下会测试所有的方式。
支持的探测方式如下:
B: Boolean-based blind SQL injection(布尔型注入)
E: Error-based SQL injection(报错型注入)
U: UNION query SQL injection(可联合查询注入)
S: Stacked queries SQL injection(可多语句查询注入)
T: Time-based blind SQL injection(基于时间延迟注入)

0x03 Init() 第三部分

Init函数的最后一部分

    _setThreads()
    _setOS()
    _setWriteFile()
    _setMetasploit()
    _setDBMSAuthentication()
    loadBoundaries()
    loadPayloads()
    _setPrefixSuffix()
    update()
    _loadQueries()

_setThreads():指定线程数,通过--threads来指定线程,默认为1

_setOS():设置目标系统类型

_setWriteFile():指定要写到目标系统的文件

_setMetasploit():用以建立反弹shell的设置

_setDBMSAuthentication():在知道高权限用户的密码的时候,可以使用此参数--dbms-cred,有的数据库有专门的运行机制,可以切换用户,这个函数执行相关的设置

loadBoundaries():加载测试语句并解析,这里的paths.BOUNDARIES_XML为E:\sqlmap\xml\boundaries.xml

loadPayloads():加载payloads,payload的路径为E:\sqlmap\xml\payloads\,payload是根据不同注入类型进行分类的

上面两个方法涉及到Sqlmap的payload组成方式,建议看这篇文章,一个比较核心的问题,我目前也不能很好组织语言来描述。
SQLMap的前世今生(Part1)
_setPrefixSuffix():在有些环境中,需要在注入的payload的前面或者后面加一些字符,来保证payload的正常执行。这个函数是进行前缀的设置,前缀通过--suffix参数指定

update():指定--update参数时,进行更新

_loadQueries():Loads queries from 'xml/queries.xml' file.

Sqlmap源码中payload文件

可以看到sqlmap对不同数据库的查询语句进行了分类和细化,仔细看看sql server部分就可以知道如果存在注入在sql server上可以进行怎么进行注入了,学习的好资源

至此,对于sqlmap的加载资源部分已经全部看完,这一部分的代码主要是在lib/core/option.py模块中,接下来回到sqlmap.py,就要执行start()函数进行注入探测了。