Reverse Engineering Challenge: Silent Oracle
A walk-through of an HTB Neurogrid reversing challenge where you analyze a stripped 64-bit PIE binary in Ghidra, identify a hardcoded comparison function, discover the local flag is a decoy, and brute-force the real flag from the remote service using a Python script that tests prefixes based on response differences.
Overview
This reverse-engineering challenge was part of the Neurogrid CTF hosted by HTB.
The task included a Docker instance and a local source file mirroring the remote service (chall). Running it displayed an ASCII witch and prompted for an unknown input string. Any incorrect input produced a failure message.
The initial objective was simple: determine the exact string the program expected and check whether submitting it would reveal a flag.
The Tools used are the following;
- Ghidra
- Python
Initial File Analysis
Before starting this challenge I did some small analysis on what this file is. I ran file on chall.
chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a198e4a69af44b16ca4a19e59794582502f451a4, for GNU/Linux 4.4.0, stripped
That tells me that this file is;
- A 64 bit linux binary using little endian
- Compiled for normal 64 bit linux systems
- Stripped, so all symbols and function names removed.
Nothing unusual here.
Core techniques
Analyzing the binary in Ghidra revealed several functions.
Two stood out: FUN_00101283 and FUN_001011fc.
FUN_00101283
This function handled input, printed the banner, and passed the user’s string to the comparison function:
cVar1 = FUN_001011fc(local_58, local_60);
Based on structure and behavior, this was effectively the main function.
FUN_001011fc
This function compared the user input against a hardcoded string:
if (*(char *)(param_1 + (int)local_c) !=
PTR_s_HTB{test_flag_hahaha}_0015d068[(int)local_c])
{
puts(&DAT_0015b040);
sleep(5);
return 0;
}
The logic compared each character of the input with the corresponding character in the embedded string HTB{test_flag_hahaha}. Entering that string locally returned a success message.
However, submitting it to the remote Docker instance resulted in a failure. That confirmed the local string was only a placeholder.
Testing small prefixes showed different failure messages. For example:
-
"HTB{"→ partially correct -
"HTB{1"→ fully incorrect
This implied the remote instance performed partial validation.
So brute-forcing it character-by-character became possible.
Enumeration Script
To automate the process, I wrote a simple Python script that:
- Connects to the remote instance
- Sends a candidate prefix
- Checks the returned message
- Extends the prefix whenever the response indicates “partially correct”
import socket
import time
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_!@#$%^&*()-+=[];:'\",./<>?|\\"
prefix = "HTB{"
def talk(payload):
time.sleep(0.15)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
try:
s.connect(("94.237.52.235", 59894))
except:
return ""
data = ""
try:
while True:
chunk = s.recv(1024)
if not chunk:
break
data += chunk.decode("utf-8", errors="ignore")
except:
pass
try:
s.send((payload + "\n").encode())
except:
s.close()
return ""
try:
while True:
chunk = s.recv(1024)
if not chunk:
break
data += chunk.decode("utf-8", errors="ignore")
except:
pass
s.close()
return data
while True:
for char in charset:
attempt = prefix + char
resp = talk(attempt)
if "YOU ARE BANISHED" in resp:
prefix += char
print(prefix)
break
if "CONTINUE ON WITH YOUR ADVENTURE" in resp:
print("DONE:", prefix)
exit()
Retrieve the flag
Running the script recovered the correct flag;
HTB{Tim1ng_z@_h0ll0w_t3ll5}

