Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/worktrees/musing-swirles
Submodule musing-swirles added at 7266fa
37 changes: 37 additions & 0 deletions software/open-wrt/99-wan-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/sh

# 1. OpenWrt Default-Müll rigoros killen!
uci delete network.lan
uci delete network.wan
uci delete network.wan6

# 2. WAN sauber neu aufbauen (eth0)
uci set network.wan=interface
uci set network.wan.device='eth0'
uci set network.wan.proto='static'
uci set network.wan.ipaddr='172.19.0.2'
uci set network.wan.netmask='255.255.255.0'

# 3. LAN sauber neu aufbauen (eth1)
uci set network.lan=interface
uci set network.lan.device='eth1'
uci set network.lan.proto='static'
uci set network.lan.ipaddr='172.18.0.254'
uci set network.lan.netmask='255.255.255.0'

# 4. QEMU SSH-Interface (eth2)
uci set network.qemu=interface
uci set network.qemu.device='eth2'
uci set network.qemu.proto='dhcp'

uci commit network

# Firewall für SSH und Durchleitung öffnen
uci add_list firewall.@zone[0].network='qemu'
uci set firewall.@zone[0].input='ACCEPT'
uci set firewall.@zone[0].forward='ACCEPT'
uci set firewall.@zone[1].input='ACCEPT'
uci set firewall.@zone[1].forward='ACCEPT'
uci commit firewall

(echo "password"; echo "password") | passwd root
43 changes: 43 additions & 0 deletions software/open-wrt/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y \
wget \
iproute2 \
e2tools \
python3 \
&& ARCH=$(dpkg --print-architecture) \
&& if [ "$ARCH" = "arm64" ]; then \
apt-get install -y qemu-system-arm qemu-efi-aarch64; \
else \
apt-get install -y qemu-system-x86; \
fi \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /openwrt

RUN ARCH=$(dpkg --print-architecture) \
&& if [ "$ARCH" = "arm64" ]; then \
wget -q https://downloads.openwrt.org/releases/24.10.5/targets/armsr/armv8/openwrt-24.10.5-armsr-armv8-generic-ext4-combined-efi.img.gz \
&& gunzip openwrt-24.10.5-armsr-armv8-generic-ext4-combined-efi.img.gz; \
EC=$?; [ $EC -eq 0 ] || [ $EC -eq 2 ] \
&& mv openwrt-24.10.5-armsr-armv8-generic-ext4-combined-efi.img openwrt.img; \
else \
wget -q https://downloads.openwrt.org/releases/24.10.5/targets/x86/64/openwrt-24.10.5-x86-64-generic-ext4-combined.img.gz \
&& gunzip openwrt-24.10.5-x86-64-generic-ext4-combined.img.gz; \
EC=$?; [ $EC -eq 0 ] || [ $EC -eq 2 ] \
&& mv openwrt-24.10.5-x86-64-generic-ext4-combined.img openwrt.img; \
fi

COPY 99-wan-config /tmp/99-wan-config

RUN ARCH=$(dpkg --print-architecture) \
&& if [ "$ARCH" = "arm64" ]; then SKIP=262656; else SKIP=33792; fi \
&& dd if=openwrt.img of=partition2.img bs=512 skip=$SKIP 2>/dev/null \
&& printf 'cd /etc/uci-defaults\nwrite /tmp/99-wan-config 99-wan-config\n' | debugfs -w partition2.img \
&& dd if=partition2.img of=openwrt.img bs=512 seek=$SKIP conv=notrunc 2>/dev/null \
&& rm partition2.img

COPY start.sh start.sh
RUN chmod +x start.sh

CMD ["/openwrt/start.sh"]
153 changes: 153 additions & 0 deletions software/open-wrt/ctf-router.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/bin/bash
# Startet/stoppt die CTF Router Challenge.
# Der OpenWrt-Router wird zwischen Angreifer und ICS-Netz gehängt.
# Usage: ./ctf-router.sh start|stop|status

set -uo pipefail

# --- Config ---
ROUTER_DIR="$(cd "$(dirname "$0")" && pwd)"
ROUTER_CONTAINER="open-wrt-openwrt-1"
INT_NET="virtual_virt-cybics"
EXT_NET="ext_ctf"
EXT_SUBNET="172.22.0.0/24"
EXT_GATEWAY="172.22.0.1"
ROUTER_INT_IP="172.18.0.50"
ROUTER_EXT_IP="172.22.0.2"
ATTACK_EXT_IP="172.22.0.100"
ATTACK_CONTAINER="attack-machine"

find_container() {
docker ps --format '{{.Names}}' | grep -i "$1" | head -1
}

