Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3abd3e1

Browse files
committed
Patching silent per-thread issue with technique switching (fixes #3784)
1 parent 32e09c8 commit 3abd3e1

9 files changed

Lines changed: 74 additions & 53 deletions

File tree

lib/core/agent.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from lib.core.common import extractRegexResult
1313
from lib.core.common import filterNone
1414
from lib.core.common import getSQLSnippet
15+
from lib.core.common import getTechnique
1516
from lib.core.common import isDBMSVersionAtLeast
1617
from lib.core.common import isNumber
1718
from lib.core.common import isTechniqueAvailable
@@ -89,8 +90,8 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N
8990

9091
if kb.forceWhere:
9192
where = kb.forceWhere
92-
elif where is None and isTechniqueAvailable(kb.technique):
93-
where = kb.injection.data[kb.technique].where
93+
elif where is None and isTechniqueAvailable(getTechnique()):
94+
where = kb.injection.data[getTechnique()].where
9495

9596
if kb.injection.place is not None:
9697
place = kb.injection.place
@@ -234,8 +235,8 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None):
234235
expression = unescaper.escape(expression)
235236
query = None
236237

237-
if where is None and kb.technique and kb.technique in kb.injection.data:
238-
where = kb.injection.data[kb.technique].where
238+
if where is None and getTechnique() is not None and getTechnique() in kb.injection.data:
239+
where = kb.injection.data[getTechnique()].where
239240

240241
# If we are replacing (<where>) the parameter original value with
241242
# our payload do not prepend with the prefix
@@ -244,7 +245,7 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None):
244245

245246
# If the technique is stacked queries (<stype>) do not put a space
246247
# after the prefix or it is in GROUP BY / ORDER BY (<clause>)
247-
elif kb.technique == PAYLOAD.TECHNIQUE.STACKED:
248+
elif getTechnique() == PAYLOAD.TECHNIQUE.STACKED:
248249
query = kb.injection.prefix
249250
elif kb.injection.clause == [2, 3] or kb.injection.clause == [2] or kb.injection.clause == [3]:
250251
query = kb.injection.prefix
@@ -282,9 +283,9 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None, trimEmp
282283
# Take default values if None
283284
suffix = kb.injection.suffix if kb.injection and suffix is None else suffix
284285

285-
if kb.technique and kb.technique in kb.injection.data:
286-
where = kb.injection.data[kb.technique].where if where is None else where
287-
comment = kb.injection.data[kb.technique].comment if comment is None else comment
286+
if getTechnique() is not None and getTechnique() in kb.injection.data:
287+
where = kb.injection.data[getTechnique()].where if where is None else where
288+
comment = kb.injection.data[getTechnique()].comment if comment is None else comment
288289

289290
if Backend.getIdentifiedDbms() == DBMS.ACCESS and any((comment or "").startswith(_) for _ in ("--", "[GENERIC_SQL_COMMENT]")):
290291
comment = queries[DBMS.ACCESS].comment.query

lib/core/common.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,20 @@ def readInput(message, default=None, checkBatch=True, boolean=False):
11251125

11261126
return retVal or ""
11271127

1128+
def setTechnique(technique):
1129+
"""
1130+
Thread-safe setting of currently used technique (Note: dealing with cases of per-thread technique switching)
1131+
"""
1132+
1133+
getCurrentThreadData().technique = technique
1134+
1135+
def getTechnique():
1136+
"""
1137+
Thread-safe getting of currently used technique
1138+
"""
1139+
1140+
return getCurrentThreadData().technique or kb.technique
1141+
11281142
def randomRange(start=0, stop=1000, seed=None):
11291143
"""
11301144
Returns random integer value in given range
@@ -3231,18 +3245,16 @@ def isHeavyQueryBased(technique=None):
32313245
Returns True whether current (kb.)technique is heavy-query based
32323246
32333247
>>> pushValue(kb.injection.data)
3234-
>>> pushValue(kb.technique)
3235-
>>> kb.technique = PAYLOAD.TECHNIQUE.STACKED
3236-
>>> kb.injection.data[kb.technique] = [test for test in getSortedInjectionTests() if "heavy" in test["title"].lower()][0]
3248+
>>> setTechnique(PAYLOAD.TECHNIQUE.STACKED)
3249+
>>> kb.injection.data[getTechnique()] = [test for test in getSortedInjectionTests() if "heavy" in test["title"].lower()][0]
32373250
>>> isHeavyQueryBased()
32383251
True
3239-
>>> kb.technique = popValue()
32403252
>>> kb.injection.data = popValue()
32413253
"""
32423254

