HTB: oBfsC4t10n Challenge

oBfsC4t10n - HackTheBox Challenge Writeup

Challenge Information

FieldValue
NameoBfsC4t10n
CategoryMisc
DifficultyHard
Authord3vn0mi

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:

  1. 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.
  2. XLSM macro → HTA. The workbook’s Auto_Open macro 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.
  3. HTA → shellcode injection. The HTA enabled AccessVBOM in 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

Terminal window
# 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

Terminal window
mkdir -p xl && unzip -o -q invoice.xlsm -d xl
python3 -m oletools.olevba invoice.xlsm > macros.txt

The 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 chars
d1 = 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 chars
o = 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 chars
part3 = "...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 access
SetRegistryValue("HKCU\\...\\Excel\\Security\\AccessVBOM", 1);
// 2. Inject a new VBA module at runtime via AddFromString, itself
// obfuscated with string concatenation ("&") and Chr() codes
VBProject.VBComponents.Add(...).CodeModule.AddFromString(decodedModuleSource);
// 3. Run the injected macro, which stages and executes shellcode

5. 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 VBA
key = 0x7e425620
start = 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)
Terminal window
strings -n 6 decoded.bin | grep -E "HTB\{|domain|wininet|ws2"
# Reveals the WinINet/WS2_32-based C2 beacon strings and the flag

Flag: 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 storage
  • unzip — inspecting/extracting the OOXML (XLSM) package structure
  • strings — 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 (unzip method 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 for application/ or download keywords — decode the href directly 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, and sharedStrings.xml — then reassembling it at runtime via Auto_Open — defeats tools that only look at macro text.
  • HTA-based droppers frequently flip AccessVBOM before using AddFromString to 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.