diff --git a/lib/core/common.py b/lib/core/common.py index a97d49649..8cf95a16a 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2222,16 +2222,16 @@ def wasLastResponseDelayed(): # response times should be inside +-7*stdev([normal response times]) # Math reference: http://www.answers.com/topic/standard-deviation - deviation = stdev(kb.responseTimes) + deviation = stdev(kb.responseTimes.get(kb.responseTimeMode, [])) threadData = getCurrentThreadData() if deviation and not conf.direct: - if len(kb.responseTimes) < MIN_TIME_RESPONSES: + if len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES: warnMsg = "time-based standard deviation method used on a model " warnMsg += "with less than %d response times" % MIN_TIME_RESPONSES logger.warn(warnMsg) - lowerStdLimit = average(kb.responseTimes) + TIME_STDEV_COEFF * deviation + lowerStdLimit = average(kb.responseTimes[kb.responseTimeMode]) + TIME_STDEV_COEFF * deviation retVal = (threadData.lastQueryDuration >= max(MIN_VALID_DELAYED_RESPONSE, lowerStdLimit)) if not kb.testMode and retVal: diff --git a/lib/core/option.py b/lib/core/option.py index 38654ee8d..423871938 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1891,7 +1891,9 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.reflectiveCounters = {REFLECTIVE_COUNTER.MISS: 0, REFLECTIVE_COUNTER.HIT: 0} kb.requestCounter = 0 kb.resendPostOnRedirect = None - kb.responseTimes = [] + kb.responseTimes = {} + kb.responseTimeMode = None + kb.responseTimePayload = None kb.resumeValues = True kb.safeCharEncode = False kb.safeReq = AttribDict() @@ -1915,7 +1917,6 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.timeValidCharsRun = 0 kb.uChar = NULL kb.unionDuplicates = False - kb.whereCollectTimes = False kb.xpCmdshellAvailable = False if flushAll: diff --git a/lib/core/settings.py b/lib/core/settings.py index aa194119d..510365054 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -61,6 +61,9 @@ URI_QUESTION_MARKER = "__QUESTION_MARK__" ASTERISK_MARKER = "__ASTERISK_MARK__" REPLACEMENT_MARKER = "__REPLACEMENT_MARK__" +RANDOM_INTEGER_MARKER = "[RANDINT]" +RANDOM_STRING_MARKER = "[RANDSTR]" + PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" PRINTABLE_CHAR_REGEX = r"[^\x00-\x1f\x7f-\xff]" diff --git a/lib/request/connect.py b/lib/request/connect.py index cffc9da60..eec80328d 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -97,6 +97,8 @@ from lib.core.settings import LARGE_CHUNK_TRIM_MARKER from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PERMISSION_DENIED_REGEX from lib.core.settings import PLAIN_TEXT_CONTENT_TYPE +from lib.core.settings import RANDOM_INTEGER_MARKER +from lib.core.settings import RANDOM_STRING_MARKER from lib.core.settings import REPLACEMENT_MARKER from lib.core.settings import TEXT_CONTENT_TYPE_REGEX from lib.core.settings import UNENCODED_ORIGINAL_VALUE @@ -1020,34 +1022,37 @@ class Connect(object): post = urlencode(post, spaceplus=kb.postSpaceToPlus) if timeBasedCompare: - if len(kb.responseTimes) < MIN_TIME_RESPONSES: + if len(kb.responseTimes.get(kb.responseTimeMode, [])) < MIN_TIME_RESPONSES: clearConsoleLine() + kb.responseTimes.setdefault(kb.responseTimeMode, []) + if conf.tor: warnMsg = "it's highly recommended to avoid usage of switch '--tor' for " warnMsg += "time-based injections because of its high latency time" singleTimeWarnMessage(warnMsg) - warnMsg = "[%s] [WARNING] time-based comparison requires " % time.strftime("%X") + warnMsg = "[%s] [WARNING] %stime-based comparison requires " % (time.strftime("%X"), "(case) " if kb.responseTimeMode else "") warnMsg += "larger statistical model, please wait" dataToStdout(warnMsg) - while len(kb.responseTimes) < MIN_TIME_RESPONSES: - Connect.queryPage(content=True) + while len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES: + value = kb.responseTimePayload.replace(RANDOM_INTEGER_MARKER, str(randomInt(6))).replace(RANDOM_STRING_MARKER, randomStr()) if kb.responseTimePayload else kb.responseTimePayload + Connect.queryPage(value=value, content=True, raise404=False) dataToStdout('.') - dataToStdout("\n") + dataToStdout(" (done)\n") elif not kb.testMode: - warnMsg = "it is very important not to stress the network adapter " + warnMsg = "it is very important to not stress the network adapter " warnMsg += "during usage of time-based payloads to prevent potential " - warnMsg += "errors " + warnMsg += "disruptions " singleTimeWarnMessage(warnMsg) if not kb.laggingChecked: kb.laggingChecked = True - deviation = stdev(kb.responseTimes) + deviation = stdev(kb.responseTimes[kb.responseTimeMode]) if deviation > WARN_TIME_STDEV: kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE @@ -1115,7 +1120,8 @@ class Connect(object): if timeBasedCompare: return wasLastResponseDelayed() elif noteResponseTime: - kb.responseTimes.append(threadData.lastQueryDuration) + kb.responseTimes.setdefault(kb.responseTimeMode, []) + kb.responseTimes[kb.responseTimeMode].append(threadData.lastQueryDuration) if not response and removeReflection: page = removeReflectiveValues(page, payload) diff --git a/lib/request/inject.py b/lib/request/inject.py index 4a3594115..bd83287b9 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -432,6 +432,8 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if time and (isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED)) and not found: + kb.responseTimeMode = re.sub(r"(?i)[^a-z]", "", re.sub(r"'[^']+'", "", expression)) if re.search(r"(?i)SELECT.+FROM", expression) else None + if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME): kb.technique = PAYLOAD.TECHNIQUE.TIME else: @@ -441,7 +443,6 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser value = _goBooleanProxy(booleanExpression) else: value = _goInferenceProxy(query, fromUser, batch, unpack, charsetType, firstChar, lastChar, dump) - else: errMsg = "none of the injection types identified can be " errMsg += "leveraged to retrieve queries output" @@ -449,6 +450,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser finally: kb.resumeValues = True + kb.responseTimeMode = None conf.tbl = popValue() conf.db = popValue() diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index aa04ceb9c..41b54d7ca 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -47,8 +47,10 @@ from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import MAX_BISECTION_LENGTH from lib.core.settings import MAX_TIME_REVALIDATION_STEPS +from lib.core.settings import NULL from lib.core.settings import PARTIAL_HEX_VALUE_MARKER from lib.core.settings import PARTIAL_VALUE_MARKER +from lib.core.settings import RANDOM_INTEGER_MARKER from lib.core.settings import VALID_TIME_CHARS_RUN_THRESHOLD from lib.core.threads import getCurrentThreadData from lib.core.threads import runThreads @@ -261,29 +263,23 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None while len(charTbl) != 1: position = (len(charTbl) >> 1) posValue = charTbl[position] + falsePayload = None if "'%s'" % CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) + falsePayload = safeStringFormat(payload, (expressionUnescaped, idx, RANDOM_INTEGER_MARKER)) else: # e.g.: ... > '%c' -> ... > ORD(..) markingValue = "'%s'" % CHAR_INFERENCE_MARK unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue)) forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) + falsePayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, NULL) - if timeBasedCompare and kb.whereCollectTimes: - kb.responseTimes = [] - - warnMsg = "\n[%s] [WARNING] time-based comparison requires " % time.strftime("%X") - warnMsg += "larger statistical model, please wait" - dataToStdout(warnMsg) - - while len(kb.responseTimes) < MIN_TIME_RESPONSES: - falseWherePayload = re.sub(r"\b%s\b" % posValue, str(randomInt(6)), forgedPayload) - Request.queryPage(falseWherePayload, content=True, raise404=False) - dataToStdout('.') - - dataToStdout("\n") - kb.whereCollectTimes = False + if timeBasedCompare: + if kb.responseTimeMode: + kb.responseTimePayload = falsePayload + else: + kb.responseTimePayload = None result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(kb.technique) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index ceabe2eb8..86e088ef3 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -236,10 +236,6 @@ class Entries: query = whereQuery(query) - if conf.dumpWhere: - kb.whereCollectTimes = True - pushValue(kb.responseTimes) - count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) lengths = {} @@ -328,10 +324,6 @@ class Entries: warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) - if conf.dumpWhere: - kb.responseTimes = popValue() - kb.whereCollectTimes = False - for column, columnEntries in entries.items(): length = max(lengths[column], len(column))