WIDE (rev) – CTF Write-up
Challenge Overview
We’re given a 64-bit Linux executable named wide and a database file named db.ex. The application lists several dimensions, but the only interesting entry is encrypted and requires a decryption key.
- An unstripped x86-64 ELF and a custom database
- A hidden entry named
Flaggle Alpha - A key stored as a wide-character string
- Static key recovery without reversing the database cipher
Initial Reconnaissance
The executable is a dynamically linked 64-bit ELF:
$ file wide
wide: ELF 64-bit LSB shared object, x86-64, dynamically linked, not stripped
Running strings against the database reveals seven records. The last one is immediately suspicious:
$ strings db.ex
Primus
people breathe variety practice
Our home dimension
Cheagaz
scene control river importance
The Ice Dimension
...
Flaggle Alpha
admin secret power hidden
The application expects the database path as its first argument:
$ ./wide db.ex
[*] Displaying Dimensions.... [*]
[*] Name | Code | Encrypted [*]
[X] Primus | people breathe variety practice | [*]
[X] Cheagaz | scene control river importance | [*]
[X] Byenoovia | fighting cast it parallel | [*]
[X] Cloteprea | facing motor unusual heavy | [*]
[X] Maraqa | stomach motion sale valuable | [*]
[X] Aidor | feathers stream sides gate | [*]
[X] Flaggle Alpha | admin secret power hidden | * [*]
Which dimension would you like to examine? 6
[X] That entry is encrypted - please enter your WIDE decryption key:
The menu uses zero-based indices, so option 6 selects the seventh database record.
Static Analysis
The main function reads fixed-size records of 0xB4 bytes from db.ex, prints their metadata and passes the database to menu().
Inside menu(), encrypted records follow a separate path. The program reads up to 16 bytes of user input, converts it to a wide-character string with mbstowcs() and compares it against a hardcoded value:
printf("[X] That entry is encrypted - please enter your WIDE decryption key: ");
fgets(key, 0x10, stdin);
mbstowcs(wide_key, key, 0x10);
if (wcscmp(wide_key, L"sup3rs3cr3tw1d3") == 0) {
decrypt(entry);
puts(entry);
}
The required key is therefore:
sup3rs3cr3tw1d3
There is no need to reimplement the XOR loop because the application already decrypts and prints the selected record after a successful comparison.
Why Ordinary Strings Misses the Key
A normal strings wide search does not show the password:
$ strings wide | grep sup3rs3cr3tw1d3
The key is stored as a wchar_t array. On this Linux target, each character occupies four bytes in little-endian order:
00001110: 6b65 793a 2000 0000 7300 0000 7500 0000 key: ...s...u...
00001120: 7000 0000 3300 0000 7200 0000 7300 0000 p...3...r...s...
00001130: 3300 0000 6300 0000 7200 0000 3300 0000 3...c...r...3...
00001140: 7400 0000 7700 0000 3100 0000 6400 0000 t...w...1...d...
00001150: 3300 0000 0000 0000 3.......
The null bytes prevent the default ASCII scan from recognizing one continuous string. GNU strings can search 32-bit little-endian characters explicitly:
$ strings -e L wide | grep sup3r
sup3rs3cr3tw1d3
Result
Using the recovered key against entry 6 lets the original program decrypt the record:
$ ./wide db.ex
...
Which dimension would you like to examine? 6
[X] That entry is encrypted - please enter your WIDE decryption key: sup3rs3cr3tw1d3
HTB{som3_str1ng5_4r3_w1d3}
Key Takeaways
- Check how a suspected secret is represented in memory; ASCII searches will miss UTF-16 and UTF-32 strings.
- On Linux,
wchar_tis commonly four bytes, while Windows typically uses two-byte wide characters. - A hardcoded comparison value may be enough to solve a challenge without reversing the subsequent decryption routine.
- Menu numbering can be zero-based even when the interface presents a human-readable list.