源代码
def start():
"""
This function calls a function that performs checks on both URL
stability and all GET, POST, Cookie and User-Agent parameters to
check if they are dynamic and SQL injection affected
"""
if not conf.start:
return False if conf.direct:
initTargetEnv()
setupTargetEnv()
action()
return True if conf.url and not conf.forms:
kb.targetUrls.add(( conf.url, conf.method, conf.data, conf.cookie )) if conf.configFile and not kb.targetUrls:
errMsg = "you did not edit the configuration file properly, set "
errMsg += "the target url, list of targets or google dork"
logger.error(errMsg)
return False if kb.targetUrls and len(kb.targetUrls) > 1:
infoMsg = "sqlmap got a total of %d targets" % len(kb.targetUrls)
logger.info(infoMsg) hostCount = 0
cookieStr = ""
setCookieAsInjectable = True for targetUrl, targetMethod, targetData, targetCookie in kb.targetUrls:
try:
conf.url = targetUrl
conf.method = targetMethod
conf.data = targetData
conf.cookie = targetCookie
initTargetEnv()
parseTargetUrl() testSqlInj = False
if PLACE.GET in conf.parameters:
for parameter in re.findall(r"([^=]+)=[^&]+&?", conf.parameters[PLACE.GET]):
paramKey = (conf.hostname, conf.path, PLACE.GET, parameter)
if paramKey not in kb.testedParams:
testSqlInj = True
break
else:
paramKey = (conf.hostname, conf.path, None, None)
if paramKey not in kb.testedParams:
testSqlInj = True testSqlInj &= (conf.hostname, conf.path, None, None) not in kb.testedParams if not testSqlInj:
infoMsg = "skipping '%s'" % targetUrl
logger.info(infoMsg)
continue if conf.multipleTargets:
hostCount += 1
if conf.forms:
message = "[#%d] form:\n%s %s" % (hostCount, conf.method or HTTPMETHOD.GET, targetUrl)
else:
message = "url %d:\n%s %s%s" % (hostCount, conf.method or HTTPMETHOD.GET, targetUrl, " (PageRank: %s)" % get_pagerank(targetUrl) if conf.googleDork and conf.pageRank else "") if conf.cookie:
message += "\nCookie: %s" % conf.cookie if conf.data:
message += "\nPOST data: %s" % urlencode(conf.data) if conf.data else "" if conf.forms:
if conf.method == HTTPMETHOD.GET and targetUrl.find("?") == -1:
continue message += "\ndo you want to test this form? [Y/n/q] "
test = readInput(message, default="Y") if not test or test[0] in ("y", "Y"):
if conf.method == HTTPMETHOD.POST:
message = "Edit POST data [default: %s]%s: " % (urlencode(conf.data) if conf.data else "None", " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "")
conf.data = readInput(message, default=conf.data)
if extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data):
message = "do you want to fill blank fields with random values? [Y/n] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
while extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data):
item = extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data)
if item[-1] == '&':
conf.data = conf.data.replace(item, "%s%s&" % (item[:-1], randomStr()))
else:
conf.data = conf.data.replace(item, "%s%s" % (item, randomStr()))
conf.data = urldecode(conf.data) elif conf.method == HTTPMETHOD.GET:
if conf.url.find("?") > -1:
firstPart = conf.url[:conf.url.find("?")]
secondPart = conf.url[conf.url.find("?")+1:]
message = "Edit GET data [default: %s]: " % secondPart
test = readInput(message, default=secondPart)
conf.url = "%s?%s" % (firstPart, test) elif test[0] in ("n", "N"):
continue
elif test[0] in ("q", "Q"):
break elif conf.realTest:
logger.info(message)
else:
message += "\ndo you want to test this url? [Y/n/q]"
test = readInput(message, default="Y") if not test or test[0] in ("y", "Y"):
pass
elif test[0] in ("n", "N"):
continue
elif test[0] in ("q", "Q"):
break logMsg = "testing url %s" % targetUrl
logger.info(logMsg) setupTargetEnv()
if not checkConnection(suppressOutput = conf.forms) or not checkString() or not checkRegexp():
continue if conf.nullConnection:
checkNullConnection() if not conf.dropSetCookie and conf.cj:
for _, cookie in enumerate(conf.cj):
cookie = getUnicode(cookie)
index = cookie.index(" for ") cookieStr += "%s;" % cookie[8:index] if cookieStr:
cookieStr = cookieStr[:-1] if PLACE.COOKIE in conf.parameters:
message = "you provided an HTTP Cookie header value. "
message += "The target url provided its own Cookie within "
message += "the HTTP Set-Cookie header. Do you want to "
message += "continue using the HTTP Cookie values that "
message += "you provided? [Y/n] "
test = readInput(message, default="Y") if not test or test[0] in ("y", "Y"):
setCookieAsInjectable = False if setCookieAsInjectable:
conf.httpHeaders.append(("Cookie", cookieStr))
conf.parameters[PLACE.COOKIE] = cookieStr
__paramDict = paramToDict(PLACE.COOKIE, cookieStr) if __paramDict:
conf.paramDict[PLACE.COOKIE] = __paramDict
# TODO: consider the following line in __setRequestParams()
# __testableParameters = True 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):
if not conf.string and not conf.regexp:
# NOTE: this is not needed anymore, leaving only to display
# a warning message to the user in case the page is not stable
checkStability() # Do a little prioritization reorder of a testable parameter list
parameters = conf.parameters.keys() # Order of testing list (last to first)
orderList = (PLACE.URI, PLACE.GET, PLACE.POST) for place in orderList:
if place in parameters:
parameters.remove(place)
parameters.insert(0, place) proceed = True for place in parameters:
# Test User-Agent and Referer headers only if
# --level >= 3
skip = (place == PLACE.UA and conf.level < 3)
skip |= (place == PLACE.REFERER and conf.level < 3) # Test Cookie header only if --level >= 2
skip |= (place == PLACE.COOKIE and conf.level < 2) skip &= not (place == PLACE.UA and intersect(USER_AGENT_ALIASES, conf.testParameter))
skip &= not (place == PLACE.REFERER and intersect(REFERER_ALIASES, conf.testParameter)) if skip:
continue if not conf.paramDict.has_key(place):
continue paramDict = conf.paramDict[place]
for parameter, value in paramDict.items():
if not proceed:
break testSqlInj = True paramKey = (conf.hostname, conf.path, place, parameter) if paramKey in kb.testedParams:
testSqlInj = False infoMsg = "skipping previously processed %s parameter '%s'" % (place, parameter)
logger.info(infoMsg) # Avoid dinamicity test if the user provided the
# parameter manually
elif parameter in conf.testParameter or conf.realTest:
pass elif not checkDynParam(place, parameter, value):
warnMsg = "%s parameter '%s' is not dynamic" % (place, parameter)
logger.warn(warnMsg) else:
logMsg = "%s parameter '%s' is dynamic" % (place, parameter)
logger.info(logMsg) kb.testedParams.add(paramKey) if testSqlInj:
check = heuristicCheckSqlInjection(place, parameter)
if not check and conf.realTest and\
not simpletonCheckSqlInjection(place, parameter, value):
continue logMsg = "testing sql injection on %s " % place
logMsg += "parameter '%s'" % parameter
logger.info(logMsg) injection = checkSqlInjection(place, parameter, value)
proceed = not kb.endDetection if injection is not None and injection.place is not None:
kb.injections.append(injection) # In case when user wants to end detection phase (Ctrl+C)
if not proceed:
break msg = "%s parameter '%s' " % (injection.place, injection.parameter)
msg += "is vulnerable. Do you want to keep testing the others? [y/N] "
test = readInput(msg, default="N") if test[0] in ("n", "N"):
proceed = False
paramKey = (conf.hostname, conf.path, None, None)
kb.testedParams.add(paramKey)
else:
warnMsg = "%s parameter '%s' is not " % (place, parameter)
warnMsg += "injectable"
logger.warn(warnMsg) if len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None):
if not conf.realTest:
errMsg = "all parameters are not injectable, try to "
errMsg += "increase --level/--risk values to perform "
errMsg += "more tests." if isinstance(conf.tech, list) and len(conf.tech) > 0:
errMsg += " Rerun without providing the --technique switch." if not conf.textOnly and kb.originalPage:
percent = (100.0 * len(getFilteredPageContent(kb.originalPage)) / len(kb.originalPage))
errMsg += " Give it a go with the --text-only switch "
errMsg += "if the target page has a low percentage of "
errMsg += "textual content (~%.2f%% of " % percent
errMsg += "page content is text)" raise sqlmapNotVulnerableException, errMsg
else:
errMsg = "it seems that all parameters are not injectable"
raise sqlmapNotVulnerableException, errMsg
else:
# Flush the flag
kb.testMode = False __saveToSessionFile()
__showInjections()
__selectInjection() if kb.injection.place is not None and kb.injection.parameter is not None:
if kb.testQueryCount == 0 and conf.realTest:
condition = False
elif conf.multipleTargets:
message = "do you want to exploit this SQL injection? [Y/n] "
exploit = readInput(message, default="Y") condition = not exploit or exploit[0] in ("y", "Y")
else:
condition = True
if condition:
action() except KeyboardInterrupt:
if conf.multipleTargets:
warnMsg = "user aborted in multiple target mode"
logger.warn(warnMsg) message = "do you want to skip to the next target in list? [Y/n/q]"
test = readInput(message, default="Y") if not test or test[0] in ("y", "Y"):
pass
elif test[0] in ("n", "N"):
return False
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
else:
raise except sqlmapUserQuitException:
raise except sqlmapSilentQuitException:
raise except exceptionsTuple, e:
e = getUnicode(e) if conf.multipleTargets:
e += ", skipping to the next %s" % ("form" if conf.forms else "url")
logger.error(e)
else:
logger.critical(e)
return False finally:
showHttpErrorCodes() if conf.loggedToOut and not conf.multipleTargets:
logger.info("Fetched data logged to text files under '%s'" % conf.outputPath) return True
代码解释
10-14行
if conf.direct:
initTargetEnv()
setupTargetEnv()
action()
return True
conf.direct是通过命令行参数:"-d"指定的。
通过参数"-d"指定要连接的数据库
eg:-d "mysql:123123//root:@127.0.0.1:3306/security"
39-40行
initTargetEnv()
parseTargetUrl()
initTargetEnv()函数主要就是完成全局变量conf和kb的初始化工作
parseTargetUrl()函数主要完成针对目标网址的解析工作,如获取协议名、路径、端口、请求参数等信息
43-52行
if PLACE.GET in conf.parameters:
for parameter in re.findall(r"([^=]+)=[^&]+&?", conf.parameters[PLACE.GET]):
paramKey = (conf.hostname, conf.path, PLACE.GET, parameter)
if paramKey not in kb.testedParams:
testSqlInj = True
break
else:
paramKey = (conf.hostname, conf.path, None, None)
if paramKey not in kb.testedParams:
testSqlInj = True
测试过的url参数信息会保存到kb.testedParams中(第230行和第259行),所以在进行test之前,会先判断当前的url是否已经test过
如果没test过的话,则testSqlInj = True,否则testSqlInj = False。
当testSqlInj = False的时候,就不会执行 injection = checkSqlInjection(place, parameter, value)这句代码了。
126行
setupTargetEnv()
该函数主要包含3个子功能:
1.创建保存目标执行结果的目录和文件
2.将get或post发送的数据解析成字典形式,并保存到conf.paramDict中
3.读取session文件(如果存在的话),并提起文件中的数据,保存到kb变量中
133-162行
如果在命令中有提供cookie的话,就会将攻城师指定的cookie加入到http请求头中,以便使用cookie访问。
164-263行
提取url中的参数信息,并将其传递给checkSqlInjection函数,checkSqlInjection函数的功能主要是检测给定的url参数,看其是否可注入,如果可注入的话,就将payload等相关信息返回(即checkSqlInjection函数的返回值),再将其append到kb.injections中。
304行
action()是很总要的一个函数,该函数主要根据攻城师的命令行参数选型,从而利用存在注入漏洞的url,以进一步获取攻城师要获取的数据。
比如:当前的数据库用户、枚举数据库的所有数据表等等