談到SQL注入,那麼第一時間就會想到神器SQLMAP,SQLMap是一款用來檢測與利用的SQL注入開源工具。那麼SQLMap在掃描SQL的邏輯到底是怎樣實現的呢,接下來就探討下SQLMap的掃描邏輯,通過了解SQLMap的掃描邏輯打造一款屬于自己的SQL掃描工具。
SQLMAP在進行掃描之前會有一些其他的功能去確定當前的目標的一些有用的信息,如防火墻的檢測,當檢測到有防火墻之后,進行SQL檢測時的判斷依據也會有所調整,如bool類型的盲注的時候,而其中heuristicCheckSqlInjection這一函數既會影響到接下來所要使用什么樣的Payload進行測試,heuristicCheckSqlInjection翻譯過來的意思是啟發式SQL注入測試,那麼到底什么是啟發式,具體作用到底是什么呢,在我們經常使用SQLMAP的時候,有可能會出現以下的提示。
上面提示我們當前的目標數據庫版本好像是Oracle,而這上面的提示的依據就是根據這一個啟發式SQL注入測試也就是本文要介紹的heuristicCheckSqlInjection來決定的。SQLMAP中有許多的Payload,足以有幾百條,那麼如果全部Payload都測試一遍的話,無疑是一件很浪費時間的事情,除非Payload中的risk,clause,where字段中加以過濾,還會以這一個根據數據庫的指紋來選擇所要測試的Payload。
heuristicCheckSqlInjection的主要作用可以分為以下幾點:
#!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:
后生成了隨機的字符,然后就是發包檢測返回的數據了。
如下圖:
其實熟悉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]"/>
這一條正規
到此SQLMap就可以確定數據的版本了,從而選擇對應的測試Payload,減少SQLMAP的掃描時間。
相比指紋識別,獲取絕對路徑的功能模塊相對簡單,利用正則匹配尋找出絕對路徑。
#!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漏洞。
至此heuristicCheckSqlInjection的功能也介紹的差不多了,通過具體了解SQLMAP的一些掃描規則又或者是思路,可以讓我們根據具體的情況去配置SQLMAP又或者編寫自己的SQL Fuzz系統,其中可以通過編輯errors.xml這一指數據紋配置來增強SQLMAP的嗅探能力,從而打造更強大的神器了。