<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址:http://drops.wooyun.org/papers/10652

    0x00 前言


    談到SQL注入,那麼第一時間就會想到神器SQLMAP,SQLMap是一款用來檢測與利用的SQL注入開源工具。那麼SQLMap在掃描SQL的邏輯到底是怎樣實現的呢,接下來就探討下SQLMap的掃描邏輯,通過了解SQLMap的掃描邏輯打造一款屬于自己的SQL掃描工具。

    0x01 SQLMAP的準備工作


    SQLMAP在進行掃描之前會有一些其他的功能去確定當前的目標的一些有用的信息,如防火墻的檢測,當檢測到有防火墻之后,進行SQL檢測時的判斷依據也會有所調整,如bool類型的盲注的時候,而其中heuristicCheckSqlInjection這一函數既會影響到接下來所要使用什么樣的Payload進行測試,heuristicCheckSqlInjection翻譯過來的意思是啟發式SQL注入測試,那麼到底什么是啟發式,具體作用到底是什么呢,在我們經常使用SQLMAP的時候,有可能會出現以下的提示。

    p1

    上面提示我們當前的目標數據庫版本好像是Oracle,而這上面的提示的依據就是根據這一個啟發式SQL注入測試也就是本文要介紹的heuristicCheckSqlInjection來決定的。SQLMAP中有許多的Payload,足以有幾百條,那麼如果全部Payload都測試一遍的話,無疑是一件很浪費時間的事情,除非Payload中的risk,clause,where字段中加以過濾,還會以這一個根據數據庫的指紋來選擇所要測試的Payload。

    heuristicCheckSqlInjection的主要作用可以分為以下幾點:

    1. 數據庫版本的識別。
    2. 絕對路徑獲取。
    3. XSS的測試

    0x02 數據庫版本的識別


    #!python
    # Alphabet used for heuristic checks
    HEURISTIC_CHECK_ALPHABET = ('"', '\'', ')', '(', ',', '.')
    ...
    randStr = ""
    while '\'' not in randStr:
        randStr = randomStr(length=10, alphabet=HEURISTIC_CHECK_ALPHABET)
    kb.heuristicMode = True
    payload = "%s%s%s" % (prefix, randStr, suffix)
    payload = agent.payload(place, parameter, newValue=payload)
    page, _ = Request.queryPage(payload, place, content=True, raise404=False)
    ...
    

    首先會從HEURISTIC_CHECK_ALPHABET中隨機抽取10個字符出現構造Payload,當然里面的都不是些普通的字符,而且些特殊字符,當我們進行SQL注入測試的時候會很習慣的在參數后面加個分號啊什么的,又或者是其他一些特殊的字符,出現運氣好的話有可能會暴出數據的相關錯誤信息,而那個時候我們就可以根據所暴出的相關錯誤信息去猜測當前目標的數據庫是什么。

    實際找個網站測試,打下碼,保護下。

    http://***.***.***/datalist/default.aspx/article?category_id=1051
    

    那麼經過while '\'' not in randStr:后生成了隨機的字符,然后就是發包檢測返回的數據了。

    如下圖:

    p2

    其實熟悉SQL注入的人也知道這是一個Oracle的一個錯誤信息,那麼接下來看看SQLMAP到底是怎樣去判斷的。

    位于./lib/request/connect.py中的getPage函數,大約在598行。

    #!python
    def getPage(**kwargs):
        ...
        processResponse(page, responseHeaders)
        ...
    

    其中processResponse會調用到./lib/parse/html.py中的htmlParser函數,這一個函數就是根據不同的數據庫指紋去識別當前的數據庫究竟是什么。

    #!python
    def htmlParser(page):
        """
        This function calls a class that parses the input HTML page to
        fingerprint the back-end database management system
        """
        xmlfile = paths.ERRORS_XML
        handler = HTMLHandler(page)
        parseXmlFile(xmlfile, handler)
        if handler.dbms and handler.dbms not in kb.htmlFp:
            kb.lastParserStatus = handler.dbms
            kb.htmlFp.append(handler.dbms)
        else:
            kb.lastParserStatus = None
        # generic SQL warning/error messages
        if re.search(r"SQL (warning|error|syntax)", page, re.I):
            handler._markAsErrorPage()
        return handler.dbms
    

    最終實現的的其實是HTMLHandler這個類,而paths.ERRORS_XML這一變量的就是SQLMAP用來識別的指紋配置文件路徑,位置在于./xml/errors.xml中。

    #!html
    <!-- Oracle -->
    <dbms value="Oracle">
        <error regexp="\bORA-[0-9][0-9][0-9][0-9]"/>
        <error regexp="Oracle error"/>
        <error regexp="Oracle.*Driver"/>
        <error regexp="Warning.*\Woci_.*"/>
        <error regexp="Warning.*\Wora_.*"/>
    </dbms>
    

    這一配置文件的比較簡單,其實也就是一些對應數據庫的正則。SQLMAP在解析errors.xml的時候,然后根據regexp中的正則去匹配當前的頁面信息然后去確定當前的數據庫。

    #!python
    class HTMLHandler(ContentHandler):
        """
        This class defines methods to parse the input HTML page to
        fingerprint the back-end database management system
        """
        def __init__(self, page):
            ContentHandler.__init__(self)
            self._dbms = None
            self._page = page
            self.dbms = None
        def _markAsErrorPage(self):
            threadData = getCurrentThreadData()
            threadData.lastErrorPage = (threadData.lastRequestUID, self._page)
        def startElement(self, name, attrs):
            if name == "dbms":
                self._dbms = attrs.get("value")
            elif name == "error":
                if re.search(attrs.get("regexp"), self._page, re.I):
                    self.dbms = self._dbms
                    self._markAsErrorPage()
    

    可以發現當前返回的頁面信息命中了<error regexp="\bORA-[0-9][0-9][0-9][0-9]"/>這一條正規

    p3

    到此SQLMap就可以確定數據的版本了,從而選擇對應的測試Payload,減少SQLMAP的掃描時間。

    0x03 絕對路徑獲取與XSS檢測


    相比指紋識別,獲取絕對路徑的功能模塊相對簡單,利用正則匹配尋找出絕對路徑。

    #!python
    def parseFilePaths(page):
        """
        Detects (possible) absolute system paths inside the provided page content
        """
        if page:
            for regex in (r" in <b>(?P<result>.*?)</b> on line", r"(?:>|\s)(?P<result>[A-Za-z]:[\\/][\w.\\/]*)", r"(?:>|\s)(?P<result>/\w[/\w.]+)"):
                for match in re.finditer(regex, page):
                    absFilePath = match.group("result").strip()
                    page = page.replace(absFilePath, "")
                    if isWindowsDriveLetterPath(absFilePath):
                        absFilePath = posixToNtSlashes(absFilePath)
                    if absFilePath not in kb.absFilePaths:
                        kb.absFilePaths.add(absFilePath)
    

    而XSS的檢測代碼位于889行中:

    #!python
    # String used for dummy XSS check of a tested parameter value
    DUMMY_XSS_CHECK_APPENDIX = "<'\">"
        ...
        value = "%s%s%s" % (randomStr(), DUMMY_XSS_CHECK_APPENDIX, randomStr())
        payload = "%s%s%s" % (prefix, "'%s" % value, suffix)
        payload = agent.payload(place, parameter, newValue=payload)
        page, _ = Request.queryPage(payload, place, content=True, raise404=False)
        paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
        if value in (page or ""):
            infoMsg = "heuristic (XSS) test shows that %s parameter " % paramType
            infoMsg += "'%s' might be vulnerable to XSS attacks" % parameter
            logger.info(infoMsg)
        ...
    

    最后根據輸入的字符是否留著頁面上,如果存在就提示有可能擁有XSS漏洞。

    0x04 總結


    至此heuristicCheckSqlInjection的功能也介紹的差不多了,通過具體了解SQLMAP的一些掃描規則又或者是思路,可以讓我們根據具體的情況去配置SQLMAP又或者編寫自己的SQL Fuzz系統,其中可以通過編輯errors.xml這一指數據紋配置來增強SQLMAP的嗅探能力,從而打造更強大的神器了。

    97av