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.rodata
post-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
.rodata
block 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!}