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
filechallbashchall: 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.
#### codeFUN_00101283
FUN_00101283This function handled input, printed the banner, and passed the user’s string to the comparison function:
codecVar1 = FUN_001011fc(local_58, local_60);
Based on structure and behavior, this was effectively the
main#### codeFUN_001011fc
FUN_001011fcThis function compared the user input against a hardcoded string:
codeif (*(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}
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:
-
→ partially correctcode
"HTB{" -
→ fully incorrectcode
"HTB{1"
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”
pythonimport 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;
codeHTB{Tim1ng_z@_h0ll0w_t3ll5}
