Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions data/txt/common-columns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,42 @@ shouji
u_pass
hashedPw

# API keys, tokens and secrets

api_key
apikey
api_token
access_token
refresh_token
auth_token
session_token
remember_token
secret_key
client_secret
encryption_key
reset_token
password_reset_token
verification_token
confirmation_token
otp
otp_secret
totp_secret
mfa_secret
two_factor_secret

# Framework and identity columns

deleted_at
uuid
role
is_admin
is_active
is_verified
date_of_birth
dob
credit_card
postal_code

# password (international)

adgangskode
Expand Down
45 changes: 45 additions & 0 deletions data/txt/common-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1807,3 +1807,48 @@
/opt/kibana/config/kibana.yml
/etc/kibana/kibana.yml
/etc/elasticsearch/elasticsearch.yml

# Containers and orchestration secrets

/.dockerenv
/proc/self/cgroup
/proc/1/cgroup
/run/secrets/kubernetes.io/serviceaccount/token
/var/run/secrets/kubernetes.io/serviceaccount/token
/run/secrets/kubernetes.io/serviceaccount/namespace
/var/run/secrets/kubernetes.io/serviceaccount/namespace
/run/secrets/kubernetes.io/serviceaccount/ca.crt

# Cloud provider credentials

/root/.aws/credentials
/root/.aws/config
/root/.config/gcloud/application_default_credentials.json
/root/.config/gcloud/credentials.db
/root/.azure/accessTokens.json
/root/.kube/config

# SSH keys and DB/tool history

/root/.ssh/authorized_keys
/root/.ssh/id_ed25519
/root/.ssh/id_ecdsa
/root/.ssh/id_dsa
/root/.mysql_history
/root/.psql_history
/root/.rediscli_history
/root/.python_history
/root/.sqlite_history

# dotenv, VCS and app config under common web roots

/var/www/.env
/var/www/html/.env
/app/.env
/usr/share/nginx/html/.env
/var/www/html/.git/config
/var/www/html/.git/HEAD
/var/www/html/wp-config.php
/app/application.properties
/app/application.yml
/app/docker-compose.yml
9 changes: 9 additions & 0 deletions data/txt/common-tables.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3372,17 +3372,23 @@ django_session
django_migrations
django_content_type
django_admin_log
auth_user_groups
auth_user_user_permissions

# laravel

migrations
password_resets
password_reset_tokens
failed_jobs
personal_access_tokens
job_batches
model_has_roles
model_has_permissions
role_has_permissions
cache
cache_locks
notifications

# rails

Expand Down Expand Up @@ -3420,3 +3426,6 @@ subscriptions
users_bak
users_old
orders_backup
user_roles
role_permissions
tokens
77 changes: 46 additions & 31 deletions lib/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
from lib.core.dicts import DBWIRE_MODULES
from lib.core.dicts import DEFAULT_DOC_ROOTS
from lib.core.dicts import DEPRECATED_OPTIONS
from lib.core.dicts import HTML_ENTITIES
from lib.core.dicts import OBSOLETE_OPTIONS
from lib.core.dicts import SQL_STATEMENTS
from lib.core.enums import ADJUST_TIME_DELAY
Expand Down Expand Up @@ -189,7 +190,6 @@
from lib.core.settings import URLENCODE_CHAR_LIMIT
from lib.core.settings import URLENCODE_FAILSAFE_CHARS
from lib.core.settings import USER_AGENT_ALIASES
from lib.core.settings import VERSION_COMPARISON_CORRECTION
from lib.core.settings import VERSION_STRING
from lib.core.settings import ZIP_HEADER
from lib.core.settings import WEBSCARAB_SPLITTER
Expand Down Expand Up @@ -629,7 +629,9 @@ def paramToDict(place, parameters=None):
if place in conf.parameters and not parameters:
parameters = conf.parameters[place]

parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters)
# Note: shield real HTML entities (e.g. &amp; &mdash; &rsquo;) from being split on the '&'/';' delimiter;
# match a named entity of any length but only when it is a genuine one, so a plain "&word;" still splits
parameters = re.sub(r"&(\w+);", lambda match: "%s%s%s" % (PARAMETER_AMP_MARKER, match.group(1), PARAMETER_SEMICOLON_MARKER) if match.group(1) in HTML_ENTITIES else match.group(0), parameters)
if place == PLACE.COOKIE:
splitParams = parameters.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)
else:
Expand Down Expand Up @@ -1807,7 +1809,17 @@ def parseTargetUrl():
errMsg += "in the hostname part"
raise SqlmapGenericException(errMsg)

hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filterNone((re.search(r"\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P<port>\d+))?", urlSplit.netloc).group("port")))
netloc = urlSplit.netloc

# Note: strip any URL userinfo ('user:pass@') so it is not mistaken for host:port (as the proxy
# parser already does). Credentials embedded in the URL are NOT applied - '--auth-cred' is the
# supported way to pass HTTP authentication - so warn rather than silently dropping them.
if '@' in netloc:
if not any((conf.get("authType"), conf.get("authCred"), conf.get("authFile"))):
singleTimeWarnMessage("credentials in the target URL are ignored. Use '--auth-cred' for HTTP authentication")
netloc = netloc.rsplit('@', 1)[-1]

hostnamePort = netloc.split(":") if not re.search(r"\[.+\]", netloc) else filterNone((re.search(r"\[.+\]", netloc).group(0), re.search(r"\](:(?P<port>\d+))?", netloc).group("port")))

conf.scheme = (urlSplit.scheme.strip().lower() or "http")
conf.path = urlSplit.path.strip()
Expand Down Expand Up @@ -2943,7 +2955,12 @@ def extractErrorMessage(page):

if match:
candidate = htmlUnescape(match.group("result")).replace("<br>", "\n").strip()
if candidate and (1.0 * len(re.findall(r"[^A-Za-z,. ]", candidate)) / len(candidate) > MIN_ERROR_PARSING_NON_WRITING_RATIO):
# Note: only the generic '(fatal|error|warning|exception): ...' regexes can capture
# arbitrary prose, so guard those with the non-writing-char ratio; the specific
# DBMS-signature regexes (e.g. MSSQL 'Unclosed quotation mark ...') are definitive and
# must not be discarded just because the message happens to read like plain text
generic = "(fatal|error|warning|exception)" in regex
if candidate and (not generic or 1.0 * len(re.findall(r"[^A-Za-z,. ]", candidate)) / len(candidate) > MIN_ERROR_PARSING_NON_WRITING_RATIO):
retVal = candidate
break

Expand Down Expand Up @@ -3497,42 +3514,40 @@ def isDBMSVersionAtLeast(minimum):
if not any(isNoneValue(_) for _ in (Backend.getVersion(), minimum)) and Backend.getVersion() != UNKNOWN_DBMS_VERSION:
version = Backend.getVersion().replace(" ", "").rstrip('.')

correction = 0.0
# Note: a fuzzy/ranged detected version (e.g. '>2', '<2') is captured as a sign so an
# otherwise-equal comparison still resolves in the right direction
vSign = 0
if ">=" in version:
pass
elif '>' in version:
correction = VERSION_COMPARISON_CORRECTION
vSign = 1
elif '<' in version:
correction = -VERSION_COMPARISON_CORRECTION
vSign = -1

version = extractRegexResult(r"(?P<result>[0-9][0-9.]*)", version)

if version:
if '.' in version:
parts = version.split('.', 1)
parts[1] = filterStringValue(parts[1], '[0-9]')
version = '.'.join(parts)
minimum = minimum if isinstance(minimum, six.string_types) else getUnicode(minimum)

try:
version = float(filterStringValue(version, '[0-9.]')) + correction
except ValueError:
return None

if isinstance(minimum, six.string_types):
if '.' in minimum:
parts = minimum.split('.', 1)
parts[1] = filterStringValue(parts[1], '[0-9]')
minimum = '.'.join(parts)

correction = 0.0
if minimum.startswith(">="):
pass
elif minimum.startswith(">"):
correction = VERSION_COMPARISON_CORRECTION

minimum = float(filterStringValue(minimum, '[0-9.]')) + correction

retVal = version >= minimum
mSign = 0
if minimum.startswith(">="):
pass
elif minimum.startswith(">"):
mSign = 1

