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
11 changes: 10 additions & 1 deletion 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.102"
VERSION = "1.10.7.105"
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 @@ -519,6 +519,10 @@
# Reference: http://www.the-interweb.com/serendipity/index.php?/archives/94-A-brief-analysis-of-40,000-leaked-MySpace-passwords.html
COMMON_PASSWORD_SUFFIXES += ("!", ".", "*", "!!", "?", ";", "..", "!!!", ",", "@")

# Most common passwords (frequency-ordered) tried for very slow, per-hash-salted algorithms (bcrypt) where a
# full dictionary is impractical; kept small on purpose so a candidate-major attack stays within a time budget
COMMON_PASSWORDS = ("123456", "123456789", "12345678", "password", "qwerty", "12345", "123123", "111111", "1234567890", "1234567", "qwerty123", "000000", "1q2w3e", "abc123", "password1", "1234", "qwertyuiop", "123321", "password123", "1q2w3e4r5t", "iloveyou", "654321", "666666", "987654321", "1q2w3e4r", "7777777", "dragon", "1qaz2wsx", "123qwe", "monkey", "123456a", "112233", "qwe123", "159753", "letmein", "11111111", "222222", "123abc", "qazwsx", "555555", "princess", "admin", "121212", "1234qwer", "sunshine", "football", "aaaaaa", "123123123", "computer", "michael", "superman", "welcome", "zxcvbnm", "asdfghjkl", "1111", "shadow", "master", "999999", "88888888", "secret", "qwerty1", "12341234", "101010", "1111111", "asdfgh", "147258369", "qwertyui", "123654", "google", "123456789a", "ashley", "jesus", "ninja", "mustang", "baseball", "jennifer", "hunter", "soccer", "batman", "andrew", "tigger", "charlie", "robert", "thomas", "hockey", "ranger", "daniel", "hannah", "maggie", "696969", "harley", "1234abcd", "trustno1", "buster", "starwars", "freedom", "whatever", "qazwsxedc", "passw0rd")

# Splitter used between requests in WebScarab log files
WEBSCARAB_SPLITTER = "### Conversation"

Expand Down Expand Up @@ -882,6 +886,11 @@
# Give up on hash recognition if nothing was found in first given number of rows
HASH_RECOGNITION_QUIT_THRESHOLD = 1000

# Wall-clock budget (in seconds) for the pure-Python cracking of very slow, per-hash-salted algorithms
# (bcrypt); candidate-major so the most common passwords are tried against every hash first, then it stops
# and the remainder is left for a dedicated tool (e.g. 'hashcat'). Overridable via 'SQLMAP_HASH_ATTACK_TIME_LIMIT'
HASH_ATTACK_TIME_LIMIT = 300

# Regular expression used for automatic hex conversion and hash cracking of (RAW) binary column values
HASH_BINARY_COLUMNS_REGEX = r"(?i)pass|psw|hash"

Expand Down
23 changes: 18 additions & 5 deletions lib/request/http2.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
# HTTP/2 frame codec (RFC 7540 section 4.1) - the zero-table-risk brick. Pure stdlib, py2/py3, ASCII.

# frame types (RFC 7540 s6)
DATA, HEADERS, RST_STREAM, SETTINGS, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION = 0x0, 0x1, 0x3, 0x4, 0x6, 0x7, 0x8, 0x9
DATA, HEADERS, RST_STREAM, SETTINGS, PUSH_PROMISE, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION = 0x0, 0x1, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9
# flags
FLAG_END_STREAM = 0x1
FLAG_ACK = 0x1
Expand Down Expand Up @@ -374,6 +374,7 @@ def encode(self, headers):
out += encode_string(value)
return bytes(out)

SETTINGS_ENABLE_PUSH = 0x2
SETTINGS_INITIAL_WINDOW_SIZE = 0x4
BIG_WINDOW = (1 << 31) - 1

Expand Down Expand Up @@ -469,9 +470,12 @@ def __init__(self, host, port, proxy, timeout):
if self.sock.selected_alpn_protocol() != "h2":
raise IOError("server did not negotiate h2 (ALPN=%r)" % self.sock.selected_alpn_protocol())
self.sock.settimeout(timeout)
# connection preface + client SETTINGS (advertise a large per-stream window) + bump conn window
# connection preface + client SETTINGS (disable server push + advertise a large per-stream window)
# + bump conn window. ENABLE_PUSH=0 keeps servers from opening pushed streams whose HPACK header
# block we would otherwise have to decode to keep the dynamic table in sync (skipping it desyncs
# the decoder and corrupts every later header) - this client has no use for pushed responses.
self.sock.sendall(CONNECTION_PREFACE)
self.sock.sendall(encode_frame(SETTINGS, 0, 0, struct.pack("!HI", SETTINGS_INITIAL_WINDOW_SIZE, BIG_WINDOW)))
self.sock.sendall(encode_frame(SETTINGS, 0, 0, struct.pack("!HIHI", SETTINGS_ENABLE_PUSH, 0, SETTINGS_INITIAL_WINDOW_SIZE, BIG_WINDOW)))
self.sock.sendall(encode_frame(WINDOW_UPDATE, 0, 0, struct.pack("!I", BIG_WINDOW - 65535)))
except Exception:
self.close()
Expand Down Expand Up @@ -514,6 +518,9 @@ def exchange(self, method, path, authority, headers, body, timeout):
elif ftype == PING:
if not (flags & FLAG_ACK):
self.sock.sendall(encode_frame(PING, FLAG_ACK, 0, payload))
elif ftype == PUSH_PROMISE:
self.usable = False # we advertised ENABLE_PUSH=0; a push would desync HPACK
raise _UnprocessedStream("unexpected PUSH_PROMISE despite SETTINGS_ENABLE_PUSH=0")
elif ftype == GOAWAY:
self.usable = False # server won't accept new streams -> retire connection
last_sid = (struct.unpack("!I", payload[4:8])[0] & 0x7fffffff) if len(payload) >= 8 else 0
Expand Down Expand Up @@ -601,12 +608,18 @@ def exchange_pair(self, requests, timeout):
elif ftype == PING:
if not (flags & FLAG_ACK):
self.sock.sendall(encode_frame(PING, FLAG_ACK, 0, payload))
elif ftype == PUSH_PROMISE:
self.usable = False # we advertised ENABLE_PUSH=0; a push would desync HPACK
raise _UnprocessedStream("unexpected PUSH_PROMISE despite SETTINGS_ENABLE_PUSH=0")
elif ftype == GOAWAY:
# Routine on a long-lived connection (server retires it after its per-connection request cap).
# The pair did not complete cleanly, so it must be re-sent on a fresh connection; flag it
# retry-safe (idempotent boolean read) rather than crashing the extraction.
self.usable = False
raise IOError("GOAWAY during timeless pair")
raise _UnprocessedStream("GOAWAY during timeless pair")
elif ftype == RST_STREAM and fsid in state:
self.usable = False
raise IOError("stream reset during timeless pair")
raise _UnprocessedStream("stream reset during timeless pair")
elif ftype in (HEADERS, CONTINUATION) and fsid in state:
p = payload
if ftype == HEADERS:
Expand Down
60 changes: 44 additions & 16 deletions lib/request/timeless.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# front-proxy makes order track arrival, not work. calibrate() detects that (and estimates the readable
# delta) so the caller never applies the oracle blind - it falls back to classic time-based instead.

import socket
import ssl
import threading

from lib.core.enums import DBMS
Expand Down Expand Up @@ -49,18 +51,32 @@ def buildConditionPair(condition, heavy, cheap="0"):
return condExpr, negExpr


def _pairOrder(conn, reqA, reqB, timeout):
def _pairOrder(connSource, reqA, reqB, timeout, retries=2):
"""Send reqA and reqB as one coalesced pair; return the stream id that finished FIRST plus the two
stream ids in send order (reqA got the lower id)."""
order, _results = conn.exchange_pair([reqA, reqB], timeout)
loSid = conn.next_sid - 4
hiSid = conn.next_sid - 2
return order[0], loSid, hiSid
stream ids in send order (reqA got the lower id).

`connSource` is either a live _H2Connection or a zero-arg callable returning one. The callable form
lets a dropped connection be replaced transparently and the pair re-sent: a long extraction routinely
outlives a single HTTP/2 connection (the server retires it with GOAWAY after its per-connection request
cap), and a coalesced boolean-read pair is idempotent, so re-sending it on a fresh connection is safe.
A raw connection (used by calibration and the self-test) is not retried - it simply raises."""
attempt = 0
while True:
conn = connSource() if callable(connSource) else connSource
try:
order, _results = conn.exchange_pair([reqA, reqB], timeout)
return order[0], conn.next_sid - 4, conn.next_sid - 2
except (socket.error, ssl.SSLError, IOError):
conn.close() # retire; a callable source reopens on the next pass
attempt += 1
if not callable(connSource) or attempt > retries:
raise


def readBit(conn, reqCond, reqNeg, votes=5, timeout=30):
def readBit(connSource, reqCond, reqNeg, votes=5, timeout=30):
"""Read one boolean by the cond-last FRACTION over symmetric pairs, ESCALATING when the fraction is
ambiguous (load-degraded).
ambiguous (load-degraded). `connSource` is a live connection or a factory (see _pairOrder) so a
connection dropped mid-vote (e.g. server GOAWAY) is replaced and that pair re-sent transparently.

