- Challenge: beacon.bin (First Stage)
- Category: Binary Exploitation / Reverse Engineering
- Difficulty: Insane
- URL of the room: Scheme Catcher
The actual functionality of the program is concealed by a custom encrypted .easter section found in the beacon.bin binary. We found a self-decrypting stub that XORs the encrypted code with key 0x0D using dynamic analysis with GDB. The payload_load() function uncovered a hardcoded HTTP path /7ln6Z1X9EF built from hex immediates after decryption. This path resulted in a directory listing with the next stage binary and foothold.txt (Second flag).
Scanning using nmap
$ nmap -sV -p- 10.48.190.204
Starting Nmap 7.94 ( https://nmap.org ) at 2025-12-11 12:00 IST
Nmap scan report for 10.48.190.204
Host is up (0.045s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
80/tcp open http Apache/2.4.58 (Ubuntu)
9004/tcp open unknown
21337/tcp open unknown
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 234.56 seconds
After the scan we used gobuster to list hidden directory on port 80
$ gobuster dir -u http://10.48.190.204 -w /usr/share/wordlists/dirb/common.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.48.190.204
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: zip,bin,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess (Status: 403) [Size: 279]
/.htpasswd (Status: 403) [Size: 279]
/.hta (Status: 403) [Size: 279]
/dev (Status: 301) [Size: 314] [--> http://10.48.190.204/dev/]
/index.html (Status: 200) [Size: 10918]
/server-status (Status: 403) [Size: 279]
Progress: 18456 / 18460 (99.98%)
===============================================================
Finished
===============================================================
On this directory we found a zip file which is carying our first binary beacon.bin
file beacon.bin
# beacon.bin: ELF 64-bit LSB executable, x86-64
checksec --file=beacon.bin
# Arch: amd64-64-little
# RELRO: Partial RELRO
# Stack: No canary found
# NX: NX enabled
# PIE: No PIE (0x400000)- 64-bit Linux executable
- No stack canary (vulnerable to buffer overflows)
- NX enabled (no shellcode on stack)
- No PIE (static addresses)
strings beacon.binFound:
THM{Welcom3_to_th3_eastmass_pwnland}- First flag placeholderlocalhost- HTTP connection targetGET %s HTTP/1.1- HTTP request format/tmp/b68vC103RH- Temporary file path
Interesting Functions:
setup()- Initializationpayload_load()- Suspicious - loads somethingcmd()- Command executiondelete_cmd()- Cleanupstart_socket_server()- Network functionality
Note: The disassembly revealed a brief loop that iterated over a memory range from
0x401370to0x401bc4when examining the early code in the.eastersection. Each byte in this loop was read, XORed with the constant0x0D, and then written back to the original location. Execution was then redirected to that area. This pattern is typical of a self-decrypting XOR stub, which means that the actual program logic is encrypted and only decrypted during runtime, just prior to execution.
gdb beacon.bin
(gdb) info functions
# All functions marked <encrypted>
(gdb) disas main
# Showed XOR decryption loop!Decryption Stub Found at 0x804000:
0x804000: nop # NOP sled
0x804008: movabs rsi,0x401370 # Source: encrypted code
0x804012: movabs rdi,0x401bc4 # Destination: decrypted
0x80401d: cmpb [rsi],0xd # Compare with key 0x0D
0x804020: inc rsi # Next byte
0x804023: cmp rsi,rdi # Check if done
0x804026: jne 0x804020 # Loop
0x804028: push 0x401370 # Jump to decrypted main
0x80402d: retThe Algorithm: XOR each byte from 0x401370 to 0x401bc4 with key 0x0D
Breaking After Decryption:
(gdb) break *0x804027 # Right after decryption
(gdb) run
(gdb) x/10i 0x401370 # Now readable!Functions are now visible!
(gdb) disas payload_loadKey Instructions Found:
# Creates socket connection to localhost:80
0x4015e2: call socket@plt
0x401619: call connect@plt
# Constructs HTTP GET request
0x401648: movabs rax,0x58315a366e6c372f # ← SUSPICIOUS HEX
0x401652: mov QWORD PTR [rbp-0x11c],rax
0x401659: movl DWORD PTR [rbp-0x114],0x464539
# Sends HTTP request
0x401674: lea rdx,[rip+0xa75] # "GET %s HTTP/1.1..."
0x40167b: call snprintf@pltDecoding the Suspicious hex:
#!/usr/bin/env python3
import struct
# First 8-byte immediate
val1 = 0x58315a366e6c372f
# Second 4-byte immediate (only 3 bytes used)
val2 = 0x464539
# Convert to little-endian bytes
part1 = struct.pack('<Q', val1) # b'/7ln6Z1X'
part2 = struct.pack('<I', val2)[:3] # b'9EF'
full_path = part1 + part2
print("Hidden path:", full_path.decode('ascii'))Got the hidden path as /7ln6Z1X9EF
Analysis of Reverse Data Flow:
The format
"GET %s HTTP/1.1"is used bysnprintf()at0x401674.The source of the
%sparameter isrbp-0x11c.Looking for writes to
rbp-0x11cin reverse, themovabsinstruction was located at0x401648.
Accessing the Hidden Directory
curl http://10.48.190.204/7ln6Z1X9EF/Directory Listing Revealed:
Index of /7ln6Z1X9EF
- foothold.txt 37 bytes
- 4.2.0-R1-1337-server.zip 5.2M
curl http://10.48.190.204/7ln6Z1X9EF/foothold.txt
# THM{beacon_analysis_complete_on_to_stage2}- file - Binary identification
- checksec - Security feature enumeration
- readelf - ELF structure analysis
- strings - Static string extraction
- GDB - Dynamic debugging
- objdump - Disassembly
Next: Scheme-Catcher-part-2.md