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: 11 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,30 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: [ 'pypy-2.7', '3.8', '3.14' ]
exclude:
include:
- os: ubuntu-latest
python-version: "pypy-2.7"
- os: macos-latest
python-version: 'pypy-2.7'
python-version: "3.8"
- os: windows-latest
python-version: "3.14"

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Basic import test
run: python -c "import sqlmap; import sqlmapapi"

- name: Smoke test
run: python sqlmap.py --smoke

- name: Vuln test
run: python sqlmap.py --vuln
12 changes: 6 additions & 6 deletions data/txt/sha256sums.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ df768bcb9838dc6c46dab9b4a877056cb4742bd6cfaaf438c4a3712c5cc0d264 extra/shutils/
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/vulnserver/__init__.py
9e5e4d3d9acb767412259895a3ee75e1a5f42d0b9923f17605d771db384a6f60 extra/vulnserver/vulnserver.py
b8411d1035bb49b073476404e61e1be7f4c61e205057730e2f7880beadcd5f60 lib/controller/action.py
e376093d4f6e42ee38b050af329179df9c1c136b7667b2f1cb559f5d4b69ebd9 lib/controller/checks.py
ced1c82713afc1309c1495485b3d25a11c95af1f7460ea7922dbb96dacac37b4 lib/controller/checks.py
430475857a37fd997e73a47d7485c5dd4aa0985ef32c5a46b5e7bff01749ba66 lib/controller/controller.py
56e03690c1b783699c9f30cb2f8cc743d3716aba8137e6b253b21d1dd31a4314 lib/controller/handler.py
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/controller/__init__.py
Expand All @@ -181,17 +181,17 @@ a033f92d136c707a25927c2383125ddb004d4283db62c004dcd67c3fc242bb1c lib/core/dump.
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/core/__init__.py
914a13ee21fd610a6153a37cbe50830fcbd1324c7ebc1e7fc206d5e598b0f7ad lib/core/log.py
02a2264324caa249154e024a01bcd7cc40dbca4d647d5d10a50654b4415a6d77 lib/core/optiondict.py
c1cb56f2a43e9f2f6b25d5f3d504e856ea21df6fc14af5e37b1000feef2bdb5a lib/core/option.py
8d12a0acbc5e71a40fb19e65af49cd665b10aa313e1b81b336387edf8dd2f14b lib/core/option.py
9a213f91c8ad468466bd92e5e5805040f904055eb607fb2ed75b4c0e30b8accd lib/core/patch.py
49c0fa7e3814dfda610d665ee02b12df299b28bc0b6773815b4395514ddf8dec lib/core/profiling.py
03db48f02c3d07a047ddb8fe33a757b6238867352d8ddda2a83e4fec09a98d04 lib/core/readlineng.py
48797d6c34dd9bb8a53f7f3794c85f4288d82a9a1d6be7fcf317d388cb20d4b3 lib/core/replication.py
0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py
888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py
c811fffa88aa0deb40e6da2854b8705eec75ef6375a760535c71c21a8cde98f7 lib/core/settings.py
46ff4955bcc1d33301c67788c0aca4194e9d25dc1176cf70ccd7de7a010f400c lib/core/settings.py
cd5a66deee8963ba8e7e9af3dd36eb5e8127d4d68698811c29e789655f507f82 lib/core/shell.py
bcb5d8090d5e3e0ef2a586ba09ba80eef0c6d51feb0f611ed25299fbb254f725 lib/core/subprocessng.py
d35650179816193164a5f177102f18379dfbe6bb6d40fbb67b78d907b41c8038 lib/core/target.py
70ea3768f1b3062b22d20644df41c86238157ec80dd43da40545c620714273c6 lib/core/target.py
ddf8c5a3dbebd6cdf8b8ba4417e36652d1e040f025175cb6487f1aebc0208836 lib/core/testing.py
b5b65f018d6ef4b1ceeebbc50d372e07d4733267c9f3f4b13062efd065e847b6 lib/core/threads.py
b9aacb840310173202f79c2ba125b0243003ee6b44c92eca50424f2bdfc83c02 lib/core/unescaper.py
Expand All @@ -208,10 +208,10 @@ c5b258be7485089fac9d9cd179960e774fbd85e62836dc67cce76cc028bb6aeb lib/parse/hand
d2e771cdacef25ee3fdc0e0355b92e7cd1b68f5edc2756ffc19f75d183ba2c73 lib/parse/payloads.py
455ab0ec63e55cd56ce4a884b85bdc089223155008cab0f3696da5a33118f95b lib/parse/sitemap.py
1be3da334411657461421b8a26a0f2ff28e1af1e28f1e963c6c92768f9b0847c lib/request/basicauthhandler.py
a1c638493ecdc5194db7186bbfed815c6eed2344f2607cac8c9fa50534824266 lib/request/basic.py
1d5972aba14e4e340e3dde4f1d39a671020187fb759f435ba8b7f522dd4498fa lib/request/basic.py
bc61bc944b81a7670884f82231033a6ac703324b34b071c9834886a92e249d0e lib/request/chunkedhandler.py
2daf0ce19eacda64687f441c90ef8da51714c3e8947c993ba08fb4ecdc4f5287 lib/request/comparison.py
f83140c85be7f572f83c4ab4279fa1d8601243210cdfe4a44b2fc218befbcffd lib/request/connect.py
c7ab9699f30b67fdee3ddafdc215981da21aa6820d8dcd620f5c2ca82ddde2f4 lib/request/connect.py
8e06682280fce062eef6174351bfebcb6040e19976acff9dc7b3699779783498 lib/request/direct.py
cf019248253a5d7edb7bc474aa020b9e8625d73008a463c56ba2b539d7f2d8ec lib/request/dns.py
f56fc33251bd6214e3a6316c8f843eb192b2996aa84bd4c3e98790fdcf6e8cf0 lib/request/httpshandler.py
Expand Down
2 changes: 2 additions & 0 deletions lib/controller/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1377,13 +1377,15 @@ def checkWaf():
kb.choices.redirect = REDIRECTION.YES
kb.resendPostOnRedirect = False
conf.timeout = IPS_WAF_CHECK_TIMEOUT
kb.checkWafMode = True

try:
retVal = (Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, raise404=False, disableTampering=True)[1] or 0) < IPS_WAF_CHECK_RATIO
except SqlmapConnectionException:
retVal = True
finally:
kb.matchRatio = None
kb.checkWafMode = False

conf.timeout = popValue()
kb.resendPostOnRedirect = popValue()
Expand Down
5 changes: 4 additions & 1 deletion lib/core/option.py
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,7 @@ def _setSocketPreConnect():

def _thread():
while kb.get("threadContinue") and not conf.get("disablePrecon"):
done = False
try:
with kb.locks.socket:
keys = list(socket._ready.keys())
Expand All @@ -1076,6 +1077,7 @@ def _thread():
if q is not None and len(q) < SOCKET_PRE_CONNECT_QUEUE_SIZE:
q.append((s, time.time()))
s = None
done = True

if s is not None:
try:
Expand All @@ -1088,7 +1090,7 @@ def _thread():
except:
pass
finally:
time.sleep(0.01)
time.sleep(0.01 if not done else 0.001)

def create_connection(*args, **kwargs):
retVal = None
Expand Down Expand Up @@ -2086,6 +2088,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR)
kb.chars.at, kb.chars.space, kb.chars.dollar, kb.chars.hash_ = ("%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, _, KB_CHARS_BOUNDARY_CHAR) for _ in randomStr(length=4, lowercase=True))

kb.checkWafMode = False
kb.choices = AttribDict(keycheck=False)
kb.codePage = None
kb.commonOutputs = None
Expand Down
9 changes: 6 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.2.6"
VERSION = "1.10.2.12"
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 @@ -122,7 +122,10 @@
PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP", "BaseHTTP")

# Identify WAF/IPS inside limited number of responses (Note: for optimization purposes)
IDENTYWAF_PARSE_LIMIT = 10
IDENTYWAF_PARSE_COUNT_LIMIT = 10

# Identify WAF/IPS inside limited size of responses
IDENTYWAF_PARSE_PAGE_LIMIT = 4 * 1024

# Maximum sleep time in "Murphy" (testing) mode
MAX_MURPHY_SLEEP_TIME = 3
Expand Down Expand Up @@ -779,7 +782,7 @@
# For preventing MemoryError exceptions (caused when using large sequences in difflib.SequenceMatcher)
MAX_DIFFLIB_SEQUENCE_LENGTH = 10 * 1024 * 1024

# Page size threshold used in heuristic checks (e.g. getHeuristicCharEncoding(), identYwaf, htmlParser, etc.)
# Page size threshold used in heuristic checks (e.g. getHeuristicCharEncoding(), htmlParser, etc.)
HEURISTIC_PAGE_SIZE_THRESHOLD = 64 * 1024

# Maximum (multi-threaded) length of entry in bisection algorithm
Expand Down
8 changes: 8 additions & 0 deletions lib/core/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,14 @@ def _setHashDB():
errMsg = "unable to flush the session file ('%s')" % getSafeExString(ex)
raise SqlmapFilePathException(errMsg)

for suffix in ("-shm", "-wal"):
leftover = conf.hashDBFile + suffix
if os.path.exists(leftover):
try:
os.remove(leftover)
except OSError:
pass

conf.hashDB = HashDB(conf.hashDBFile)

def _resumeHashDBValues():
Expand Down
7 changes: 4 additions & 3 deletions lib/request/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
from lib.core.settings import DEFAULT_COOKIE_DELIMITER
from lib.core.settings import EVENTVALIDATION_REGEX
from lib.core.settings import HEURISTIC_PAGE_SIZE_THRESHOLD
from lib.core.settings import IDENTYWAF_PARSE_LIMIT
from lib.core.settings import IDENTYWAF_PARSE_COUNT_LIMIT
from lib.core.settings import IDENTYWAF_PARSE_PAGE_LIMIT
from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE
from lib.core.settings import META_CHARSET_REGEX
from lib.core.settings import PARSE_HEADERS_LIMIT
Expand Down Expand Up @@ -395,8 +396,8 @@ def processResponse(page, responseHeaders, code=None, status=None):
if msg:
logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.'))

if not conf.skipWaf and kb.processResponseCounter < IDENTYWAF_PARSE_LIMIT:
rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(getUnicode(responseHeaders.headers if responseHeaders else [])), page[:HEURISTIC_PAGE_SIZE_THRESHOLD])
if not conf.skipWaf and kb.processResponseCounter < IDENTYWAF_PARSE_COUNT_LIMIT:
rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(getUnicode(responseHeaders.headers if responseHeaders else [])), page[:IDENTYWAF_PARSE_PAGE_LIMIT] if not kb.checkWafMode else page[:HEURISTIC_PAGE_SIZE_THRESHOLD])