reqCond does the heavy work iff the guessed condition is TRUE; reqNeg does the SAME heavy work iff the
condition is FALSE (it carries the negated condition). Exactly one runs heavy, so whichever finishes
Expand All @@ -83,10 +99,10 @@ def readBit(conn, reqCond, reqNeg, votes=5, timeout=30):
cap = votes * 5 # escalation ceiling for ambiguous (load-degraded) bits
while True:
if i % 2 == 0:
first, loSid, _hiSid = _pairOrder(conn, reqCond, reqNeg, timeout)
first, loSid, _hiSid = _pairOrder(connSource, reqCond, reqNeg, timeout)
condSid = loSid
else:
first, _loSid, hiSid = _pairOrder(conn, reqNeg, reqCond, timeout)
first, _loSid, hiSid = _pairOrder(connSource, reqNeg, reqCond, timeout)
condSid = hiSid
if first != condSid: # reqCond finished last -> it ran heavy -> vote says TRUE
condLast += 1
Expand Down Expand Up @@ -159,6 +175,16 @@ def __init__(self, host, port, refReq, proxy=None, asymVotes=12, votes=5, timeou
def _conn(self):
conn = getattr(self._local, "conn", None)
if conn is None or not conn.usable:
if conn is not None: # retire the dead one so reconnects don't leak sockets
try:
conn.close()
except Exception:
pass
with self._lock:
try:
self._conns.remove(conn)
except ValueError:
pass
conn = self._local.conn = connect(self.host, self.port, self.proxy, self.timeout)
with self._lock:
self._conns.append(conn)
Expand All @@ -172,9 +198,11 @@ def readBitFromSpecs(self, condSpec, negSpec=None):
be used for a heavy-base condition (the trivial-base reference is not comparable). Specs are the
(url, method, headers, post) tuples from getPage(buildOnly=True)."""
reqCond = _specToReq(condSpec, self.host)
# Pass the bound _conn factory (not a resolved connection) so a pair whose connection is retired
# mid-read (server GOAWAY on a long dump) is transparently re-sent on a fresh one.
if negSpec is not None:
return readBit(self._conn(), reqCond, _specToReq(negSpec, self.host), votes=self.votes, timeout=self.timeout)
return readBitAsymmetric(self._conn(), reqCond, self.refReq, self.asymVotes, self.timeout)
return readBit(self._conn, reqCond, _specToReq(negSpec, self.host), votes=self.votes, timeout=self.timeout)
return readBitAsymmetric(self._conn, reqCond, self.refReq, self.asymVotes, self.timeout)

def close(self):
with self._lock:
Expand Down Expand Up @@ -655,7 +683,7 @@ def readBitLive(conn, condition, vector=None, votes=1, timeout=30):
return readBit(conn, reqCond, reqNeg, votes=votes, timeout=timeout)


def readBitAsymmetric(conn, reqCond, reqRef, votes=12, timeout=30):
def readBitAsymmetric(connSource, reqCond, reqRef, votes=12, timeout=30):
"""Read one boolean WITHOUT the negated comparison - only the condition request and a FIXED always-heavy
reference. When the condition is TRUE, reqCond runs the SAME heavy work as reqRef, so they race ~50/50
and reqCond finishes last about HALF the votes; when FALSE - or when the comparison is NULL / the DBMS
Expand All @@ -668,10 +696,10 @@ def readBitAsymmetric(conn, reqCond, reqRef, votes=12, timeout=30):
condLast = 0
for i in range(votes):
if i % 2 == 0:
order, _ = conn.exchange_pair([reqCond, reqRef], timeout); condSid = conn.next_sid - 4
first, condSid, _hiSid = _pairOrder(connSource, reqCond, reqRef, timeout)
else:
order, _ = conn.exchange_pair([reqRef, reqCond], timeout); condSid = conn.next_sid - 2
if order[0] != condSid: # cond finished last -> it ran the heavy branch this vote
first, _loSid, condSid = _pairOrder(connSource, reqRef, reqCond, timeout)
if first != condSid: # cond finished last -> it ran the heavy branch this vote
condLast += 1
return condLast * 4 >= votes # >= 25% cond-last -> TRUE (mid-way between ~50% and ~0%)

Expand Down
Loading