|
|
|
联系客服020-83701501

SQLMAP源码分析Part1-流程篇

联系在线客服,可以获得免费在线咨询服务。 QQ咨询 我要预约
SQLMAP源码阐发Part1:流程篇

0x00 概述

1.drops曩昔的文档 SQLMAP进阶把持介绍过SQLMAP的初级把持编制,网上也有几篇介绍过SQLMAP源码的文章曾是土木人,都写的非常好,倡议大师都看一下。
2.我操办分几篇文章详细的介绍下SQLMAP的源码,让想了解的友人们了解一下SQLMAP的道理和一些手工注入的语句,今日先初步第一篇:流程篇。
3.曩昔最佳了解SQLMAP各个选项的意义,大约参考sqlmap用户手册和SQLMAP目次doc/README.pdf
4.内容中如有纰谬大约没有写邃晓的地方,迎接指正革新。有全数内容是参考上面介绍的几篇文章的,在此一并说明,感谢他们。

0x01 流程图

enter image description here

0x02 调试编制

1.我用的IDE是PyCharm。
2.在菜单栏Run->Edit Configurations。点击左边的“+”,决定Python,Script中决定sqlmap.py的途径,Script parameters中填入注入时的下令,以下图。 enter image description here

3.翻开sqlmap.py,初步函数是main函数,在main函数处下断点。 enter image description here

4.右键Debug ‘sqlmap’,然后措施就积极跳到咱们下断点的main()函数处,后背大约持续减少断点停止调试。以下图,左边红色的代表跳转到下一个断点处,上面红色的闪现跳到下一句代码处

enter image description here

5.其它,如果要在代码中加中文表白,需要在初步处减少以下语句:#coding:utf-8。

0x03 流程

3.1 初始化

我这里用的版本是:1.0-dev-nongit-20150614
miin()函数初步73行:

Default
12 paths.SQLMAP_ROOT_PATH = modulePath()setPaths()

进入common.py中的setPaths()函数后,就大约看到这个函数是定义SQLMAP途径和文件的,相同于:

Default
12345 paths.SQLMAP_EXTRAS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "extra")paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "procs")paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "shell")paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper")paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf")

接上来的78行函数initOptions(cmdLineOptions),包括了三个函数,陶染如流程图所示,设置conf,KB,参数. conf会生存用户输出的一些参数,譬喻url,端口
kb会生存注入时的一些参数,其中有两个是比拟非凡的kb.chars.start和kb.chars.stop,这两个是随机字符串,后背会有介绍。

Default
123 _setConfAttributes()_setKnowledgeBaseAttributes()_mergeOptions(inputOptions, overrideOptions)

3.2 start

102行的start函数,算是检测初步的地方.start()函数位于controller.py中。

Default
12345 if conf.direct: initTargetEnv()setupTargetEnv()action()return True

起首这四句,意义是,如果你把持-d选项,那么sqlmap就会直接进入action()函数,连贯数据库,语句相同为:

Default
1234567 python sqlmap.py -d "mysql://admin:admin@192.168.21.17/* <![CDATA[ */!function(){try{var t="currentScript"in document?document.currentScript:function(){for(var t=document.getElementsByTagName("script"),e=t.length;e--;)if(t[e].getAttribute("cf-hash"))return t[e]}();if(t&&t.previousSibling){var e,r,n,i,c=t.previousSibling,a=c.getAttribute("data-cfemail");if(a){for(e="",r=parseInt(a.substr(0,2),16),n=2;a.length-n;n+=2)i=parseInt(a.substr(n,2),16)^r,e+=String.fromCharCode(i);e=document.createTextNode(e),c.parentNode.replaceChild(e,c)}}}catch(u){}}();/* ]]> */:3306/testdb" -f --banner --dbs --user  #!pythonif conf.url and not any((conf.forms, conf.crawlDepth)):    kb.targets.add((conf.url, conf.method, conf.data, conf.cookie, None))

上面代码会把url,methos,data,cookie退出到kb.targets,这些参数即是咱们输出的

enter image description here

接上来从274行的for轮回中,大约进入检测关头

Default
12 for targetUrl, targetMethod, targetData, targetCookie,targetHeaders in kb.targets:

此轮回先初始化一些一些变量,然结果断曩昔能否注入过,如果没有注入过,testSqlInj=True,不然testSqlInj=false。后背会停止果断能否检测过。