container_in_network() {
docker inspect "$1" --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' 2>/dev/null | grep -q "$2"
}

# --- START ---
do_start() {
echo "Starting CTF router challenge..."

# CybICS muss laufen
if ! docker network ls --format '{{.Name}}' | grep -q "$INT_NET"; then
echo "ERROR: $INT_NET not found. Start CybICS first."
exit 1
fi

ATTACK=$(find_container "$ATTACK_CONTAINER")
[ -n "$ATTACK" ] && echo "Found attack machine: $ATTACK"

# ext_ctf Netz anlegen
if ! docker network ls --format '{{.Name}}' | grep -q "^${EXT_NET}$"; then
docker network create --driver bridge --subnet "$EXT_SUBNET" --gateway "$EXT_GATEWAY" "$EXT_NET" \
|| { echo "ERROR: could not create $EXT_NET"; exit 1; }
fi

# Router hochfahren
(cd "$ROUTER_DIR" && docker compose up -d --build)
sleep 3
if ! docker ps --format '{{.Names}}' | grep -q "$ROUTER_CONTAINER"; then
echo "ERROR: router container not running"
exit 1
fi

# Router in beide Netze hängen
container_in_network "$ROUTER_CONTAINER" "$INT_NET" \
|| docker network connect --ip "$ROUTER_INT_IP" "$INT_NET" "$ROUTER_CONTAINER"
container_in_network "$ROUTER_CONTAINER" "$EXT_NET" \
|| docker network connect --ip "$ROUTER_EXT_IP" "$EXT_NET" "$ROUTER_CONTAINER"

# Attack-Machine zusätzlich ins ext_ctf (bleibt im internen wegen Docker port-mapping Limitation)
if [ -n "$ATTACK" ]; then
container_in_network "$ATTACK" "$EXT_NET" \
|| docker network connect --ip "$ATTACK_EXT_IP" "$EXT_NET" "$ATTACK"
fi

# Warten bis SSH antwortet
echo -n "Waiting for router SSH"
for _ in $(seq 1 24); do
nc -z -w2 127.0.0.1 2222 2>/dev/null && break
echo -n "."
sleep 5
done
echo " ok"

echo ""
echo "CTF router challenge running."
echo ""
echo "Router:"
echo " internal $ROUTER_INT_IP (virt-cybics)"
echo " external $ROUTER_EXT_IP (ext_ctf)"
echo " SSH localhost:2222 (root/password)"
echo ""
echo "From a Kali VM (real isolation):"
echo " nmap <host-ip> -> find port 2222"
echo " ssh root@<host-ip> -p 2222"
echo " then pivot into 172.18.0.0/24"
echo ""
if [ -n "$ATTACK" ]; then
echo "Attack machine: $ATTACK_EXT_IP in ext_ctf"
echo " (stays in virt-cybics too — docker limitation)"
echo " noVNC still at localhost:6081"
fi
}

# --- STOP ---
do_stop() {
echo "Stopping CTF router challenge..."

ATTACK=$(find_container "$ATTACK_CONTAINER")
if [ -n "$ATTACK" ]; then
container_in_network "$ATTACK" "$EXT_NET" \
&& docker network disconnect "$EXT_NET" "$ATTACK" 2>/dev/null
fi

if docker ps --format '{{.Names}}' | grep -q "$ROUTER_CONTAINER"; then
(cd "$ROUTER_DIR" && docker compose down --timeout 5)
fi

docker network ls --format '{{.Name}}' | grep -q "^${EXT_NET}$" \
&& docker network rm "$EXT_NET" 2>/dev/null

echo "Stopped. Back to normal."
}

# --- STATUS ---
do_status() {
echo "CTF Router Challenge status:"
echo ""

if docker ps --format '{{.Names}}' | grep -q "$ROUTER_CONTAINER"; then
echo " router: running"
container_in_network "$ROUTER_CONTAINER" "$INT_NET" && echo " -> $INT_NET ($ROUTER_INT_IP)"
container_in_network "$ROUTER_CONTAINER" "$EXT_NET" && echo " -> $EXT_NET ($ROUTER_EXT_IP)"
else
echo " router: not running"
fi

if docker network ls --format '{{.Name}}' | grep -q "^${EXT_NET}$"; then
echo " ext_ctf: exists"
else
echo " ext_ctf: not created"
fi

ATTACK=$(find_container "$ATTACK_CONTAINER")
if [ -n "$ATTACK" ]; then
if container_in_network "$ATTACK" "$EXT_NET"; then
echo " attack-vm: ctf mode (ext_ctf + virt-cybics)"
else
echo " attack-vm: normal (virt-cybics only)"
fi
else
echo " attack-vm: not found"
fi
echo ""
}

