Python:SQLMap源码精读—start函数
源代码
1 def start(): 2 """ 3 This function calls a function that performs checks on both URL 4 stability and all GET, POST, Cookie and User-Agent parameters to 5 check if they are dynamic and SQL injection affected 6 """ 7 if not conf.start: 8 return False 9 10 if conf.direct: 11 initTargetEnv() 12 setupTargetEnv() 13 action() 14 return True 15 16 if conf.url and not conf.forms: 17 kb.targetUrls.add(( conf.url, conf.method, conf.data, conf.cookie )) 18 19 if conf.configFile and not kb.targetUrls: 20 errMsg = "you did not edit the configuration file properly, set " 21 errMsg += "the target url, list of targets or google dork" 22 logger.error(errMsg) 23 return False 24 25 if kb.targetUrls and len(kb.targetUrls) > 1: 26 infoMsg = "sqlmap got a total of %d targets" % len(kb.targetUrls) 27 logger.info(infoMsg) 28 29 hostCount = 0 30 cookieStr = "" 31 setCookieAsInjectable = True 32 33 for targetUrl, targetMethod, targetData, targetCookie in kb.targetUrls: 34 try: 35 conf.url = targetUrl 36 conf.method = targetMethod 37 conf.data = targetData 38 conf.cookie = targetCookie 39 initTargetEnv() 40 parseTargetUrl() 41 42 testSqlInj = False 43 if PLACE.GET in conf.parameters: 44 for parameter in re.findall(r"([^=]+)=[^&]+&?", conf.parameters[PLACE.GET]): 45 paramKey = (conf.hostname, conf.path, PLACE.GET, parameter) 46 if paramKey not in kb.testedParams: 47 testSqlInj = True 48 break 49 else: 50 paramKey = (conf.hostname, conf.path, None, None) 51 if paramKey not in kb.testedParams: 52 testSqlInj = True 53 54 testSqlInj &= (conf.hostname, conf.path, None, None) not in kb.testedParams 55 56 if not testSqlInj: 57 infoMsg = "skipping '%s'" % targetUrl 58 logger.info(infoMsg) 59 continue 60 61 if conf.multipleTargets: 62 hostCount += 1 63 if conf.forms: 64 message = "[#%d] form:\n%s %s" % (hostCount, conf.method or HTTPMETHOD.GET, targetUrl) 65 else: 66 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 "") 67 68 if conf.cookie: 69 message += "\nCookie: %s" % conf.cookie 70 71 if conf.data: 72 message += "\nPOST data: %s" % urlencode(conf.data) if conf.data else "" 73 74 if conf.forms: 75 if conf.method == HTTPMETHOD.GET and targetUrl.find("?") == -1: 76 continue 77 78 message += "\ndo you want to test this form? [Y/n/q] " 79 test = readInput(message, default="Y") 80 81 if not test or test[0] in ("y", "Y"): 82 if conf.method == HTTPMETHOD.POST: 83 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 "") 84 conf.data = readInput(message, default=conf.data) 85 if extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data): 86 message = "do you want to fill blank fields with random values? [Y/n] " 87 test = readInput(message, default="Y") 88 if not test or test[0] in ("y", "Y"): 89 while extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data): 90 item = extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) 91 if item[-1] == '&': 92 conf.data = conf.data.replace(item, "%s%s&" % (item[:-1], randomStr())) 93 else: 94 conf.data = conf.data.replace(item, "%s%s" % (item, randomStr())) 95 conf.data = urldecode(conf.data) 96 97 elif conf.method == HTTPMETHOD.GET: 98 if conf.url.find("?") > -1: 99 firstPart = conf.url[:conf.url.find("?")] 100 secondPart = conf.url[conf.url.find("?")+1:] 101 message = "Edit GET data [default: %s]: " % secondPart 102 test = readInput(message, default=secondPart) 103 conf.url = "%s?%s" % (firstPart, test) 104 105 elif test[0] in ("n", "N"): 106 continue 107 elif test[0] in ("q", "Q"): 108 break 109 110 elif conf.realTest: 111 logger.info(message) 112 else: 113 message += "\ndo you want to test this url? [Y/n/q]" 114 test = readInput(message, default="Y") 115 116 if not test or test[0] in ("y", "Y"): 117 pass 118 elif test[0] in ("n", "N"): 119 continue 120 elif test[0] in ("q", "Q"): 121 break 122 123 logMsg = "testing url %s" % targetUrl 124 logger.info(logMsg) 125 126 setupTargetEnv() 127 if not checkConnection(suppressOutput = conf.forms) or not checkString() or not checkRegexp(): 128 continue 129 130 if conf.nullConnection: 131 checkNullConnection() 132 133 if not conf.dropSetCookie and conf.cj: 134 for _, cookie in enumerate(conf.cj): 135 cookie = getUnicode(cookie) 136 index = cookie.index(" for ") 137 138 cookieStr += "%s;" % cookie[8:index] 139 140 if cookieStr: 141 cookieStr = cookieStr[:-1] 142 143 if PLACE.COOKIE in conf.parameters: 144 message = "you provided an HTTP Cookie header value. " 145 message += "The target url provided its own Cookie within " 146 message += "the HTTP Set-Cookie header. Do you want to " 147 message += "continue using the HTTP Cookie values that " 148 message += "you provided? [Y/n] " 149 test = readInput(message, default="Y") 150 151 if not test or test[0] in ("y", "Y"): 152 setCookieAsInjectable = False 153 154 if setCookieAsInjectable: 155 conf.httpHeaders.append(("Cookie", cookieStr)) 156 conf.parameters[PLACE.COOKIE] = cookieStr 157 __paramDict = paramToDict(PLACE.COOKIE, cookieStr) 158 159 if __paramDict: 160 conf.paramDict[PLACE.COOKIE] = __paramDict 161 # TODO: consider the following line in __setRequestParams() 162 # __testableParameters = True 163 164 if (len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None)) \ 165 and (kb.injection.place is None or kb.injection.parameter is None): 166 if not conf.string and not conf.regexp: 167 # NOTE: this is not needed anymore, leaving only to display 168 # a warning message to the user in case the page is not stable 169 checkStability() 170 171 # Do a little prioritization reorder of a testable parameter list 172 parameters = conf.parameters.keys() 173 174 # Order of testing list (last to first) 175 orderList = (PLACE.URI, PLACE.GET, PLACE.POST) 176 177 for place in orderList: 178 if place in parameters: 179 parameters.remove(place) 180 parameters.insert(0, place) 181 182 proceed = True 183 184 for place in parameters: 185 # Test User-Agent and Referer headers only if 186 # --level >= 3 187 skip = (place == PLACE.UA and conf.level < 3) 188 skip |= (place == PLACE.REFERER and conf.level < 3) 189 190 # Test Cookie header only if --level >= 2 191 skip |= (place == PLACE.COOKIE and conf.level < 2) 192 193 skip &= not (place == PLACE.UA and intersect(USER_AGENT_ALIASES, conf.testParameter)) 194 skip &= not (place == PLACE.REFERER and intersect(REFERER_ALIASES, conf.testParameter)) 195 196 if skip: 197 continue 198 199 if not conf.paramDict.has_key(place): 200 continue 201 202 paramDict = conf.paramDict[place] 203 for parameter, value in paramDict.items(): 204 if not proceed: 205 break 206 207 testSqlInj = True 208 209 paramKey = (conf.hostname, conf.path, place, parameter) 210 211 if paramKey in kb.testedParams: 212 testSqlInj = False 213 214 infoMsg = "skipping previously processed %s parameter '%s'" % (place, parameter) 215 logger.info(infoMsg) 216 217 # Avoid dinamicity test if the user provided the 218 # parameter manually 219 elif parameter in conf.testParameter or conf.realTest: 220 pass 221 222 elif not checkDynParam(place, parameter, value): 223 warnMsg = "%s parameter '%s' is not dynamic" % (place, parameter) 224 logger.warn(warnMsg) 225 226 else: 227 logMsg = "%s parameter '%s' is dynamic" % (place, parameter) 228 logger.info(logMsg) 229 230 kb.testedParams.add(paramKey) 231 232 if testSqlInj: 233 check = heuristicCheckSqlInjection(place, parameter) 234 if not check and conf.realTest and\ 235 not simpletonCheckSqlInjection(place, parameter, value): 236 continue 237 238 logMsg = "testing sql injection on %s " % place 239 logMsg += "parameter '%s'" % parameter 240 logger.info(logMsg) 241 242 injection = checkSqlInjection(place, parameter, value) 243 proceed = not kb.endDetection 244 245 if injection is not None and injection.place is not None: 246 kb.injections.append(injection) 247 248 # In case when user wants to end detection phase (Ctrl+C) 249 if not proceed: 250 break 251 252 msg = "%s parameter '%s' " % (injection.place, injection.parameter) 253 msg += "is vulnerable. Do you want to keep testing the others? [y/N] " 254 test = readInput(msg, default="N") 255 256 if test[0] in ("n", "N"): 257 proceed = False 258 paramKey = (conf.hostname, conf.path, None, None) 259 kb.testedParams.add(paramKey) 260 else: 261 warnMsg = "%s parameter '%s' is not " % (place, parameter) 262 warnMsg += "injectable" 263 logger.warn(warnMsg) 264 265 if len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None): 266 if not conf.realTest: 267 errMsg = "all parameters are not injectable, try to " 268 errMsg += "increase --level/--risk values to perform " 269 errMsg += "more tests." 270 271 if isinstance(conf.tech, list) and len(conf.tech) > 0: 272 errMsg += " Rerun without providing the --technique switch." 273 274 if not conf.textOnly and kb.originalPage: 275 percent = (100.0 * len(getFilteredPageContent(kb.originalPage)) / len(kb.originalPage)) 276 errMsg += " Give it a go with the --text-only switch " 277 errMsg += "if the target page has a low percentage of " 278 errMsg += "textual content (~%.2f%% of " % percent 279 errMsg += "page content is text)" 280 281 raise sqlmapNotVulnerableException, errMsg 282 else: 283 errMsg = "it seems that all parameters are not injectable" 284 raise sqlmapNotVulnerableException, errMsg 285 else: 286 # Flush the flag 287 kb.testMode = False 288 289 __saveToSessionFile() 290 __showInjections() 291 __selectInjection() 292 293 if kb.injection.place is not None and kb.injection.parameter is not None: 294 if kb.testQueryCount == 0 and conf.realTest: 295 condition = False 296 elif conf.multipleTargets: 297 message = "do you want to exploit this SQL injection? [Y/n] " 298 exploit = readInput(message, default="Y") 299 300 condition = not exploit or exploit[0] in ("y", "Y") 301 else: 302 condition = True 303 if condition: 304 action() 305 306 except KeyboardInterrupt: 307 if conf.multipleTargets: 308 warnMsg = "user aborted in multiple target mode" 309 logger.warn(warnMsg) 310 311 message = "do you want to skip to the next target in list? [Y/n/q]" 312 test = readInput(message, default="Y") 313 314 if not test or test[0] in ("y", "Y"): 315 pass 316 elif test[0] in ("n", "N"): 317 return False 318 elif test[0] in ("q", "Q"): 319 raise sqlmapUserQuitException 320 else: 321 raise 322 323 except sqlmapUserQuitException: 324 raise 325 326 except sqlmapSilentQuitException: 327 raise 328 329 except exceptionsTuple, e: 330 e = getUnicode(e) 331 332 if conf.multipleTargets: 333 e += ", skipping to the next %s" % ("form" if conf.forms else "url") 334 logger.error(e) 335 else: 336 logger.critical(e) 337 return False 338 339 finally: 340 showHttpErrorCodes() 341 342 if conf.loggedToOut and not conf.multipleTargets: 343 logger.info("Fetched data logged to text files under '%s'" % conf.outputPath) 344 345 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,以进一步获取攻城师要获取的数据。
比如:当前的数据库用户、枚举数据库的所有数据表等等