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 injData = [] 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 if not testSqlInj: infoMsg = "skipping '%s'" % targetUrl logger.info(infoMsg) continue if conf.multipleTargets: hostCount += 1 if conf.forms: name = kb.formNames[(targetUrl, targetMethod, targetData, targetCookie)] message = "[#%d] %s:\n%s %s" % (hostCount, "form%s" % (" '%s'" % name if name else ""), conf.method or HTTPMETHOD.GET, targetUrl) else: message = "%s %d:\n%s %s" % ("url", hostCount, conf.method or HTTPMETHOD.GET, targetUrl) if conf.cookie: message += "\nCookie: %s" % conf.cookie if conf.data: message += "\nPOST data: %s" % repr(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]: " % (conf.data if conf.data else "") conf.data = readInput(message, default=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 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(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 not kb.injPlace or not kb.injParameter or not kb.injType: if not conf.string and not conf.regexp and not conf.eRegexp: # 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() for place in (PLACE.URI, PLACE.POST, PLACE.GET): if place in parameters: parameters.remove(place) parameters.insert(0, place) for place in parameters: if not conf.paramDict.has_key(place): continue paramDict = conf.paramDict[place] for parameter, value in paramDict.items(): 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: pass elif not checkDynParam(place, parameter, value): warnMsg = "%s parameter '%s' is not dynamic" % (place, parameter) logger.warn(warnMsg) testSqlInj = False else: logMsg = "%s parameter '%s' is dynamic" % (place, parameter) logger.info(logMsg) kb.testedParams.add(paramKey) if testSqlInj: heuristicCheckSqlInjection(place, parameter, value) for parenthesis in range(0, 4): logMsg = "testing sql injection on %s " % place logMsg += "parameter '%s' with " % parameter logMsg += "%d parenthesis" % parenthesis logger.info(logMsg) injType = checkSqlInjection(place, parameter, value, parenthesis) if injType: injData.append((place, parameter, injType)) break else: infoMsg = "%s parameter '%s' is not " % (place, parameter) infoMsg += "injectable with %d parenthesis" % parenthesis logger.info(infoMsg) if not injData: warnMsg = "%s parameter '%s' is not " % (place, parameter) warnMsg += "injectable" logger.warn(warnMsg) if not kb.injPlace or not kb.injParameter or not kb.injType: if len(injData) == 1: injDataSelected = injData[0] elif len(injData) > 1: injDataSelected = __selectInjection(injData) else: raise sqlmapNotVulnerableException, "all parameters are not injectable" if injDataSelected == "Quit": return else: kb.injPlace, kb.injParameter, kb.injType = injDataSelected setInjection() if kb.injPlace and kb.injParameter and kb.injType: if 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: if kb.paramMatchRatio: conf.matchRatio = kb.paramMatchRatio[(kb.injPlace, kb.injParameter)] setMatchRatio() checkForParenthesis() action() except KeyboardInterrupt: if conf.multipleTargets: warnMsg = "Ctrl+C detected 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
def comparison(page, headers=None, getSeqMatcher=False): regExpResults = None # String to be excluded before calculating page hash if conf.eString and conf.eString in page: index = page.index(conf.eString) length = len(conf.eString) pageWithoutString = page[:index] pageWithoutString += page[index+length:] page = pageWithoutString # Regular expression matches to be excluded before calculating page hash if conf.eRegexp: regExpResults = re.findall(conf.eRegexp, page, re.I | re.M) if regExpResults: for regExpResult in regExpResults: index = page.index(regExpResult) length = len(regExpResult) pageWithoutRegExp = page[:index] pageWithoutRegExp += page[index+length:] page = pageWithoutRegExp # String to match in page when the query is valid if conf.string: if conf.string in page: return True else: return False # Regular expression to match in page when the query is valid if conf.regexp: if re.search(conf.regexp, page, re.I | re.M): return True else: return False if conf.seqLock: conf.seqLock.acquire() conf.seqMatcher.set_seq2(page) ratio = round(conf.seqMatcher.ratio(), 3) if conf.seqLock: conf.seqLock.release() # If the url is stable and we did not set yet the match ratio and the # current injected value changes the url page content if conf.matchRatio is None: if conf.md5hash is not None and ratio > 0.6 and ratio < 1: logger.debug("setting match ratio to %.3f" % ratio) conf.matchRatio = ratio elif conf.md5hash is None or ( conf.md5hash is not None and ratio < 0.6 ): logger.debug("setting match ratio to default value 0.900") conf.matchRatio = 0.900 if conf.matchRatio is not None: setMatchRatio() # If it has been requested to return the ratio and not a comparison # response if getSeqMatcher: return ratio # If the url is stable it returns True if the page has the same MD5 # hash of the original one # NOTE: old implementation, it did not handle automatically the fact # that the url could be not stable (due to VIEWSTATE, counter, etc.) #elif conf.md5hash is not None: # return conf.md5hash == md5hash(page) # If the url is not stable it returns sequence matcher between the # first untouched HTTP response page content and this content elif ratio > conf.matchRatio: return True else: return False