Odyssey Finals 2025 - DarBox Inwi (DFIR)
Writeup for DarBox Inwi DFIR challenge from Odyssey Finals 2025
DarBox Inwi - DFIR Challenge Writeup
The challenge presented a router firmware (firmware.bin) and network traffic capture (traffic.pcapng) with the premise that the router was spying on the user.
Firmware Analysis
We first examine the firmware.bin structure and that reveals a SquashFS filesystem at offset 0x40. We extract it using binwalk:
1
binwalk -e firmware.bin
Finding the Backdoor
Next, we search for suspicious startup scripts in _firmware.bin.extracted/squashfs-root/etc/init.d/
I found sysmon, a suspicious init script that starts a hidden binary:
1
cat _firmware.bin.extracted/squashfs-root/etc/init.d/sysmon
The script launches /usr/bin/.sys/upgrade/sysmon - a hidden binary (suspicious!).
Reverse Engineering the Backdoor
This is the backdoor. Upon analyzing it, we find that it exfiltrates data via HTTP POST requests.
Key findings:
- Function names:
send_http_exfil,get_log_entries,get_system_info,read_flag_file - Format string:
secretfile=%s&key=%s&%s&%s×tamp=%ld - File path:
/root/info
We also learn of the decoding mechanism:
- Layer 1: XOR with 0x47 (obfuscates the HTTP POST data)
- Layer 2: Custom hex digit encoding (0x10-0x19, 0x41-0x46)
- Layer 3: Repeating-key XOR with the KEY field
Extracting Network Traffic
We need to get the exfiltrated data and decrypt it:
1
tshark -r traffic.pcapng -Y "http.request.method == POST" -T fields -e http.file_data
Found a POST to /api/data with hex-encoded data:
1
140204150213010e0b025a5056515650065301505251525302530557575357560151535002575353525202...
Decryption Process
Layer 1: XOR with 0x47
1
2
3
import binascii
data = binascii.unhexlify(hex_string) # hex_string from POST request
decoded = bytes([b ^ 0x47 for b in data])
This revealed readable text:
1
SECRETFILE=...&KEY=...&USERNAME=ROOT&HOSTNAME=loq&SYSNAME=lINUX...
The data was structured as URL parameters separated by 0x06 bytes, with 0x1d as the key-value separator (= sign).
Layer 2: Parse Fields
Split the decoded data by field separators:
1
2
3
4
parts = decoded.split(b'\x06')
for part in parts:
if b'\x1d' in part:
field_name, field_value = part.split(b'\x1d', 1)
This got us:
SECRETFILE=1711161117411446171516151445144210101410114616141745...KEY=131012411342114313101216131512161311- Other fields: USERNAME, HOSTNAME, SYSNAME, LOGS, etc.
Layer 3: Custom Hex Decoding
The SECRETFILE and KEY values used a custom encoding scheme:
- Bytes 0x10-0x19 represent hex digits 0-9
- Bytes 0x41-0x46 represent hex digits A-F
Conversion logic:
1
2
3
4
5
6
7
8
def decode_custom_hex(data):
hex_string = ""
for b in data:
if 0x10 <= b <= 0x19:
hex_string += str(b - 0x10) # 0x10 -> '0', 0x11 -> '1', etc.
elif 0x41 <= b <= 0x46:
hex_string += chr(b) # 0x41 -> 'A', 0x42 -> 'B', etc.
return binascii.unhexlify(hex_string)
Applied to KEY:
1
2
3
131012411342114313101216131512161311
→ 302A3B1C3026352631
→ b'0*;\x1c0&5&1'
Applied to SECRETFILE:
1
2
71617A4F75654E4B00401F647E04455E42010058642C5E79414E056F580B6944154779495F525461
→ (binary data)
Layer 4: Repeating-Key XOR
The SECRETFILE was encrypted using repeating-key XOR with the decoded KEY:
1
2
3
4
5
key_bytes = binascii.unhexlify("302A3B1C3026352631")
secretfile_bytes = binascii.unhexlify("71617A4F75654E4B00401F647E04455E42010058642C5E79414E056F580B6944154779495F525461")
decoded = bytes([secretfile_bytes[i] ^ key_bytes[i % len(key_bytes)]
for i in range(len(secretfile_bytes))])
Flag
AKASEC{m1p5_b4ckd00r_0n_th4_r0ut3r_xoxo}