minimum = extractRegexResult(r"(?P<result>[0-9][0-9.]*)", minimum)

if minimum:
# Note: compare dotted versions component-wise as int tuples, not as floats; a float
# collapses e.g. 5.4.3->5.43 and 5.10.0->5.100(==5.1), silently mis-ordering multi-part
# or multi-digit-minor versions (MariaDB 10.11, PostgreSQL 9.10, Presto 0.99 vs 0.178)
vParts = tuple(int(_) for _ in re.findall(r"\d+", version))
mParts = tuple(int(_) for _ in re.findall(r"\d+", minimum))
length = max(len(vParts), len(mParts))
vParts += (0,) * (length - len(vParts))
mParts += (0,) * (length - len(mParts))

retVal = (vParts, vSign) >= (mParts, mSign)

return retVal

Expand Down
5 changes: 4 additions & 1 deletion lib/core/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ def _serializeEncode(value):
name = "%s.%s" % (value.__class__.__module__, value.__class__.__name__)
if name in _SERIALIZE_CLASSES:
return {_SERIALIZE_TAG: "o", "c": name, "d": [[_serializeEncode(k), _serializeEncode(v)] for (k, v) in value.items()], "s": _serializeEncode(dict(value.__dict__))}
elif value.__class__ is dict:
elif value.__class__ is dict or (name or "").split(".")[0] not in ("lib", "plugins", "thirdparty"):
# a plain dict, or a foreign mapping subclass (e.g. collections.OrderedDict/defaultdict): store the
# items as a plain mapping so the data round-trips, instead of silently degrading to its text repr.
# A non-allowlisted lib/plugins/thirdparty subclass still falls through to _serializeUnknown (fail loudly)
return {_SERIALIZE_TAG: "m", "v": [[_serializeEncode(k), _serializeEncode(v)] for (k, v) in value.items()]}
else:
return _serializeUnknown(value, name)
Expand Down
9 changes: 7 additions & 2 deletions lib/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
_cache = {}
_method_locks = {}

# Private marker prefixing the slow-path (frozen) cache key so it can never collide with a raw
# fast-path key: without it e.g. args ([1,2],) freezes to ((1,2),), the exact fast key of ((1,2),),
# and the two calls would share a cache slot and return each other's result
_FROZEN_KEY_MARKER = object()