Default
1234567 def setupTargetEnv():_createTargetDirs()_setRequestParams()_setHashDB()_resumeHashDBValues()_setResultsFile()_setAuthCred()

372行setupTargetEnv()函数中包括了5个函数,这些函数陶染是

1.树立输出结果目次

2.阐发苦求参数

3.设置session新闻,即是session.sqlite。

4.恢复session的数据,持续扫描。

5.存储扫描结果。

6.减少认证新闻

其中比拟事多的即是session.sqlite,这个文件在sqlmap的输出目次中,测试的结果都会生具备这个文件里。

3.2.1 checkWaf

Default
123 checkWaf()if conf.identifyWaf:identifyWaf()

377行checkWaf()是检测能否有WAF,检测编制是NMAP的http-waf-detect.nse,譬喻页面为index.php?id=1,那当初减少一个随机变量index.php?id=1&aaa=2,设置paoyload相同为AND 1=1 UNION ALL SELECT 1,2,3,table_name FROM information_schema.tables WHERE 2>1– ../../../etc/passwd,如果没有WAF,页面不会改观,如果有WAF,因为payload中有很多迟缓字符,大多数时候页面都会发生改动。
接上来的conf.identifyWaf代表sqlmap的参数–identify-waf,如果指定了此参数,就会进入identifyWaf()函数,主要检测的waf都在sqlmap的waf目次下。

enter image description here

只管检测的编制都比拟繁冗,凡是查看返回的数据库包种能否包括了某些特征字符。如:

Default
1234567891011121314151617 __product__ = "360 Web Application Firewall (360)" def detect(get_page):    retval = False     for vector in WAF_ATTACK_VECTORS:        page, headers, code = get_page(get=vector)        retval = re.search(r"wangzhan\.360\.cn", headers.get("X-Powered-By-360wzb", ""), re.I) is not None        if retval:            break     return retval if (len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None)) \                and (kb.injection.place is None or kb.injection.parameter is None):

回到start函数,385行会果断能否注入过,如果尚未测试过参数能否大约注入,则进入if语句中。如果曩昔测试过,则不会进入此语句。

Default
12345678910 for place in parameters:????# Test User-Agent and Referer headers only if????# --level >= 3????skip = (place == PLACE.USER_AGENT and conf.level <????skip |= (place == PLACE.REFERER and conf.level < 3)????# Test Host header only if????# --level >= 5????skip |= (place == PLACE.HOST and conf.level < 5)????# Test Cookie header only if --level >= 2????skip |= (place == PLACE.COOKIE and conf.level < 2)

这中间sqlmap给了咱们一些表白,大约看到,level>=3时,会测试user-agent,referer,level>=5时,会测试HOST,level>=2时,会测试cookie。只管终极的测试果断还要在响应的xml中指定,后背会介绍。
480行的checkDynParam()函数会果断参数能否是动静的,譬喻index.php?id=1,经由互换id的值,如果参数是动静的,页面会差异。

3.2.2 heuristicCheckSqlInjection

 

Default
1 check = heuristicCheckSqlInjection(place, parameter)

502行有个heuristicCheckSqlInjection()函数,翻译过来是疏浚雷同性sql注入测试,实在即是后嗣行一个繁冗的测试,设置一个payload,然后阐发苦求结果。
heuristicCheckSqlInjection()在checks.py中,821行初步以下:

Default
12345678910111213141516171819202122 if conf.prefix or conf.suffix:????????if conf.prefix:????????????prefix = conf.prefix?????????if conf.suffix:????????????suffix = conf.suffix?????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)?????kb.heuristicMode = False?????parseFilePaths(page)????result = wasLastResponseDBMSError()

起首conf.prefix和conf.suffix代表用户指定的前缀和后缀;在while ‘\” not in randStr中,随机决定'”‘, ‘\”, ‘)’, ‘(‘, ‘,’, ‘.’中的字符,选10个,并且单引号要在。接上来天生一个payload,相同u’name=PAYLOAD_DELIMITER\__1).”.”.”\’.”__PAYLOAD_DELIMITER’。其中PAYLOAD_DELIMITER\__1和__PAYLOAD_DELIMITER是随机字符串。苦求网页后,调用parseFilePaths停止阐发,查看能否爆出绝对于途径,而wasLastResponseDBMSError是果断response中能否包括了数据库的报错新闻。

