← Back to Writeups
HTBN/AReversing

Hexecution

XESXOR8/23/20266 min read
#reversing#htb#n/a

Hexecution

Platform: HackTheBox | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-02-28 | Status: Solved Techniques: custom_vm_emulation, opcode_reverse_engineering, permutation_inversion, double_permutation_reversal, block_shuffle_reversal

Summary

Two files: cook — ELF 64-bit LSB PIE executable, x86-64, stripped, dynamically linked (custom VM interpreter) and recipe.asm — 289 lines of cooking-themed esoteric assembly. The binary reads and executes the recipe file.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: HackTheBox | ID: 20260228_hackthebox_hexecution
  • Tags: elf64, esoteric_language, stripped, block_cipher, permutation_cipher, custom_vm, opcode_mapping, cooking_themed
  • Indicators: two files: binary interpreter + custom assembly source, cooking-themed instruction names (BOIL, ROAST, SPELL, etc.), custom register names (VEGETABLE, FRUIT, MEAT, DAIRY, PROTEIN, CARBO), AES256 used as opcode name but performs simple byte store, stripped ELF binary that reads and executes external .asm file
  • Source: 20260228_hackthebox_hexecution.md

Foothold

Vulnerability / Misconfiguration

  1. Custom_vm_emulation
  2. Opcode_reverse_engineering
  3. Permutation_inversion
  4. Double_permutation_reversal
  5. Block_shuffle_reversal
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • custom_vm_emulation
  • opcode_reverse_engineering
  • permutation_inversion
  • double_permutation_reversal
  • block_shuffle_reversal
  • Tags: elf64, esoteric_language, stripped, block_cipher, permutation_cipher, custom_vm, opcode_mapping, cooking_themed

Original Writeup

<details><summary>Click to expand original content</summary>

Description

My friend is always tampering with low-level things. So created something different, and going to challenge him. Before challenge him, can you try it and see if it works or not?

Two files: cook — ELF 64-bit LSB PIE executable, x86-64, stripped, dynamically linked (custom VM interpreter) and recipe.asm — 289 lines of cooking-themed esoteric assembly. The binary reads and executes the recipe file.

Analysis

Initial Reconnaissance

$ file cook
cook: ELF 64-bit LSB pie executable, x86-64, dynamically linked, stripped

$ file recipe.asm
recipe.asm: ASCII text

$ wc -l recipe.asm
289 recipe.asm

The binary is stripped — no symbols, full reverse engineering through disassembler required. The recipe.asm file contains instructions with cooking-themed names: BOIL, AES256, SPELL, ROAST, QUICKMAFFS, GRIND, GOODBYE, WINDOW, LADDER, PEPEFROG, CHAIR.

VM Architecture

The VM has 6 16-bit registers and a 256-byte memory array:

RegisterPurpose
VEGETABLEGeneral purpose
FRUITGeneral purpose
MEATGeneral purpose
DAIRYGeneral purpose
PROTEINAccumulator / pointer
CARBOMemory base pointer

Additionally: internal counter (auto-increment for AES256 and SPELL 0).

Opcode Mapping

Full reverse engineering of the cook binary through Ghidra revealed the following mapping:

Cooking OpcodeActual OperationDescription
BOIL REG, immMOV REG, immLoad immediate value
AES256 immmem[CARBO + counter++] = immSequential byte write to memory
SPELL 1write(stdout)Print mem[CARBO..PROTEIN]
SPELL 0read(stdin)Read input into mem[CARBO..]
ROAST R1, R2XOR R1, R2Bitwise XOR
QUICKMAFFS addrPROTEIN = mem[addr]Load from memory into PROTEIN
GOODBYE R1, R2MOV R1, R2Register copy
WINDOW REGmem[CARBO] = REGWrite register to memory at CARBO address
LADDER REGINC REGIncrement register
GRIND R1, R2assert R1 == R2Compare, exit on mismatch
PEPEFROG R1, R2memcmp 32 bytesCompare 32 bytes of memory, output success
CHAIRDebug dump ASCIIDump memory as ASCII

Analyzing recipe.asm

The program executes the following logic:

  1. Lines 1-20: Writes and outputs the prompt "Enter the flag: "
  2. Line 24: Reads 32-byte user input via SPELL 0 into memory starting at offset 0x14
  3. Lines 25-57: Writes 32-byte reference string to memory at offset 0x70:
   Hex: 35 6d 61 4e 63 49 34 5f 5f 55 31 30 5f 64 65 35
        4c 31 33 5f 4d 6e 34 55 30 75 34 74 72 66 6e 5f
   ASCII: 5maNcI4__U10_de5L13_Mn4U0u4trfn_
  1. Line 60: Input length check == 32 via GRIND PROTEIN, VEGETABLE
  2. Lines 61-188 (Phase 1): In-place permutation within 4-byte blocks: [a,b,c,d] → [c,b,d,a]
  3. Lines 189-285 (Phase 2): Inter-block permutation — reading from shuffled input at specific addresses and sequential writing to new memory area (0x42+)
  4. Lines 287-289: Compare result with reference string via PEPEFROG

