Skip to content

Commit 023d85f

Browse files
committed
Minor update
1 parent 2afa924 commit 023d85f

2 files changed

Lines changed: 27 additions & 15 deletions

File tree

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.7.239"
23+
VERSION = "1.10.7.240"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

tests/test_jitter_stress.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@
66
77
Adversarial JITTER stress harness for time-based blind extraction.
88
9-
Drives the REAL bisection() + REAL wasLastResponseDelayed() against a mock oracle that returns a
10-
simulated RESPONSE DURATION (base + jitter + timeSec-if-condition-true) instead of a boolean - so
11-
the actual statistical delay-decision and its re-validation run under controlled network jitter,
12-
with NO real sleeping (thousands of extractions per second, fully deterministic per seed).
9+
Drives the REAL bisection() + REAL wasLastResponseDelayed() + REAL validateChar() re-validation
10+
against a mock oracle that returns a simulated RESPONSE DURATION (base + jitter + timeSec-if-condition-
11+
true) instead of a boolean - so the whole time-based decision stack runs under controlled network
12+
jitter, with NO real sleeping (thousands of extractions per second, fully deterministic per seed).
13+
The delimiter-wrapped template is what lets validateChar's per-char '!=' re-check actually fire (it is
14+
sqlmap's main defense against a single spike faking one bit); without it the harness is far too harsh.
1315
1416
Two tiers:
15-
* TestJitterRegression - ALWAYS runs. Low/mild jitter MUST extract perfectly. A real regression
16-
guard for the time-based decision stack (deterministic, fast, non-flaky).
17-
* TestJitterStressSweep - OPT-IN (set env SQLMAP_JITTER_STRESS=1). The adversarial sweeps that map
18-
the failure surface (Gaussian sigma, heavy-tailed spikes). Informational
19-
+ loose bounds only; kept out of normal CI to avoid slowness/flakiness.
17+
* TestJitterRegression - ALWAYS runs. Low/mild jitter MUST extract perfectly, and a spike in the
18+
baseline model MUST NOT hide genuine delays. Deterministic, fast, non-flaky.
19+
* TestJitterStressSweep - OPT-IN (set env SQLMAP_JITTER_STRESS=1). Adversarial sweeps (Gaussian
20+
sigma, heavy-tailed spikes) mapping where extraction finally degrades.
21+
Informational + loose bounds only; kept out of normal CI (slow/noisy).
2022
2123
Run the sweep on demand: SQLMAP_JITTER_STRESS=1 python -m unittest tests.test_jitter_stress -v
2224
"""
@@ -35,11 +37,15 @@
3537
from lib.core.common import getCurrentThreadData, setTechnique
3638
from lib.core.datatype import AttribDict
3739
from lib.core.enums import ADJUST_TIME_DELAY, PAYLOAD
40+
from lib.core.settings import PAYLOAD_DELIMITER
3841
from lib.request.connect import Connect
3942
import lib.techniques.blind.inference as inf
4043

41-
_TEMPLATE = "EXPR=%s IDX=%d CMP>%d"
42-
_PARSE = re.compile(r"IDX=(\d+) CMP(.)(\d+)")
44+
# The comparison must sit BETWEEN PAYLOAD_DELIMITERs: validateChar (inference.py) rewrites '>' to '!='
45+
# with a regex anchored on the delimiters, and without them that per-char re-validation silently
46+
# no-ops (defeating sqlmap's main per-request-spike defense and making this harness far too pessimistic).
47+
_TEMPLATE = "%sEXPR=%%s IDX=%%d CMP>%%d%s" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER)
48+
_PARSE = re.compile(r"IDX=(\d+) CMP(!=|=|>)(\d+)") # bisection '>'/'=' plus validateChar's '!='
4349
_TIMESEC = 5.0
4450
_BASE = 0.10 # base (non-delay) round-trip latency, seconds
4551
_STRESS = os.environ.get("SQLMAP_JITTER_STRESS")
@@ -107,7 +113,7 @@ def oracle(payload=None, timeBasedCompare=False, **kwargs):
107113
return False
108114
idx, op, thr = int(m.group(1)), m.group(2), int(m.group(3))
109115
ch = ord(secret[idx - 1]) if 0 <= idx - 1 < len(secret) else 0
110-
cond = (ch > thr) if op == ">" else (ch == thr)
116+
cond = (ch > thr) if op == ">" else (ch != thr) if op == "!=" else (ch == thr)
111117
if "NOT(" in payload:
112118
cond = not cond
113119
td.lastQueryDuration = _BASE + abs(jitter(rng)) + (_TIMESEC if cond else 0.0)
@@ -183,11 +189,14 @@ def test_baseline_spike_does_not_hide_a_genuine_delay(self):
183189
@unittest.skipUnless(_STRESS, "adversarial jitter sweep is opt-in (set SQLMAP_JITTER_STRESS=1)")
184190
class TestJitterStressSweep(_JitterBase):
185191
"""Opt-in failure-surface map. Prints correctness vs jitter and asserts only loose, non-flaky
186-
invariants (clean case perfect, degradation is monotone-ish). Use to evaluate hardening changes."""
192+
invariants (clean case perfect). Use to evaluate hardening changes."""
187193

188194
SECRET = "Str0ng!"
189195

190196
def test_gaussian_sweep(self):
197+
# Continuous jitter: degrades only once sigma approaches timeSec/7 (7*stdev threshold nears the
198+
# real delay). That is the FUNDAMENTAL limit of the statistic - the answer there is a larger
199+
# timeSec (--time-sec), not a code change; shown here so a regression that degrades it earlier is visible.
191200
print("\n[jitter] Gaussian sigma sweep (timeSec=%.0f, base=%.2f):" % (_TIMESEC, _BASE))
192201
for sigma in (0.0, 0.3, 0.5, 0.7, 0.9, 1.2):
193202
ok, n = self._rate(self.SECRET, _gaussian(sigma))
@@ -196,8 +205,11 @@ def test_gaussian_sweep(self):
196205
self.assertEqual(ok, n)
197206

198207
def test_heavy_tailed_spike_sweep(self):
208+
# One-off +8s spikes: baseline-trim (stripTimeOutliers) keeps the model clean and validateChar's
209+
# '!=' re-check catches a spike that fakes a single bit, so extraction stays ~perfect until an
210+
# absurd spike rate (a fifth of all requests). This is the payoff of both defenses together.
199211
print("\n[jitter] Heavy-tailed spike sweep (base sigma=0.2, spike=+8s):")
200-
for p in (0.0, 0.01, 0.03, 0.05, 0.10):
212+
for p in (0.0, 0.01, 0.03, 0.05, 0.10, 0.20):
201213
ok, n = self._rate(self.SECRET, _spike(0.2, p, 8.0))
202214
print(" spike_p=%.2f -> %d/%d (%3.0f%%)" % (p, ok, n, 100.0 * ok / n))
203215
if p == 0.0:

0 commit comments

Comments
 (0)