HTB Satellite Hijack - Pez1181/CTF GitHub Wiki
๐ฐ๏ธ [Hard] SatelliteHijack
๐ Challenge Type: Reversing
๐ Vulnerability: Encrypted shellcode & XOR validation
๐ก๏ธ Protection Bypassed: memfrob obfuscation, runtime decryption, XOR-based flag check
๐ฆ Files Provided
satellite: PIE-enabled ELF executable that echoes user inputlibrary.so: Shared object, stripped and crashes on executionshellcode.bin: Extracted fromlibrary.so's.rodatapost-decryption
๐ Recon & Vulnerability Analysis
๐งช Step 1: Initial Testing
Running satellite shows it simply echoes back input โ no visible vulnerability.
Using ltrace, we spot this call:
send_satellite_message(...)
This is dynamically resolved from library.so, confirming that real logic is offloaded into the shared object.
๐ Step 2: Backdoor Discovery
Reversing send_satellite_message() in library.so reveals:
strncpy(local_28,"TBU`QSPE`FOWJSPONFOU", 0x15);
for (...) local_28[i] -= 1;
getenv(local_28); // becomes SAT_PROJECTENVIRONMENT
So: setting the environment variable SAT_PROJECTENVIRONMENT triggers a hidden payload!
We run:
SAT_PROJECTENVIRONMENT=1 ./satellite
Still no output โ but under the hood, it jumps to FUN_001023e3().
๐ฅ Step 3: Shellcode Execution
Inside FUN_001023e3() we find:
- Allocates RWX memory via
mmap() - Copies
.rodatablock into memory - Calls
memfrob()(XOR with0x2A) - Then executes the result
So: encrypted shellcode is sitting in .rodata, and executed only if the env var is set.
We dump this decrypted shellcode using Ghidra or runtime memory dumping tools.
๐งฉ Step 4: XOR-Encoded Flag Check
Disassembling the shellcode shows:
mov rax, 0x37593076307b356c
mov rdx, 0x3a7c3e753f665666
...
check[i] = <encrypted bytes>
if ((input[i] ^ check[i]) != i) fail;
This means:
To solve: XOR each encrypted byte with its index to recover the correct flag character.
From runtime or static analysis, we extract the encrypted check buffer:
buf = bytearray(b"l5{0v0Y7fVf?u>|:O!|Lx!o$j,;f")
โ Final Script
This fully reconstructs the flag:
# Final working solution โ reconstructs the flag from memory-extracted check buffer
buf = bytearray(b"l5{0v0Y7fVf?u>|:O!|Lx!o$j,;f")
# XOR each byte with its index
for i in range(len(buf)):
buf[i] ^= i
# Format and print flag
flag = b"HTB{" + buf + b"}"
print(flag.decode())
๐ Output
HTB{S4t3llite_D0wn_S4t3ll1t3_DOWNNN!}
๐ง Final Notes
This was a beautifully layered challenge:
- Used obfuscated data in
.rodata(memfrob) - XOR trick required reversing a validation function
- Required combining static reverse engineering with runtime environment manipulation
Every layer peeled back revealed a new one โ true to its name. ๐ง
HTB{S4t3llite_D0wn_S4t3ll1t3_DOWNNN!}