with kb.locks.identYwaf:
identYwaf.non_blind.clear()
Expand Down
17 changes: 10 additions & 7 deletions lib/request/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,17 +227,18 @@ def _retryProxy(**kwargs):

@staticmethod
def _connReadProxy(conn):
retVal = b""
parts = []

if not kb.dnsMode and conn:
headers = conn.info()
if kb.pageCompress and headers and hasattr(headers, "getheader") and (headers.getheader(HTTP_HEADER.CONTENT_ENCODING, "").lower() in ("gzip", "deflate") or "text" not in headers.getheader(HTTP_HEADER.CONTENT_TYPE, "").lower()):
retVal = conn.read(MAX_CONNECTION_TOTAL_SIZE)
if len(retVal) == MAX_CONNECTION_TOTAL_SIZE:
part = conn.read(MAX_CONNECTION_TOTAL_SIZE)
if len(part) == MAX_CONNECTION_TOTAL_SIZE:
warnMsg = "large compressed response detected. Disabling compression"
singleTimeWarnMessage(warnMsg)
kb.pageCompress = False
raise SqlmapCompressionException
parts.append(part)
else:
while True:
if not conn:
Expand All @@ -252,18 +253,20 @@ def _connReadProxy(conn):
warnMsg = "large response detected. This could take a while"
singleTimeWarnMessage(warnMsg)
part = re.sub(getBytes(r"(?si)%s.+?%s" % (kb.chars.stop, kb.chars.start)), getBytes("%s%s%s" % (kb.chars.stop, LARGE_READ_TRIM_MARKER, kb.chars.start)), part)
retVal += part
parts.append(part)
else:
retVal += part
parts.append(part)
break

if len(retVal) > MAX_CONNECTION_TOTAL_SIZE:
if sum(len(_) for _ in parts) > MAX_CONNECTION_TOTAL_SIZE:
warnMsg = "too large response detected. Automatically trimming it"
singleTimeWarnMessage(warnMsg)
break

if conf.yuge:
retVal = YUGE_FACTOR * retVal
parts = YUGE_FACTOR * parts

retVal = b"".join(parts)

return retVal

Expand Down