Default
12345678910111213 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)?kb.heuristicMode = False

上面的代码是从888行初步,DUMMY_XSS_CHECK_APPENDIX = “<‘\”>”,如果输出的字符串在页面中返回了,会提示大约具备XSS裂缝。

enter image description here

接上来,咱们回到start函数中,持续看下面的代码。

Default
123 if testSqlInj:????......????injection = checkSqlInjection(place, parameter, value)

在502行果断testSqlInj,如果为true,就代表曩昔没有检测过,然后就会到checkSqlInjection,checkSqlInjection()才是真正初步测试的函数,传入的参数是注入编制如GET,参数名,参数值。咱们跟进。

3.2.3 checkSqlInjection

checkSqlInjection()在checks.py中,91行初步

Default
12 paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else placetests = getSortedInjectionTests()

paramType是注入的榜样,如GET。tests是要测试的列表,以下图所示,包括了每个测试项的称谓,这些数据凡是和/sqlmap/xml/payloads/目次下每个xml绝对应的。

enter image description here

Default
12345678910111213141516171819 if conf.dbms is None:????if not injection.dbms and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data:????????if not Backend.getIdentifiedDbms() and kb.heuristicDbms is False:????????????kb.heuristicDbms = heuristicCheckDbms(injection)????if kb.reduceTests is None and not conf.testFilter and (intersect(Backend.getErrorParsedDBMSes(), \???????SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms):????????msg = "it looks like the back-end DBMS is '%s'. " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms)????????msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]"????????kb.reduceTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else []if kb.extendTests is None and not conf.testFilter and (conf.level < 5 or conf.risk < 3) \???and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or \???kb.heuristicDbms or injection.dbms):????msg = "for the remaining tests, do you want to include all tests "????msg += "for '%s' extending provided " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms)????msg += "level (%d)" % conf.level if conf.level < 5 else ""????msg += " and " if conf.level < 5 and conf.risk < 3 else ""????msg += "risk (%d)" % conf.risk if conf.risk < 3 else ""????msg += " values? [Y/n]" if conf.level < 5 and conf.risk < 3 else " value? [Y/n]"????kb.extendTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else []

101行初步,这段代码主假定果断DBMS榜样,起首,如果用户没有手工指定dbms,则会依照页面报错大约bool榜样的测试,找出DBMS榜样,找出后,会提示能否跳过测试此外的DBMS。然后,关于测试出来的DBMS,能否用所有的payload来测试。

enter image description here

140行if stype == PAYLOAD.TECHNIQUE.UNION:会果断是否是union注入,这个stype即是payload文件夹下面xml文件中的stype, 如果是union,就会进入,然后设置装备摆设列的数量等,今日先介绍流程,union注入之后会介绍。

Default
123456 if conf.tech and isinstance(conf.tech, list) and stype not in conf.tech:????????????????debugMsg = "skipping test '%s' because the user " % title????????????????debugMsg += "specified to test only for "????????????????debugMsg += "%s techniques" % " & ".join(map(lambda x: PAYLOAD.SQLINJECTION[x], conf.tech))????????????????logger.debug(debugMsg)????????????????continue

177行,即是用户供应的–technique,共有六个选项BEUSTQ,但是当初很多文档,包括SQLMAP的官方文档都只给了BEUST的疏讲解明,少个inline_query,相称于查问语句中再退出一个查问语句。

Default
123456 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(基于韶华韶华耽延注入)Q: inline_query(内联查问)

接上来,即是天生payload的过程。288行:

Default
1 fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None)

test.request.payload为’AND [RANDNUM]=[RANDNUM]'(响应payload.xml中的request值)。依照此代码,天生一个随机字符串,如fstPayload=u’AND 2876=2876’。
302行:

Default
1234 for boundary in boundaries:     injectable = False     if boundary.level > conf.level and not (kb.extendTests and intersect(payloadDbms, kb.extendTests, True)):                    continue

轮回遍历boundaries.xml中的boundary节点,如果boundary的level大于用户供应的level,则跳过,不检测。
307行:

Default
1234567891011121314 clauseMatch = Falsefor clauseTest in test.clause:?????????if clauseTest in boundary.clause:???????????clauseMatch = True?????????breakif test.clause != [0] and boundary.clause != [0] and not clauseMatch:?????continuewhereMatch = Falsefor where in test.where:?????if where in boundary.where:?????????whereMatch = True?????????breakif not whereMatch:?????continue

