diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 0b6caf96c..95d921847 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -289,7 +289,7 @@ optDict = { "noHuffman": "boolean", "profile": "boolean", "forceDns": "boolean", - "murphyRate": "integer", + "jitter": "integer", "smokeTest": "boolean", "fpTest": "boolean", "payloadLint": "boolean", diff --git a/lib/core/settings.py b/lib/core/settings.py index e6b6c7c16..b8e38fc41 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.7.243" +VERSION = "1.10.7.244" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) @@ -180,8 +180,26 @@ IDENTYWAF_PARSE_COUNT_LIMIT = 10 # Identify WAF/IPS inside limited size of responses IDENTYWAF_PARSE_PAGE_LIMIT = 4 * 1024 -# Maximum sleep time in "Murphy" (testing) mode -MAX_MURPHY_SLEEP_TIME = 3 +# Ceiling (seconds) for a simulated heavy-tailed latency spike in '--jitter' (testing) mode +MAX_JITTER_SPIKE_TIME = 6 + +# '--jitter=N' fault injection: ~1 in N requests is perturbed to stress the time-/boolean-based blind +# oracles' jitter defenses. Each fired event either adds realistic response LATENCY - Gaussian jitter +# spanning low->high network noise, or (JITTER_SPIKE_CHANCE of the time) a heavy-tailed spike - and lets +# the genuine request proceed, or short-circuits with a transient "junk" HTTP response (gateway 5xx, +# rate-limit, a same-HTTP-code interstitial/maintenance page, or an empty body). Values come from the +# offline jitter studies (tests/test_jitter_stress.py, tests/test_boolean_jitter.py). +JITTER_SIGMAS = (0.3, 0.5, 0.9) # low / medium / high continuous jitter (seconds) +JITTER_SPIKE_CHANCE = 0.25 # portion of latency events replaced by a heavy-tailed spike +JITTER_JUNK_RESPONSES = ( + ("

502 Bad Gateway

", 502), + ("

503 Service Unavailable

", 503), + ("

504 Gateway Time-out

", 504), + ('{"error": "too many requests"}', 429), + ("Just a moment...Checking your browser before accessing.", 200), + ("We'll be back shortly. Scheduled maintenance in progress.", 200), + ("", 200), +) # Regular expression used for extracting results from Google search GOOGLE_REGEX = r"webcache\.googleusercontent\.com/search\?q=cache:[^:]+:([^+]+)\+&cd=|url\?\w+=((?![^>]+webcache\.googleusercontent\.com)http[^>]+)&(sa=U|rct=j)" diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index a4ceb7819..b750fccaa 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -887,7 +887,7 @@ def cmdLineParser(argv=None): parser.add_argument("--yuge", dest="yuge", action="store_true", help=SUPPRESS) - parser.add_argument("--murphy-rate", dest="murphyRate", type=int, + parser.add_argument("--jitter", dest="jitter", type=int, help=SUPPRESS) parser.add_argument("--debug", dest="debug", action="store_true", diff --git a/lib/request/connect.py b/lib/request/connect.py index 6f990c89a..0d25307bc 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -113,7 +113,10 @@ from lib.core.settings import MAX_CONNECTION_READ_SIZE from lib.core.settings import MAX_CONNECTIONS_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import MAX_CONSECUTIVE_CONNECTION_ERRORS -from lib.core.settings import MAX_MURPHY_SLEEP_TIME +from lib.core.settings import MAX_JITTER_SPIKE_TIME +from lib.core.settings import JITTER_SIGMAS +from lib.core.settings import JITTER_SPIKE_CHANCE +from lib.core.settings import JITTER_JUNK_RESPONSES from lib.core.settings import META_REFRESH_REGEX from lib.core.settings import MAX_TIME_RESPONSES from lib.core.settings import MIN_TIME_RESPONSES @@ -374,17 +377,31 @@ class Connect(object): setHTTPHandlers() - if conf.dummy or conf.murphyRate and randomInt() % conf.murphyRate == 0: - if conf.murphyRate: - time.sleep(randomInt() % (MAX_MURPHY_SLEEP_TIME + 1)) - - page, headers, code = randomStr(int(randomInt()), alphabet=[_unichr(_) for _ in xrange(256)]), None, None if not conf.murphyRate else randomInt(3) - + if conf.dummy: + page, headers, code = randomStr(int(randomInt()), alphabet=[_unichr(_) for _ in xrange(256)]), None, None threadData.lastPage = page threadData.lastCode = code - return page, headers, code + # Simulated jitter (--jitter=N, testing): ~1 in N requests is perturbed to stress the time-/ + # boolean-based blind jitter defenses. Half the fired events add realistic response LATENCY + # (Gaussian jitter across low->high noise, or an occasional heavy-tailed spike) and let the + # genuine request proceed; the other half short-circuit with a transient "junk" response + # (gateway 5xx, rate-limit, a same-HTTP-code interstitial/maintenance page, or an empty body). + if conf.jitter and randomInt() % conf.jitter == 0: + if randomInt() % 2 == 0: + if random.random() < JITTER_SPIKE_CHANCE: + time.sleep(random.uniform(MAX_JITTER_SPIKE_TIME / 2.0, MAX_JITTER_SPIKE_TIME)) # heavy-tailed spike + else: + time.sleep(abs(random.gauss(0, random.choice(JITTER_SIGMAS)))) # continuous jitter + # NOTE: falls through to the genuine request, which now carries the injected latency + else: + page, code = random.choice(JITTER_JUNK_RESPONSES) + headers = None + threadData.lastPage = page + threadData.lastCode = code + return page, headers, code + if conf.liveCookies: with kb.locks.liveCookies: if not checkFile(conf.liveCookies, raiseOnError=False) or os.path.getsize(conf.liveCookies) == 0: