HTB: oBfsC4t10n Challenge
oBfsC4t10n - HackTheBox Challenge Writeup
Challenge Information
| Field | Value |
|---|---|
| Name | oBfsC4t10n |
| Category | Misc |
| Difficulty | Hard |
| Author | d3vn0mi |
Description
An email attachment lands in the SOC’s queue. It’s supposedly a malicious document, but their analysts believe something in it was broken or obfuscated badly enough that it never actually detonated. The task: reverse the chain far enough to recover the intended command-and-control mechanism — i.e., figure out what the document would have done had its errors not gotten in the way.
Solution
The provided archive contained a single file, invoice-42369643.html, delivered at 0 bytes in the extracted state — a hint that the “error” the SOC saw was simply a failed extraction, not a broken payload. The real content was sitting in the original zip under AES-256 (WinZip-style) encryption, which standard unzip can’t handle. Once decrypted with 7z, the true HTML-smuggling document appeared.
From there the chain unraveled in three distinct stages:
- HTML smuggling → XLSM. The HTML page hid a base64-encoded Excel workbook inside a
data:URI on a disguised download link, using HTML entities to dodge naive string-matching AV/scanners. - XLSM macro → HTA. The workbook’s
Auto_Openmacro reassembled an HTA payload from three separate, unrelated containers spread across the workbook’s XML and OLE streams (drawing metadata, a UserForm stream, and shared strings) — none of which lived in the obvious VBA macro source, defeating simple macro-extraction tooling. - HTA → shellcode injection. The HTA enabled
AccessVBOMin the registry, injected an obfuscated VBA module, and that module ultimately decoded and ran XOR-obfuscated shellcode revealing the intended C2 mechanism and the flag.
Key Steps
1. Recover the real archive contents
# The extracted HTML was 0 bytes — the zip listing showed the true size,# so the zip's built-in password protection was the actual blocker.unzip -l /out/<uuid>.zip# unzip -P hackthebox <uuid>.zip -> fails: "unsupported compression method 99"# Method 99 = WinZip AES encryption, which classic `unzip` cannot decrypt.
7z x -phackthebox -y /out/<uuid>.zip# Recovers invoice-42369643.html at full size (48,950 bytes)2. Pull the XLSM out of the HTML smuggling blob
import re, base64
d = open('invoice-42369643.html').read()
# Payload was an <a download="invoice-...xlsm"> with a data: URI href,# using HTML entities to break naive "application"/"download" string scans.m = re.search(r'data:application[^"]+base64,([A-Za-z0-9+/=]+)', d)blob = base64.b64decode(m.group(1))open('invoice.xlsm', 'wb').write(blob)3. Extract the macro and hunt down the HTA fragments
mkdir -p xl && unzip -o -q invoice.xlsm -d xlpython3 -m oletools.olevba invoice.xlsm > macros.txtThe Auto_Open macro wrote and launched an HTA via mshta, but its base64 body wasn’t a single contiguous string in the macro — it was split across three unrelated document parts:
import re, olefile
# Fragment 1: Shape "AlternativeText" (drawing metadata), 7,082 charsd1 = open('xl/xl/drawings/drawing1.xml').read()part1 = re.search(r'descr="([^"]+)"', d1).group(1)
# Fragment 2: A UserForm OLE stream inside vbaProject.bin, 7,076 charso = olefile.OleFileIO('xl/xl/vbaProject.bin')part2 = o.openstream(['UZdcUQeJ', 'yTJtzjKX']).read().decode()
# Fragment 3: A defined name (JLprrpFr -> Sheet1!$K$2) resolving into# xl/sharedStrings.xml, 7,134 charspart3 = "...extracted via defined-name lookup..."
# Concatenated in macro-execution order: 21,292 chars (divisible by 4 -> valid base64)hta_payload = base64.b64decode(part1 + part2 + part3)open('payload.hta', 'wb').write(hta_payload)4. Trace the HTA to its injected VBA and shellcode
// payload.hta (deobfuscated logic)// 1. Flip AccessVBOM so Office will trust programmatic VBA project accessSetRegistryValue("HKCU\\...\\Excel\\Security\\AccessVBOM", 1);
// 2. Inject a new VBA module at runtime via AddFromString, itself// obfuscated with string concatenation ("&") and Chr() codesVBProject.VBComponents.Add(...).CodeModule.AddFromString(decodedModuleSource);
// 3. Run the injected macro, which stages and executes shellcode5. Decode the XOR-obfuscated shellcode payload
import struct
b = bytearray(open('shellcode.bin', 'rb').read())
# Single-byte/DWORD XOR key recovered from the decode loop found in the VBAkey = 0x7e425620start = 0x18
for i in range(start, len(b) - 3, 4): dw = struct.unpack_from('<I', b, i)[0] ^ key struct.pack_into('<I', b, i, dw)
open('decoded.bin', 'wb').write(b)strings -n 6 decoded.bin | grep -E "HTB\{|domain|wininet|ws2"# Reveals the WinINet/WS2_32-based C2 beacon strings and the flagFlag: HTB{REDACTED}
Tools Used
7z— decrypting the WinZip-AES-protected archive- Python (
re,base64,struct) — HTML-smuggling extraction and shellcode XOR decoding oletools(olevba,olefile) — parsing the XLSM’s macro streams and OLE storageunzip— inspecting/extracting the OOXML (XLSM) package structurestrings— recovering readable C2 indicators from the decoded shellcode
Key Learnings
- A “broken” file is worth checking for the mundane explanation first — here the SOC’s “errors” were just a password-protected zip using an encryption method (
unzipmethod 99 / WinZip AES) that the default tool couldn’t handle, not a corrupted payload. - HTML smuggling hides the actual malicious file inside a
data:URI, often obfuscated with HTML entities specifically to defeat string-based scanners looking forapplication/ordownloadkeywords — decode thehrefdirectly rather than trusting a static scan. - Multi-stage document malware increasingly avoids putting the “interesting” payload in the obvious location (VBA macro source). Splitting a base64 blob across shape
AlternativeText, an OLE UserForm stream, andsharedStrings.xml— then reassembling it at runtime viaAuto_Open— defeats tools that only look at macro text. - HTA-based droppers frequently flip
AccessVBOMbefore usingAddFromStringto inject fresh VBA at runtime — this is a strong signature to search for when triaging Office-based delivery chains. - Custom XOR shellcode decoders can usually be recovered by reading the decode loop in the injected macro/script rather than brute-forcing the key — the key and loop start offset were both directly recoverable from the VBA logic.