起首,轮回遍历test.clause(payload中的clause值),如果clauseTest在boundary的clause中,则设置 clauseMatch = True,代表此条boundary大约把持。 接上来轮回结婚where(payload中的where值),如果具备这样的where,设置whereMatch = True。如果clause和where中的一个没有结婚获胜,都会结束轮回,进入下一个payload的测试。

Default
123456 prefix = boundary.prefix if boundary.prefix else ""suffix = boundary.suffix if boundary.suffix else ""ptype = boundary.ptypeprefix = conf.prefix if conf.prefix is not None else prefixsuffix = conf.suffix if conf.suffix is not None else suffixcomment = None if conf.suffix is not None else comment

上面是设置payload的前缀和后缀,如果用户设置了,则把持用户设置的,如果没有,则把持boundary中的。
352行:

Default
1234567 for where in test.where:    if where == PAYLOAD.WHERE.ORIGINAL or conf.prefix:        ......    elif where == PAYLOAD.WHERE.NEGATIVE:        ......    elif where == PAYLOAD.WHERE.REPLACE:        ......

这里的where是payload中的where值,共有三个值,where字段我了解的意义是,以甚么样的编制将咱们的payload减少出来。

1:闪现将咱们的payload直接减少在值得后背[此处指的应该是检测的参数的值] 如咱们写的参数是id=1,设置值为1的话,会呈现1后背跟payload

2:闪现将检测的参数的值改观为一个整数,然后将payload减少在这个整数的后背。 如咱们写的参数是id=1,设置值为2的话,会呈现[数字]后背跟payload

3:闪现将检测的参数的值直接改观成咱们的payload。 如咱们写的参数是id=1,设置值为3的话,会呈现值1直接被幻化成了咱们的payload。
终极在389行:

Default
123 boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause)boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)

组合前缀、后缀、payload等,天生苦求的reqPayload。
这其中有个cleanupPayload()函数,实在即是将一些值停止随机化。以下图,比方kb.chars.start,kb.chars.stop,这两个变量是在基于纰谬的注入时,随机产生的字符串。

enter image description here

在398行:

Default
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 for method, check in test.response.items():    check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None)          if method == PAYLOAD.METHOD.COMPARISON:             def genCmpPayload():            sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None)            boundPayload = agent.prefixQuery(sndPayload, prefix, where, clause)            boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)            cmpPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)            return cmpPayload        kb.matchRatio = None        kb.negativeLogic = (where == PAYLOAD.WHERE.NEGATIVE)        Request.queryPage(genCmpPayload(), place, raise404=False)        falsePage = threadData.lastComparisonPage or ""             trueResult = Request.queryPage(reqPayload, place, raise404=False)        truePage = threadData.lastComparisonPage or ""              if trueResult:            falseResult = Request.queryPage(genCmpPayload(), place, raise404=False)            if not falseResult:                infoMsg = "%s parameter '%s' seems to be '%s' injectable " % (paramType, parameter, title)                logger.info(infoMsg)                injectable = True        if not injectable and not any((conf.string, conf.notString, conf.regexp)) and kb.pageStable:            trueSet = set(extractTextTagContent(truePage))            falseSet = set(extractTextTagContent(falsePage))            candidates = filter(None, (_.strip() if _.strip() in (kb.pageTemplate or "") and _.strip() not in falsePage and _.strip() not in threadData.lastComparisonHeaders else None for _ in (trueSet - falseSet)))            if candidates:                conf.string = candidates[0]                infoMsg = "%s parameter '%s' seems to be '%s' injectable (with --string=\"%s\")" % (paramType, parameter, title, repr(conf.string).lstrip('u').strip("'"))                logger.info(infoMsg)                injectable = True    elif method == PAYLOAD.METHOD.GREP:        try:            page, headers = Request.queryPage(reqPayload, place, content=True, raise404=False)            output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) \                    or extractRegexResult(check, listToStrValue( \                    [headers[key] for key in headers.keys() if key.lower() !=URI_HTTP_HEADER.lower()] \                    if headers else None), re.DOTALL | re.IGNORECASE) \                    or extractRegexResult(check, threadData.lastRedirectMsg[1] \                    if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \                    threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)            if output:                result = output == "1"                if result:                    infoMsg = "%s parameter '%s' is '%s' injectable " % (paramType, parameter, title)                    logger.info(infoMsg)                    injectable = True        except SqlmapConnectionException, msg:            debugMsg = "problem occurred most likely because the "            debugMsg += "server hasn't recovered as expected from the "            debugMsg += "error-based payload used ('%s')" % msg            logger.debug(debugMsg)    elif method == PAYLOAD.METHOD.TIME:        trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)        if trueResult:            # Confirm test's results            trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)            if trueResult:                infoMsg = "%s parameter '%s' seems to be '%s' injectable " % (paramType, parameter, title)                logger.info(infoMsg)                injectable = True    elif method == PAYLOAD.METHOD.UNION:        configUnion(test.request.char, test.request.columns)        if not Backend.getIdentifiedDbms():            if kb.heuristicDbms is None:                warnMsg = "using unescaped version of the test "                warnMsg += "because of zero knowledge of the "                warnMsg += "back-end DBMS. You can try to "                warnMsg += "explicitly set it using option '--dbms'"                singleTimeWarnMessage(warnMsg)            else:                Backend.forceDbms(kb.heuristicDbms)        if unionExtended:            infoMsg = "automatically extending ranges for UNION "            infoMsg += "query injection technique tests as "            infoMsg += "there is at least one other (potential) "            infoMsg += "technique found"            singleTimeLogMessage(infoMsg)        reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix)        if isinstance(reqPayload, basestring):            infoMsg = "%s parameter '%s' is '%s' injectable" % (paramType, parameter, title)            logger.info(infoMsg)            injectable = True            # Overwrite 'where' because it can be set            # by unionTest() directly            where = vector[6]    kb.previousMethod = method