case "${1:-}" in
start) do_start ;;
stop) do_stop ;;
status) do_status ;;
*) echo "Usage: $0 {start|stop|status}"; exit 1 ;;
esac
51 changes: 51 additions & 0 deletions software/open-wrt/docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
services:
# Der Router, den wir testen wollen
router-test:
image: cybics-openwrt-router:test
build:
context: .
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "2222:2222"
networks:
test_ext:
ipv4_address: 172.19.0.2
test_int:
ipv4_address: 172.18.0.254

# Ein Test-Client im "Internet" (WAN)
wan-probe:
image: alpine
command: sh -c "ip route add 172.18.0.0/24 via 172.19.0.2 && sleep infinity"
cap_add:
- NET_ADMIN
networks:
test_ext:
ipv4_address: 172.19.0.100
depends_on:
- router-test

# Ein Test-Server im "ICS-Netz" (LAN)
lan-probe:
image: alpine
command: sleep infinity
networks:
test_int:
ipv4_address: 172.18.0.10
depends_on:
- router-test

networks:
test_ext:
driver: bridge
ipam:
config:
- subnet: 172.19.0.0/24
test_int:
driver: bridge
ipam:
config:
- subnet: 172.18.0.0/24
28 changes: 28 additions & 0 deletions software/open-wrt/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
services:
router:
image: cybics-openwrt-router:${CYBICS_VERSION:-latest}
build:
context: .
stdin_open: true
tty: true
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
networks:
ext_net:
ipv4_address: 172.19.0.2
virt-cybics:
ipv4_address: 172.18.0.254

networks:
ext_net:
driver: bridge
driver_opts:
com.docker.network.bridge.name: cybics-ext-net
ipam:
config:
- subnet: 172.19.0.0/24
virt-cybics:
external: true
name: virtual_virt-cybics
24 changes: 24 additions & 0 deletions software/open-wrt/run-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
# Stoppe alte Tests
docker compose -f docker-compose.test.yml down

echo "[*] Baue Router-Image..."
docker compose -f docker-compose.test.yml build --no-cache

echo "[*] Starte Test-Umgebung..."
docker compose -f docker-compose.test.yml up -d

echo "[*] Warte 60 Sekunden auf OpenWrt Boot..."
sleep 60

echo "[*] TEST 1: Ping von WAN-Probe zum Router (WAN-Interface)..."
docker exec open-wrt-wan-probe-1 ping -c 3 172.19.0.2

echo "[*] TEST 2: Ping von WAN-Probe durch den Router zur LAN-Probe..."
docker exec open-wrt-wan-probe-1 ping -c 3 172.18.0.10

if [ $? -eq 0 ]; then
echo -e "\n[+] ERFOLG: Der Router leitet Traffic korrekt weiter!"
else
echo -e "\n[-] FEHLER: Routing funktioniert nicht. Prüfe 'docker logs router-test'."
fi
35 changes: 35 additions & 0 deletions software/open-wrt/start-challenge-07.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash
set -e
$ROUTER_NET="challenge07_ext_net"


echo "[*] Starting Challenge 07: Router Pivoting..."

# Router starten
echo "[*] Starting router container..."
docker compose -p challenge07 -f docker-compose.yml up -d

# Attack-Machine Container finden
ATTACK_CONTAINER=$(docker ps --format '{{.Names}}' | grep 'attack-machine' | head -1)

if [ -z "$ATTACK_CONTAINER" ]; then
echo "[!] Error: attack-machine container not found. Is the base environment running?"
exit 1
fi

echo "[*] Found attack-machine: $ATTACK_CONTAINER"

# Attack-Machine vom internen Netz trennen
echo "[*] Disconnecting attack-machine from virt-cybics..."
docker network disconnect virtual_virt-cybics "$ATTACK_CONTAINER"

# Attack-Machine mit externem Netz verbinden
echo "[*] Connecting attack-machine to ext_net (172.19.0.100)..."
docker network connect --ip 172.19.0.100 $ROUTER_NET "$ATTACK_CONTAINER"

echo ""
echo "[+] Challenge 07 active."
echo "[+] Attack machine : $ATTACK_CONTAINER @ 172.19.0.100 (ext_net)"
echo "[+] Router WAN : 172.19.0.2 (ext_net)"
echo "[+] Router LAN : 172.18.0.254 (virt-cybics)"
echo "[+] Pivot through the router to reach the ICS network."
Loading
Loading