def cachedmethod(f):
"""
Method with a cached content
Expand Down Expand Up @@ -62,9 +67,9 @@ def _f(*args, **kwargs):
# Note: fallback (slow-path) for unhashable arguments
try:
if kwargs:
key = (_freeze(args), _freeze(kwargs))
key = (_FROZEN_KEY_MARKER, _freeze(args), _freeze(kwargs))
else:
key = _freeze(args)
key = (_FROZEN_KEY_MARKER, _freeze(args))

with lock:
if key in cache:
Expand Down
2 changes: 1 addition & 1 deletion lib/core/dicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@
DBMS.ACCESS: (ACCESS_ALIASES, "python-pyodbc", "https://github.com/mkleehammer/pyodbc", "access"),
DBMS.FIREBIRD: (FIREBIRD_ALIASES, "python3-firebirdsql", "https://github.com/nakagami/pyfirebirdsql/", "firebird"),
DBMS.MAXDB: (MAXDB_ALIASES, None, None, None), # 'maxdb'/'sapdb' SQLAlchemy dialect was removed long ago; a dead dialect only produces a misleading warning
DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "sybase"),
DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", None), # 'sybase' SQLAlchemy dialect was removed in 1.4 (external replacement abandoned); pymssql/dbwire cover '-d', so a dead dialect only wastes an attempt and shows a misleading warning
DBMS.DB2: (DB2_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"),
DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & https://github.com/jpype-project/jpype", None),
DBMS.H2: (H2_ALIASES, None, None, None),
Expand Down
11 changes: 9 additions & 2 deletions lib/core/dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
from lib.core.settings import IS_WIN
from lib.core.settings import METADB_SUFFIX
from lib.core.settings import MIN_BINARY_DISK_DUMP_SIZE
from lib.core.settings import SQLITE_INT_MAX
from lib.core.settings import SQLITE_INT_MIN
from lib.core.settings import TRIM_STDOUT_DUMP_SIZE
from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT
Expand Down Expand Up @@ -558,7 +560,10 @@ def dbTableValues(self, tableValues):
if not value or value == " ": # NULL
continue

int(value)
# Note: keep INTEGER only for values SQLite's affinity leaves untouched; leading zeros ('007'), signs ('+1') or 64-bit overflow would be silently rewritten on insert
parsed = int(value)
if str(parsed) != value or not (SQLITE_INT_MIN <= parsed <= SQLITE_INT_MAX):
raise ValueError
except ValueError:
colType = None
break
Expand All @@ -572,7 +577,9 @@ def dbTableValues(self, tableValues):
if not value or value == " ": # NULL
continue

float(value)
# Note: likewise REAL must round-trip textually ('2.00' or '1e5' would lose their exact form)
if repr(float(value)) != value:
raise ValueError
except ValueError:
colType = None
break
Expand Down
10 changes: 7 additions & 3 deletions lib/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from thirdparty import six

# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.10.7.118"
VERSION = "1.10.7.142"
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)
Expand Down Expand Up @@ -481,7 +481,7 @@
SESSION_SQLITE_FILE = "session.sqlite"

# Regular expressions used for finding file paths in error messages
FILE_PATH_REGEXES = (r"<b>(?P<result>[^<>]+?)</b> on line \d+", r"\bin (?P<result>[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s])(?P<result>[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s])(?P<result>/\w[/\w.~-]+)", r"\bhref=['\"]file://(?P<result>/[^'\"]+)", r"\bin <b>(?P<result>[^<]+): line \d+")
FILE_PATH_REGEXES = (r"<b>(?P<result>[^<>]+?)</b> on line \d+", r"\bin (?P<result>[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s'\"])(?P<result>[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s'\"])(?P<result>/\w[/\w.~-]+)", r"\bhref=['\"]file://(?P<result>/[^'\"]+)", r"\bin <b>(?P<result>[^<]+): line \d+")

# Regular expressions used for parsing error messages (--parse-errors)
ERROR_PARSING_REGEXES = (
Expand All @@ -501,7 +501,7 @@
)

# Regular expression used for parsing charset info from meta html headers
META_CHARSET_REGEX = r'(?si)<head>.*<meta[^>]+charset="?(?P<result>[^"> ]+).*</head>'
META_CHARSET_REGEX = r"""(?si)<head\b[^>]*>.*<meta[^>]+charset\s*=\s*["']?(?P<result>[^"'> ]+).*</head>"""

# Regular expression used for parsing refresh info from meta html headers
META_REFRESH_REGEX = r'(?i)<meta http-equiv="?refresh"?[^>]+content="?[^">]+;\s*(url=)?["\']?(?P<result>[^\'">]+)'
Expand Down Expand Up @@ -683,6 +683,10 @@
# Maximum integer value
MAX_INT = sys.maxsize

# Signed 64-bit range of SQLite's INTEGER storage class (used for safe --dump-format=SQLITE typing)
SQLITE_INT_MIN = -0x8000000000000000
SQLITE_INT_MAX = 0x7fffffffffffffff

# Replacement for unsafe characters in dump table filenames
UNSAFE_DUMP_FILEPATH_REPLACEMENT = '_'

Expand Down
5 changes: 4 additions & 1 deletion lib/request/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ def title(self):
if conf.cj:
if HTTP_HEADER.COOKIE in headers:
for cookie in conf.cj:
if cookie is None or cookie.domain_specified and not (conf.hostname or "").endswith(cookie.domain):
# Note: a domain-scoped cookie (Domain=example.com) is stored by the cookie jar as
# '.example.com', so a plain endswith() wrongly excludes the apex host itself
# ('example.com' does not end with '.example.com'); accept the exact domain too
if cookie is None or cookie.domain_specified and not ((conf.hostname or "").endswith(cookie.domain) or (conf.hostname or "") == cookie.domain.lstrip('.')):
continue

if ("%s=" % getUnicode(cookie.name)) in getUnicode(headers[HTTP_HEADER.COOKIE]):
Expand Down
Loading