上面这全数代码非常多,经由for轮回遍历payload中的标签,遍历的结果相同于

enter image description here

以是,上面的代码大约分为:

1.method为PAYLOAD.METHOD.COMPARISON:bool榜样盲注 2.method为PAYLOAD.METHOD.GREP:基于纰谬的sql注入 3.mehtod为PAYLOAD.METHOD.TIME:基于韶华韶华的盲注 4.method为PAYLOAD.METHOD.UNION:union连系查问

请留心,上面这四种编制,和曩昔说的六种注入编制不是一个概念,这里的是payload中的response代码,而注入用的是request代码。经由比拟request的结果和response的结果,必然能否大约注入。之后的文章会介绍怎样比拟的。。
checkSqlInjectiond的关键全数就到这里了,后背即是把注入的数据生存起来。顿时会介绍读取的时候。

3.2.4 Payload天生条件

背面具体介绍了Payload的天生编制,这里再总结一下条件:

1.sqlmap会实现读取payloads文件夹下xml文件中的每个test元素,然后轮回遍历。

2.此时还会遍历boundaries.xml文件。

3.当且仅当某个boundary元素的where节点的值包括test元素where节点的值,clause节点的值包括test元素的clause节点的值,该boundary技能格式和之后的test结婚,从而进一步天生payload。

4.where字段有三个值1:闪现将咱们的payload直接减少在值得后背[此处指的应该是检测的参数的值] 如咱们写的参数是id=1,设置值为1的话,会呈现1后背跟payload 2:闪现将检测的参数的值改观为一个整数,然后将payload减少在这个整数的后背。 如咱们写的参数是id=1,设置值为2的话,会呈现[数字]后背跟payload 3:闪现将检测的参数的值直接改观成咱们的payload。 如咱们写的参数是id=1,设置值为3的话,会呈现值1直接被幻化成了咱们的payload

5.终极的payload = url参数 + boundary.prefix+test.payload+boundary.suffix

3.2.5 Action

在start()的617行是action()函数,位于Action.py中,此函数是果断用户供应的参数,然后供应响应的函数。

Default
123456 if conf.getDbs:    conf.dumper.dbs(conf.dbmsHandler.getDbs())if conf.getTables:    conf.dumper.dbTables(conf.dbmsHandler.getTables())if conf.commonTables:    conf.dumper.dbTables(tableExists(paths.COMMON_TABLES))

3.2.6 HashDB

sqlmap注入的结果会生具备输出目次的session.sqlite文件汇总,此文件是sqlite数据库,大约把持SQLiteManager翻开。
回到controller.py中的start函数。第602行

