blitzstrike 2.4.151 → 2.4.153

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,29 @@ 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.153] — 2026-10-11
9
+
10
+ ### Fixed — blind_oracle/confirm trailing-slash redirect false-negative
11
+
12
+ - Real-target bug: `baseUrl.replace(/\/+$/,"")` stripped the trailing slash, so a
13
+ path-based endpoint (e.g. /Less-8) hit a 301 redirect and the probe hashed the
14
+ redirect body instead of the real response — causing false negatives that a
15
+ flat mock lab never exposed. Now the base keeps a trailing slash and fetches
16
+ follow redirects. Verified against sqli-labs Less-8 (boolean blind) and Less-9
17
+ (time blind): blind_oracle flags sql context, blind_sqli_confirm reports
18
+ CONFIRMED via boolean (706b vs 722b) + time (~5001ms vs ~2ms) differentials.
19
+
20
+ ## [2.4.152] — 2026-10-11
21
+
22
+ ### Added — autonomous blind SQLi confirmation (blind_sqli_confirm)
23
+
24
+ - New `blind_sqli_confirm` tool + `confirmBlindSqli()`: turns blind_oracle's
25
+ "SQL context" hint into "blind SQLi CONFIRMED" — sends the boolean oracle
26
+ (TRUE vs FALSE byte-count differential) and time oracle (TRUE-branch sleep vs
27
+ FALSE-branch instant) with payloads taken from the blind_sqli playbook (not
28
+ hand-written), and returns a deterministic verdict + evidence string.
29
+ 166 tools, 548/548 tests.
30
+
8
31
  ## [2.4.151] — 2026-10-11
9
32
 
10
33
  ### Added — time-based blind SQLi (per-DBMS, detection + extraction)
package/dist/index.js CHANGED
@@ -62357,7 +62357,7 @@ function classifyBlindProbe(input) {
62357
62357
  return { same_as_baseline: same, signal, reason };
62358
62358
  }
62359
62359
  async function blindOracle(baseUrl, param, value, opts = {}) {
62360
- const base = baseUrl.replace(/\/+$/, "");
62360
+ const base = baseUrl.replace(/\/+$/, "") + "/";
62361
62361
  const sinks = opts.sinks ?? ["sql", "ssti", "xss"];
62362
62362
  const timeDbms = opts.timeDbms ?? "mysql";
62363
62363
  const marker = `zz${Math.random().toString(36).slice(2, 10)}zz`;
@@ -62368,7 +62368,7 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62368
62368
  let baselineSize = 0;
62369
62369
  let baselineStatus = 0;
62370
62370
  try {
62371
- const r = await fetch(build(value), { headers, redirect: "manual", signal: AbortSignal.timeout(timeoutMs) });
62371
+ const r = await fetch(build(value), { headers, redirect: "follow", signal: AbortSignal.timeout(timeoutMs) });
62372
62372
  baselineStatus = r.status;
62373
62373
  const body = await r.text();
62374
62374
  baselineHash = sha256(body);
@@ -62389,7 +62389,7 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62389
62389
  for (const p of probes) {
62390
62390
  const t0 = Date.now();
62391
62391
  try {
62392
- const r = await fetch(build(p.payload), { headers, redirect: "manual", signal: AbortSignal.timeout(timeoutMs) });
62392
+ const r = await fetch(build(p.payload), { headers, redirect: "follow", signal: AbortSignal.timeout(timeoutMs) });
62393
62393
  const timing = Date.now() - t0;
62394
62394
  const status = r.status;
62395
62395
  const body = await r.text();
@@ -62443,6 +62443,46 @@ 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
+ 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: "follow", 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
+ }
62446
62486
 
62447
62487
  // src/param-precedence.ts
62448
62488
  function generatePrecedenceVariants(param, a, b) {
@@ -65125,6 +65165,18 @@ ${statusLine}` : statusLine },
65125
65165
  }
65126
65166
  return { content: [{ type: "text", text: JSON.stringify(await blindOracle(base_url, param, value, { sinks: s, timeDbms: time_dbms })) }] };
65127
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")) }] };
65179
+ });
65128
65180
  server.registerTool("oob_start", {
65129
65181
  title: "Out-of-band callback listener (blind SSRF/XXE/SQLi/RCE proof)",
65130
65182
  description: "Start a deterministic out-of-band (OOB) listener to PROVE a blind vuln (SSRF/XXE/SQLi/command injection) that does not reflect in the HTTP response. Uses interactsh-client when installed (unique *.oast.* domain, streams DNS/HTTP interactions) — the standard for public targets; falls back to a local HTTP listener for local labs. Returns the callback URL/domain + payload templates. Pair oob_start -> inject the canary -> oob_poll(id) until the hit arrives -> oob_stop(id). A hit is reported ONLY when an interaction actually arrives for the session's unique id — never a guess.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "2.4.151",
3
+ "version": "2.4.153",
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": {