write-up: IoT/Lain_Br34kC0r3/README.md
This commit is contained in:
parent
1b13d8fe65
commit
6e3c244e8b
@ -1,28 +1,48 @@
|
|||||||
# LAIN_Breakcore — Solution
|
# Lain_Br34kC0r3
|
||||||
|
|
||||||
**Difficulty:** Medium | **Category:** IoT | **Flag:** `ECW{LAIN_Br34k_CryPT0}`
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Category | IoT |
|
||||||
|
| Difficulty | Medium |
|
||||||
|
| Points | 500 |
|
||||||
|
| Author | neverhack |
|
||||||
|
| CTF | Espilon 2026 |
|
||||||
|
|
||||||
## Overview
|
---
|
||||||
|
|
||||||
UART hardware/crypto/reverse challenge. Connect to the router's UART interface:
|
## Description
|
||||||
|
|
||||||
- **TX (port 1111)**: Read only — device output
|
This challenge emulates a UART interface on a Lain router.
|
||||||
- **RX (port 2222)**: Write only — send commands
|
Open both connections, interact as if it was real hardware.
|
||||||
|
|
||||||
## Available Commands
|
- **TX**: Read only
|
||||||
|
- **RX**: Write only
|
||||||
|
|
||||||
```text
|
Maybe Lain can help you?
|
||||||
help — list basic commands
|
|
||||||
flag — get the AES-encrypted flag
|
|
||||||
dump_bin — dump the firmware (XOR'd with the key)
|
|
||||||
settings — display the XOR key used for the firmware
|
|
||||||
whoami — current user info
|
|
||||||
show config — show device configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
## Steps
|
---
|
||||||
|
|
||||||
### 1. Connect
|
## TL;DR
|
||||||
|
|
||||||
|
Connect to the split UART interface. Use `settings` to get the XOR key, `dump_bin` to get
|
||||||
|
the obfuscated firmware, de-obfuscate to extract the AES key and IV from `.rodata`, then
|
||||||
|
use `flag` to get the ciphertext and AES-CBC decrypt it to recover the flag.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
| Tool | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `nc` | Split UART connection |
|
||||||
|
| Python 3 + `pycryptodome` | XOR decoding and AES-CBC decryption |
|
||||||
|
| `strings` / Ghidra | Static analysis of deobfuscated firmware |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
### Step 1 — Connect
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Terminal 1 — TX (read output)
|
# Terminal 1 — TX (read output)
|
||||||
@ -32,7 +52,17 @@ nc <host> 1111
|
|||||||
nc <host> 2222
|
nc <host> 2222
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Get the XOR key
|
> 📸 `[screenshot: both terminals open, TX showing the device banner]`
|
||||||
|
|
||||||
|
### Step 2 — List available commands
|
||||||
|
|
||||||
|
```text
|
||||||
|
help
|
||||||
|
```
|
||||||
|
|
||||||
|
Commands available: `help`, `flag`, `dump_bin`, `settings`, `whoami`, `show config`
|
||||||
|
|
||||||
|
### Step 3 — Get the XOR key
|
||||||
|
|
||||||
```text
|
```text
|
||||||
settings
|
settings
|
||||||
@ -40,13 +70,15 @@ settings
|
|||||||
|
|
||||||
Returns the XOR key used to obfuscate the firmware dump.
|
Returns the XOR key used to obfuscate the firmware dump.
|
||||||
|
|
||||||
### 3. Dump and deobfuscate the firmware
|
> 📸 `[screenshot: settings command returning the XOR key]`
|
||||||
|
|
||||||
|
### Step 4 — Dump and deobfuscate the firmware
|
||||||
|
|
||||||
```text
|
```text
|
||||||
dump_bin
|
dump_bin
|
||||||
```
|
```
|
||||||
|
|
||||||
Save the hex output, then XOR each byte with the key from `settings`:
|
Save the hex output from TX, then deobfuscate:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
key = bytes.fromhex("<key_from_settings>")
|
key = bytes.fromhex("<key_from_settings>")
|
||||||
@ -56,23 +88,28 @@ with open("firmware.bin", "wb") as f:
|
|||||||
f.write(firmware)
|
f.write(firmware)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Reverse the firmware to extract AES key and IV
|
### Step 5 — Extract AES key and IV from firmware
|
||||||
|
|
||||||
|
Quick method:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
strings firmware.bin | grep -iE "key|iv|aes|lain"
|
strings -n 10 firmware.bin | grep -iE "key|iv|aes|lain"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or open in Ghidra/Binary Ninja and locate the AES key/IV in `.rodata`.
|
Or open in Ghidra with Xtensa architecture, navigate to `app_main()` → AES setup
|
||||||
|
functions → locate `therapy_aes_key` and associated IV in `.rodata`.
|
||||||
|
|
||||||
### 5. Get the encrypted flag
|
> 📸 `[screenshot: strings output showing the AES key and IV]`
|
||||||
|
|
||||||
|
### Step 6 — Get the encrypted flag
|
||||||
|
|
||||||
```text
|
```text
|
||||||
flag
|
flag
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns the ciphertext in hex.
|
Returns the ciphertext as a hex string on TX.
|
||||||
|
|
||||||
### 6. Decrypt the flag
|
### Step 7 — Decrypt the flag
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
@ -83,13 +120,14 @@ iv = b"<iv_from_firmware>" # 16 bytes
|
|||||||
ciphertext = bytes.fromhex("<hex_from_flag_command>")
|
ciphertext = bytes.fromhex("<hex_from_flag_command>")
|
||||||
|
|
||||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||||
print(unpad(cipher.decrypt(ciphertext), AES.block_size).decode())
|
plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
|
||||||
|
print(plaintext.decode())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 📸 `[screenshot: Python script printing the decrypted flag]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Flag
|
## Flag
|
||||||
|
|
||||||
`ECW{LAIN_Br34k_CryPT0}`
|
`ECW{LAIN_Br34k_CryPT0}`
|
||||||
|
|
||||||
## Author
|
|
||||||
|
|
||||||
neverhack
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user