Default
1234 _saveToResultsFile()_saveToHashDB()     _showInjections()   _selectInjection()

这四个函数的陶染即是生存结果生存结果、生存session、泄漏表现注入结果,包括榜样,payload等。
背面介绍过会果断testSqlInj的值,如果为True,代表没有测试过,会进入checkSqlInjection()函数,如果测试过,那么testSqlInj为false,就会跳过checkSqlInjection()。
譬喻咱们决定–current-db时,经由action()进入到conf.dumper.currentDb(conf.dbmsHandler.getCurrentDb())。进入到databases.py的getCurrentDb中。

Default
1 query = queries[Backend.getIdentifiedDbms()].current_db.query

这是失去响应的下令,譬喻mysql的下令是database().不停跟踪函数到use.py的346行

Default
123 if not value and not abortedFlag:    output = _oneShotUnionUse(expression, unpack)    value = parseUnionPage(output)

_onehotUninoUse即是读取session文件,失去曾经注入过的数据,如果session中没有,代表没有苦求过,则从新苦求失去数据。output此时是失去的网页的源码。

Default
1 retVal = hashDBRetrieve("%s%s" % (conf.hexConvert, expression), checkConf=True)

_onehotUninoUse的第一行,即是从session中失去数据,跟踪进hashdb.py的regrieve函数

Default
123456789101112131415161718192021222324 def hashKey(key):    key = key.encode(UNICODE_ENCODING) if isinstance(key, unicode) else repr(key)    retVal = int(hashlib.md5(key).hexdigest()[:12], 16)     #表白:hash的算法,对应数据库中的id。md5后,转换为10进制,即是session中的id    return retValdef retrieve(self, key, unserialize=False):    retVal = None    if key and (self._write_cache or os.path.isfile(self.filepath)):        hash_ = HashDB.hashKey(key)        retVal = self._write_cache.get(hash_)        if not retVal:                  while True:                try:                    for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)):                        retVal = row[0]                except sqlite3.OperationalError, ex:                    if not "locked" in ex.message:                        raise                except sqlite3.DatabaseError, ex:                    errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, ex)                    errMsg += "If the problem persists please rerun with `--flush-session`"                    raise SqlmapDataException, errMsg                else:                    break    return retVal if not unserialize else unserializeObject(retVal)

经由HashDB.hashKey()计算id,然后到session.sqlite中找记载,那么key是怎样天生的呢?
在common.py中有个hashDBRetrieve(),

Default
123456 def hashDBRetrieve(key, unserialize=False, checkConf=False):    _ = "%s%s%s" % (conf.url or "%s%s" % (conf.hostname, conf.port), key, HASHDB_MILESTONE_VALUE)    retVal = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any((conf.flushSession, conf.freshQueries))) else None    if not kb.inferenceMode and not kb.fileReadMode and any(_ in (retVal or "") for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)):        retVal = None    return retVal

此函数用于天生hash的key,天生编制为url+’None’+下令+HASHDB_MILESTONE_VALUE,譬喻 u’http://127.0.0.1:80/biweb/archives /detail.phpNoneDATABASE()JHjrBugdDA’。此key经由 int(hashlib.md5(key).hexdigest()[:12], 16),即是对应session中的id

enter image description here

终极在session.sqlite中依照id,就大约找到记载。

enter image description here

如上图,失去到的记载实在即是一个网页的源代码,其它大约看到current-db的前后有几个字符串,这个字符串即是kb.chars.start和kb.chars.stop
回到_oneShotUnionUse中,如果session中没有记载,则会从新停止苦求,失去数据

Default
123456 vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector        kb.unionDuplicates = vector[7]        kb.forcePartialUnion = vector[8]        query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, limited)            where = PAYLOAD.WHERE.NEGATIVE if conf.limitStart or conf.limitStop else vector[6]        payload = agent.payload(newValue=query, where=where)

终极的值经由阐发session中的记载value = parseUnionPage(output),找到kb.chars.start和kb.chars.stop中间的值,即是结果。

0x04 结束

另有很多东西没有写出来,盼望后背的几篇文章大约写好。花了很久的韶华韶华,调试、码字,不知道又没有人能看到末了。。

[via@drops:3xpl0it]

数安新闻+更多

证书相关+更多