32433255
retVal = False
32443256

3245-
technique = technique or kb.technique
3257+
technique = technique or getTechnique()
32463258

32473259
if isTechniqueAvailable(technique):
32483260
data = getTechniqueData(technique)
@@ -3630,7 +3642,7 @@ def unhandledExceptionMessage():
36303642
errMsg += "Python version: %s\n" % PYVERSION
36313643
errMsg += "Operating system: %s\n" % platform.platform()
36323644
errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap\.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding))
3633-
errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None))
3645+
errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, getTechnique()) if getTechnique() is not None else ("DIRECT" if conf.get("direct") else None))
36343646
errMsg += "Back-end DBMS:"
36353647

36363648
if Backend.getDbms() is not None:

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from thirdparty.six import unichr as _unichr
1919

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

lib/core/threads.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def reset(self):
6363
self.retriesCount = 0
6464
self.seqMatcher = difflib.SequenceMatcher(None)
6565
self.shared = shared
66+
self.technique = None
6667
self.validationRun = 0
6768
self.valueStack = []
6869

@@ -113,6 +114,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
113114

114115
kb.threadContinue = True
115116
kb.threadException = False
117+
kb.technique = ThreadData.technique
116118

117119
if threadChoice and numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)):
118120
while True:
@@ -206,6 +208,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
206208
finally:
207209
kb.threadContinue = True
208210
kb.threadException = False
211+
kb.technique = None
209212

210213
for lock in kb.locks.values():
211214
if lock.locked():

lib/request/inject.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from lib.core.common import extractExpectedValue
2020
from lib.core.common import filterNone
2121
from lib.core.common import getPublicTypeMembers
22+
from lib.core.common import getTechnique
2223
from lib.core.common import getTechniqueData
2324
from lib.core.common import hashDBRetrieve
2425
from lib.core.common import hashDBWrite
@@ -31,6 +32,7 @@
3132
from lib.core.common import pushValue
3233
from lib.core.common import randomStr
3334
from lib.core.common import readInput
35+
from lib.core.common import setTechnique
3436
from lib.core.common import singleTimeWarnMessage
3537
from lib.core.compat import xrange
3638
from lib.core.data import conf
@@ -88,7 +90,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar
8890
if value is not None:
8991
return value
9092

91-
timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED))
93+
timeBasedCompare = (getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED))
9294

9395
if timeBasedCompare and conf.threads > 1 and kb.forceThreads is None:
9496
msg = "multi-threading is considered unsafe in "
@@ -160,9 +162,9 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char
160162
parameter through a bisection algorithm.
161163
"""
162164

163-
initTechnique(kb.technique)
165+
initTechnique(getTechnique())
164166

165-
query = agent.prefixQuery(kb.injection.data[kb.technique].vector)
167+
query = agent.prefixQuery(kb.injection.data[getTechnique()].vector)
166168
query = agent.suffixQuery(query)
167169
payload = agent.payload(newValue=query)
168170
count = None
@@ -307,24 +309,24 @@ def _goBooleanProxy(expression):
307309
Retrieve the output of a boolean based SQL query
308310
"""
309311

310-
initTechnique(kb.technique)
312+
initTechnique(getTechnique())
311313

312314
if conf.dnsDomain:
313-
query = agent.prefixQuery(kb.injection.data[kb.technique].vector)
315+
query = agent.prefixQuery(kb.injection.data[getTechnique()].vector)
314316
query = agent.suffixQuery(query)
315317
payload = agent.payload(newValue=query)
316318
output = _goDns(payload, expression)
317319

