diff --git a/IoT/Lain_Br34kC0r3_V2/README.md b/IoT/Lain_Br34kC0r3_V2/README.md index 68b0085..808c152 100644 --- a/IoT/Lain_Br34kC0r3_V2/README.md +++ b/IoT/Lain_Br34kC0r3_V2/README.md @@ -1,22 +1,76 @@ -# LAIN_Br34kC0r3 V2 — Solution +# Lain_Br34kC0r3 V2 -**Chapitre 2 : Core Analysis** | Difficulté : Hard | Flag : `ESPILON{3sp32_fl4sh_dump_r3v3rs3d}` +| Field | Value | +|-------|-------| +| Category | IoT | +| Difficulty | Hard | +| Points | 500 | +| Author | Eun0us | +| CTF | Espilon 2026 | -## Overview +--- -Ce challenge fournit un dump flash complet d'un ESP32 (bootloader + partition table + NVS + firmware applicatif). Le joueur doit extraire le firmware, le reverse engineer pour trouver les clés AES-256-CBC, puis déchiffrer le flag stocké dans la NVS. +## Description -## Étape 1 — Récupérer le dump flash +**Chapter 2 — Core Analysis** + +After exploring the WIRED-MED therapy module through its UART interface, you successfully +dumped the complete flash of the device. This dump contains the bootloader, the partition +table, the NVS and the application firmware. + +Your mission: **reverse engineer the ESP32 firmware** to extract the encryption keys used +to protect patient data, then decrypt the flag stored in the NVS partition. + +*"Close your eyes. Open The Wired. Analyze the core."* + +- **TX (read):** `nc CHALLENGE_HOST 1111` +- **RX (write):** `nc CHALLENGE_HOST 2222` + +--- + +## TL;DR + +Issue `dump_flash` over the RX UART port to receive a base64-encoded full flash dump on TX. +Decode and use `binwalk` to locate the app firmware at offset 0x10000. Run `strings` on the +binary to find the AES-256-CBC key (`W1R3D_M3D_TH3R4PY_K3Y_2024_L41N!`) and IV (`L41N_WIRED_IV_01`). +Get the encrypted flag via the `encrypted_data` command, then AES-256-CBC decrypt it. + +--- + +## Tools + +| Tool | Purpose | +|------|---------| +| `nc` | Split UART connection | +| Python 3 | Base64 decode, data parsing | +| `binwalk` / `esptool.py` | Parse the flash dump | +| `strings` | Extract AES key and IV | +| Ghidra (Xtensa) | Full reverse engineering if needed | +| `pycryptodome` | AES-256-CBC decryption | + +--- + +## Solution + +### Step 1 — Dump the flash ```bash -# Terminal 1 : TX (lecture) +# Terminal 1 — TX (read) nc 1111 | tee flash_output.txt -# Terminal 2 : RX (écriture) +# Terminal 2 — RX (write) echo "dump_flash" | nc 2222 ``` -Le dump est envoyé en base64. Extraire et décoder : +The dump is sent as base64 between markers: + +``` +=== BEGIN FLASH DUMP === + +=== END FLASH DUMP === +``` + +Decode: ```python import base64 @@ -24,7 +78,6 @@ import base64 with open("flash_output.txt") as f: lines = f.readlines() -# Extract base64 lines between markers b64_data = "" capture = False for line in lines: @@ -41,17 +94,16 @@ with open("flash_dump.bin", "wb") as f: f.write(flash) ``` -## Étape 2 — Analyse du dump flash +> 📸 `[screenshot: TX terminal showing the base64 flash dump streaming out]` + +### Step 2 — Identify the flash structure ```bash -# Identifier les composants binwalk flash_dump.bin - -# Ou utiliser esptool -esptool.py image_info --version 2 flash_dump.bin ``` -Structure identifiée : +Expected structure: + ``` 0x0000 Padding (0xFF) 0x1000 ESP32 bootloader (magic 0xE9) @@ -61,64 +113,42 @@ Structure identifiée : 0x10000 Application firmware (magic 0xE9) ``` -## Étape 3 — Extraire le firmware applicatif +### Step 3 — Extract and analyze the application firmware ```bash -# Extraire l'app à partir de l'offset 0x10000 dd if=flash_dump.bin of=app_firmware.bin bs=1 skip=$((0x10000)) - -# Vérifier -file app_firmware.bin -# Devrait montrer un binaire ESP32 ou "data" - -hexdump -C app_firmware.bin | head -5 -# Premier byte devrait être 0xE9 (ESP image magic) -``` - -## Étape 4 — Reverse Engineering - -### Méthode rapide : strings - -```bash strings -n 10 app_firmware.bin | grep -i "key\|aes\|iv\|wired\|therapy" ``` -Résultats attendus : +Expected results: + ``` W1R3D_M3D_TH3R4PY_K3Y_2024_L41N! # AES-256 key (32 bytes) L41N_WIRED_IV_01 # AES IV (16 bytes) WIRED-MED Therapy Module ``` -### Méthode complète : Ghidra +> 📸 `[screenshot: strings output identifying the AES key and IV]` -1. Ouvrir Ghidra, importer `app_firmware.bin` (ou mieux, l'ELF si disponible) -2. Architecture : **Xtensa:LE:32:default** -3. Analyser → chercher `app_main` dans les symboles -4. Suivre les appels : `app_main()` → `wired_med_crypto_init()` → `mbedtls_aes_setkey_enc()` -5. Les arguments de `mbedtls_aes_setkey_enc()` pointent vers `therapy_aes_key` dans `.rodata` -6. Extraire les 32 bytes de la clé et les 16 bytes de l'IV +For full confirmation: open in Ghidra with **Xtensa:LE:32:default** architecture, +find `app_main()` → `wired_med_crypto_init()` → `mbedtls_aes_setkey_enc()`. The +key and IV are passed as arguments pointing into `.rodata`. -## Étape 5 — Récupérer le flag chiffré +### Step 4 — Get the encrypted flag ciphertext -### Option A : Commande directe -``` +On the RX port: + +```text encrypted_data ``` -→ Retourne le ciphertext en hex -### Option B : Parser la NVS -```bash -# Extraire la partition NVS -dd if=flash_dump.bin of=nvs_dump.bin bs=1 skip=$((0x9000)) count=$((0x6000)) +Returns the ciphertext as a hex string on TX. -# Utiliser nvs_tool.py de ESP-IDF (si disponible) -python3 $IDF_PATH/components/nvs_flash/nvs_partition_tool/nvs_tool.py --dump nvs_dump.bin -``` +Alternatively, extract from the NVS partition (namespace `wired_med`, key `encrypted_flag`, blob type) using `nvs_tool.py` from ESP-IDF. -Chercher l'entrée : namespace `wired_med`, key `encrypted_flag`, type blob +> 📸 `[screenshot: encrypted_data command returning the hex ciphertext]` -## Étape 6 — Déchiffrement AES-256-CBC +### Step 5 — Decrypt AES-256-CBC ```python from Crypto.Cipher import AES @@ -127,8 +157,7 @@ from Crypto.Util.Padding import unpad key = b"W1R3D_M3D_TH3R4PY_K3Y_2024_L41N!" # 32 bytes iv = b"L41N_WIRED_IV_01" # 16 bytes -# Ciphertext from encrypted_data command or NVS -ciphertext = bytes.fromhex("...") +ciphertext = bytes.fromhex("...") # from encrypted_data output cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size) @@ -136,7 +165,9 @@ print(plaintext.decode()) # ESPILON{3sp32_fl4sh_dump_r3v3rs3d} ``` -## Résumé de la chaîne d'attaque +> 📸 `[screenshot: Python decryption script printing the flag]` + +### Attack chain summary ``` dump_flash → base64 decode → binwalk → extract app @ 0x10000 @@ -144,6 +175,8 @@ dump_flash → base64 decode → binwalk → extract app @ 0x10000 → encrypted_data (or NVS parse) → AES-256-CBC decrypt → FLAG ``` -## Script de solve complet +--- -Voir `solve/solve.py` +## Flag + +`ESPILON{3sp32_fl4sh_dump_r3v3rs3d}`