Skip to content

Commit 5fb4b47

Browse files
committed
Adding voting mechanism in heavy-jitter environment
1 parent e81a99c commit 5fb4b47

3 files changed

Lines changed: 44 additions & 21 deletions

File tree

lib/core/option.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,9 @@ def _setKnowledgeBaseAttributes(flushAll=True):
23112311
# calibrated TRUE/FALSE reference bodies for the boolean same-HTTP-code anomaly guard (inference.py)
23122312
kb.trueTemplate = None
23132313
kb.falseTemplate = None
2314+
2315+
# latched once network jitter is observed, so character validation escalates to a majority vote
2316+
kb.jitterSeen = False
23142317
kb.pageStable = None
23152318
kb.pageStructurallyStable = None
23162319
kb.partRun = None

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.7.244"
23+
VERSION = "1.10.7.245"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

lib/techniques/blind/inference.py

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -539,26 +539,35 @@ def validateChar(idx, value):
539539
forgedPayload = validationPayload.replace(markingValue, unescapedCharValue)
540540
forgedPayload = safeStringFormat(forgedPayload, (expressionUnescaped, idx))
541541

542-
result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
543-
544-
if result and getTechniqueData() is not None:
545-
trueCode, falseCode = getTechniqueData().trueCode, getTechniqueData().falseCode
546-
if timeBasedCompare:
547-
if trueCode:
548-
result = threadData.lastCode == trueCode
549-
if not result:
550-
warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, trueCode)
551-
singleTimeWarnMessage(warnMsg)
552-
# A boolean validation confirmed under an UNEXPECTED HTTP code (a transient 5xx/403/429/..
553-
# landing on the validation request itself) is not trustworthy - fail it so the character is
554-
# re-extracted, riding out the blip. On a clean target every code is true/false -> no-op.
555-
elif threadData.lastCode is not None and any((trueCode, falseCode)) and threadData.lastCode not in (trueCode, falseCode):
556-
result = False
557-
singleTimeWarnMessage("unexpected HTTP code '%s' during validation phase; will re-extract" % threadData.lastCode)
542+
def _check():
543+
result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
558544

559-
incrementCounter(getTechnique())
545+
if result and getTechniqueData() is not None:
546+
trueCode, falseCode = getTechniqueData().trueCode, getTechniqueData().falseCode
547+
if timeBasedCompare:
548+
if trueCode:
549+
result = threadData.lastCode == trueCode
550+
if not result:
551+
warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, trueCode)
552+
singleTimeWarnMessage(warnMsg)
553+
# A boolean validation confirmed under an UNEXPECTED HTTP code (a transient 5xx/403/429/..
554+
# landing on the validation request itself) is not trustworthy - fail it so the character is
555+
# re-extracted, riding out the blip. On a clean target every code is true/false -> no-op.
556+
elif threadData.lastCode is not None and any((trueCode, falseCode)) and threadData.lastCode not in (trueCode, falseCode):
557+
result = False
558+
singleTimeWarnMessage("unexpected HTTP code '%s' during validation phase; will re-extract" % threadData.lastCode)
560559

561-
return result
560+
incrementCounter(getTechnique())
561+
return result
562+
563+
# Adaptive majority vote: once jitter has been observed this run a single re-check can itself
564+
# be corrupted, so confirm the character by best-of-3 independent re-checks. Confidence-gated
565+
# -> exact no-op (single check) on a clean run or before any jitter is seen.
566+
if kb.get("jitterSeen"):
567+
votes = [_check() for _ in xrange(3)]
568+
return votes.count(True) >= 2
569+
570+
return _check()
562571

563572
def huffmanChar(idx):
564573
"""
@@ -823,6 +832,9 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None,
823832
unexpectedResponse = True
824833
singleTimeWarnMessage("unexpected response content detected. Will use (extra) validation step in similar cases")
825834

835+
if unexpectedCode or unexpectedResponse:
836+
kb.jitterSeen = True # latch: jitter observed -> escalate validateChar to a vote
837+
826838
if result:
827839
minValue = posValue
828840

@@ -862,7 +874,12 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None,
862874
retVal = minValue + 1
863875

864876
if retVal in originalTbl or (retVal == ord('\n') and CHAR_INFERENCE_MARK in payload):
865-
if (timeBasedCompare or unexpectedCode or unexpectedResponse) and kb.get("timeless") is None and not validateChar(idx, retVal):
877+
# Once jitter has been observed this run, confirm EVERY resolved character
878+
# (via the best-of-3 vote in validateChar), not only visibly-glitched ones:
879+
# a same-HTTP-code junk that resembles a model corrupts a bit INVISIBLY, and a
880+
# single-char value (e.g. a boolean --is-dba) has no other char to later trip
881+
# detection. Confidence-gated on kb.jitterSeen -> no-op on a clean target.
882+
if (timeBasedCompare or unexpectedCode or unexpectedResponse or kb.get("jitterSeen")) and kb.get("timeless") is None and not validateChar(idx, retVal):
866883
if restricted:
867884
# the character fell outside this column's observed range - re-extract
868885
# over the full charset (not timing noise, so no delay increase / retry count)
@@ -871,7 +888,10 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None,
871888
kb.originalTimeDelay = conf.timeSec
872889

873890
threadData.validationRun = 0
874-
if (retried or 0) < MAX_REVALIDATION_STEPS:
891+
kb.jitterSeen = True # a needed re-extraction is itself a jitter signal (covers time-based)
892+
# under detected jitter, allow more retries so a transient burst can't exhaust the budget
893+
maxRevalidation = MAX_REVALIDATION_STEPS * 3 if kb.jitterSeen else MAX_REVALIDATION_STEPS
894+
if (retried or 0) < maxRevalidation:
875895
errMsg = "invalid character detected. retrying.."
876896
logger.error(errMsg)
877897

0 commit comments

Comments
 (0)