Solution

Step 1: Building VM Emulator

To understand the recipe.asm logic, a full VM emulator was written in Python implementing all 12 opcodes. Tracing with symbolic input allowed precise identification of both permutation phases.

Step 2: Extracting Permutations

Phase 1 (intra-block): For each 4-byte block [a,b,c,d][c,b,d,a]

Phase 2 (inter-block): Read addresses from input (base 0x14):

[0x14, 0x19, 0x1e, 0x23, 0x17, 0x1a, 0x1d, 0x20,
 0x18, 0x15, 0x16, 0x1b, 0x1c, 0x21, 0x22, 0x1f,
 0x24, 0x29, 0x2e, 0x33, 0x27, 0x2a, 0x2d, 0x30,
 0x28, 0x25, 0x26, 0x2b, 0x2c, 0x31, 0x32, 0x2f]

Indices (from 0): [0, 5, 10, 15, 3, 6, 9, 12, 4, 1, 2, 7, 8, 13, 14, 11, 16, 21, 26, 31, 19, 22, 25, 28, 20, 17, 18, 23, 24, 29, 30, 27]

Step 3: Inverting Permutations

Working backwards from the reference string:

  1. Invert Phase 2: Compute inverse permutation, apply to reference string → get Phase 1 output: 5U1c_mI0_4a5_deNLu4M01ntr43_Ufn_
  2. Invert Phase 1: For each 4-byte block reverse [c,b,d,a] → [a,b,c,d] (i.e., apply [d,b,a,c]) → get original input: REDACTED

Step 4: Verification

Direct check: apply both permutations to REDACTED:

  • After Phase 1: 5U1c_mI0_4a5_deNLu4M01ntr43_Ufn_
  • After Phase 2: 5maNcI4__U10_de5L13_Mn4U0u4trfn_ ✓ (matches reference)

Full Solve Script

#!/usr/bin/env python3
"""Hexecution solver — inverts double permutation to recover flag."""

# Reference string from AES256 instructions in recipe.asm
ref_bytes = [0x35, 0x6d, 0x61, 0x4e, 0x63, 0x49, 0x34, 0x5f,
             0x5f, 0x55, 0x31, 0x30, 0x5f, 0x64, 0x65, 0x35,
             0x4c, 0x31, 0x33, 0x5f, 0x4d, 0x6e, 0x34, 0x55,
             0x30, 0x75, 0x34, 0x74, 0x72, 0x66, 0x6e, 0x5f]
ref_str = ''.join(chr(b) for b in ref_bytes)

# Phase 2 permutation: addresses read from input (base 0x14)
phase2_addrs = [0x14, 0x19, 0x1e, 0x23, 0x17, 0x1a, 0x1d, 0x20,
                0x18, 0x15, 0x16, 0x1b, 0x1c, 0x21, 0x22, 0x1f,
                0x24, 0x29, 0x2e, 0x33, 0x27, 0x2a, 0x2d, 0x30,
                0x28, 0x25, 0x26, 0x2b, 0x2c, 0x31, 0x32, 0x2f]
perm2 = [addr - 0x14 for addr in phase2_addrs]

# Invert Phase 2: output[perm2[i]] = ref[i]
inv_perm2 = [0] * 32
for i in range(32):
    inv_perm2[perm2[i]] = i
shuffled = [ref_str[inv_perm2[j]] for j in range(32)]
shuffled_str = ''.join(shuffled)
print(f"After inverting Phase 2: {shuffled_str}")

# Invert Phase 1: reverse [c,b,d,a] -> [a,b,c,d]
# Forward: [0,1,2,3] -> [2,1,3,0], so position 0 came from 3, etc.
result = [''] * 32
for block in range(8):
    base = block * 4
    a, b, c, d = shuffled_str[base], shuffled_str[base+1], shuffled_str[base+2], shuffled_str[base+3]
    # Inverse of [a,b,c,d] -> [c,b,d,a] is [d,b,a,c]
    result[base]     = d  # original[0] = shuffled[3]
    result[base + 1] = b  # original[1] = shuffled[1]
    result[base + 2] = a  # original[2] = shuffled[0]
    result[base + 3] = c  # original[3] = shuffled[2]

flag_inner = ''.join(result)
print(f"Flag inner: {flag_inner}")
print(f"HTB{{{flag_inner}}}")
# HTB{REDACTED}
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR