diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 5767fdba8..2e57637f7 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -99,11 +99,15 @@ def heuristicCheckSqlInjection(place, parameter, value): postfix = conf.postfix payload = "%s%s%s" % (prefix, randomStr(length=10, alphabet=['"', '\'', ')', '(']), postfix) + if place == "URI": payload = conf.paramDict[place][parameter].replace('*', payload) + Request.queryPage(payload, place) result = kb.lastErrorPage and kb.lastErrorPage[0]==kb.lastRequestUID + infoMsg = "(error based) heuristics show that %s parameter '%s' is " % (place, parameter) + if result: infoMsg += "injectable" logger.info(infoMsg) @@ -147,6 +151,7 @@ def checkDynamicContent(*pages): This function checks if the provided pages have dynamic content. If they are dynamic, their content differs at specific lines. """ + infoMsg = "searching for dynamic content" logger.info(infoMsg) @@ -170,6 +175,7 @@ def checkDynamicContent(*pages): for other in kb.dynamicContent: found = True + if other.pageTotal == item.pageTotal: if isinstance(other.lineNumber, int): if other.lineNumber == item.lineNumber - 1: @@ -235,28 +241,34 @@ def checkStability(): elif not condition: warnMsg = "url is not stable, sqlmap will base the page " - warnMsg += "comparison on a sequence matcher. if no dynamic nor " - warnMsg += "injectable parameters are detected, or in case of junk " - warnMsg += "results, refer to user's " - warnMsg += "manual paragraph 'Page comparison' and provide a " - warnMsg += "string or regular expression to match on" + warnMsg += "comparison on a sequence matcher. If no dynamic nor " + warnMsg += "injectable parameters are detected, or in case of " + warnMsg += "junk results, refer to user's manual paragraph " + warnMsg += "'Page comparison' and provide a string or regular " + warnMsg += "expression to match on" logger.warn(warnMsg) message = "how do you want to proceed? [C(ontinue)/s(tring)/r(egex)/q(uit)] " test = readInput(message, default="C") + if test and test[0] in ("q", "Q"): raise sqlmapUserQuitException + elif test and test[0] in ("s", "S"): showStaticWords(firstPage, secondPage) + message = "please enter value for parameter 'string': " test = readInput(message) + if test: conf.string = test else: raise sqlmapSilentQuitException + elif test and test[0] in ("r", "R"): message = "please enter value for parameter 'regex': " test = readInput(message) + if test: conf.regex = test else: diff --git a/lib/core/common.py b/lib/core/common.py index d9b0b2690..d831c0ee2 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1107,33 +1107,42 @@ def sanitizeAsciiString(subject): def preparePageForLineComparison(page): retVal = page + if isinstance(page, basestring): return page.replace("><", ">\n<").replace("
", "\n").splitlines() + return retVal def getFilteredPageContent(page): retVal = page + if isinstance(page, basestring): retVal = re.sub(r"(?s)||<[^>]+>|\t|\n|\r", " ", page) + while retVal.find(" ") != -1: retVal = retVal.replace(" ", " ") + return retVal def getPageTextWordsSet(page): retVal = None + if isinstance(page, basestring): page = getFilteredPageContent(page) retVal = set(re.findall(r"\w+", page)) + return retVal def showStaticWords(firstPage, secondPage): infoMsg = "finding static words in longest matching part of dynamic page content" logger.info(infoMsg) + firstPage = getFilteredPageContent(firstPage) secondPage = getFilteredPageContent(secondPage) match = SequenceMatcher(None, firstPage, secondPage).find_longest_match(0, len(firstPage), 0, len(secondPage)) commonText = firstPage[match[0]:match[0]+match[2]] commonWords = getPageTextWordsSet(commonText) + infoMsg = "static words: " if commonWords: @@ -1190,6 +1199,7 @@ def posixToNtSlashes(filepath): >>> posixToNtSlashes('C:/Windows') 'C:\\\\Windows' """ + return filepath.replace('/', '\\') def ntToPosixSlashes(filepath): @@ -1199,6 +1209,7 @@ def ntToPosixSlashes(filepath): >>> ntToPosixSlashes('C:\\Windows') 'C:/Windows' """ + return filepath.replace('\\', '/') def isBase64EncodedString(subject): @@ -1209,6 +1220,7 @@ def isBase64EncodedString(subject): >>> isBase64EncodedString('123456') False """ + return re.match(r"\A(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z", subject) is not None def isHexEncodedString(subject): @@ -1219,6 +1231,7 @@ def isHexEncodedString(subject): >>> isHexEncodedString('test') False """ + return re.match(r"\A[0-9a-fA-F]+\Z", subject) is not None def getConsoleWidth(default=80): @@ -1229,12 +1242,14 @@ def getConsoleWidth(default=80): else: output=subprocess.Popen('stty size', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.read() items = output.split() + if len(items) == 2 and items[1].isdigit(): width = int(items[1]) if width is None: try: import curses + stdscr = curses.initscr() _, width = stdscr.getmaxyx() curses.endwin() @@ -1268,10 +1283,13 @@ def calculateDeltaSeconds(start, epsilon=0.05): def getInjectionCase(name): retVal = None + for case in kb.injections.root.case: if case.name == name: retVal = case + break + return retVal def initCommonOutputs(): @@ -1302,9 +1320,9 @@ def getFileItems(filename): retVal = [] checkFile(filename) - file = codecs.open(filename, 'r', conf.dataEncoding) + ifile = codecs.open(filename, 'r', conf.dataEncoding) - for line in file.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used + for line in ifile.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used if line.find('#') != -1: line = line[:line.find('#')] line = line.strip()