318320
if output is not None:
319321
return output
320322

321-
vector = kb.injection.data[kb.technique].vector
323+
vector = kb.injection.data[getTechnique()].vector
322324
vector = vector.replace(INFERENCE_MARKER, expression)
323325
query = agent.prefixQuery(vector)
324326
query = agent.suffixQuery(query)
325327
payload = agent.payload(newValue=query)
326328

327-
timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)
329+
timeBasedCompare = getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)
328330

329331
output = hashDBRetrieve(expression, checkConf=True)
330332

@@ -401,7 +403,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
401403

402404
if not conf.forceDns:
403405
if union and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
404-
kb.technique = PAYLOAD.TECHNIQUE.UNION
406+
setTechnique(PAYLOAD.TECHNIQUE.UNION)
405407
kb.forcePartialUnion = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector[8]
406408
fallback = not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion
407409

@@ -433,7 +435,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
433435
singleTimeWarnMessage(warnMsg)
434436

435437
if error and any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) and not found:
436-
kb.technique = PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY
438+
setTechnique(PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY)
437439
value = errorUse(forgeCaseExpression if expected == EXPECTED.BOOL else query, dump)
438440
count += 1
439441
found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE
@@ -446,7 +448,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
446448
singleTimeWarnMessage(warnMsg)
447449

448450
if blind and isTechniqueAvailable(PAYLOAD.TECHNIQUE.BOOLEAN) and not found:
449-
kb.technique = PAYLOAD.TECHNIQUE.BOOLEAN
451+
setTechnique(PAYLOAD.TECHNIQUE.BOOLEAN)
450452

451453
if expected == EXPECTED.BOOL:
452454
value = _goBooleanProxy(booleanExpression)
@@ -461,9 +463,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
461463
kb.responseTimeMode = "%s|%s" % (match.group(1), match.group(2)) if match else None
462464

463465
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME):
464-
kb.technique = PAYLOAD.TECHNIQUE.TIME
466+
setTechnique(PAYLOAD.TECHNIQUE.TIME)
465467
else:
466-
kb.technique = PAYLOAD.TECHNIQUE.STACKED
468+
setTechnique(PAYLOAD.TECHNIQUE.STACKED)
467469

468470
if expected == EXPECTED.BOOL:
469471
value = _goBooleanProxy(booleanExpression)
@@ -505,12 +507,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
505507

506508
def goStacked(expression, silent=False):
507509
if PAYLOAD.TECHNIQUE.STACKED in kb.injection.data:
508-
kb.technique = PAYLOAD.TECHNIQUE.STACKED
510+
setTechnique(PAYLOAD.TECHNIQUE.STACKED)
509511
else:
510512
for technique in getPublicTypeMembers(PAYLOAD.TECHNIQUE, True):
511513
_ = getTechniqueData(technique)
512514
if _ and "stacked" in _["title"].lower():
513-
kb.technique = technique
515+
setTechnique(technique)
514516
break
515517

516518
expression = cleanQuery(expression)

lib/takeover/web.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from lib.core.common import getManualDirectories
2121
from lib.core.common import getPublicTypeMembers
2222
from lib.core.common import getSQLSnippet
23+
from lib.core.common import getTechnique
2324
from lib.core.common import isTechniqueAvailable
2425
from lib.core.common import isWindowsDriveLetterPath
2526
from lib.core.common import normalizePath
@@ -147,8 +148,8 @@ def _webFileInject(self, fileContent, fileName, directory):
147148
uplQuery = getUnicode(fileContent).replace(SHELL_WRITABLE_DIR_TAG, directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)
148149
query = ""
149150

150-
if isTechniqueAvailable(kb.technique):
151-
where = kb.injection.data[kb.technique].where
151+
if isTechniqueAvailable(getTechnique()):
152+
where = kb.injection.data[getTechnique()].where
152153

153154
if where == PAYLOAD.WHERE.NEGATIVE:
154155
randInt = randomInt()

0 commit comments

Comments
 (0)