Hexecution
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
| Port | Service | Version | Notes |
|---|---|---|---|
| <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
- Custom_vm_emulation
- Opcode_reverse_engineering
- Permutation_inversion
- Double_permutation_reversal
- 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
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
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:
| Register | Purpose |
|---|---|
| VEGETABLE | General purpose |
| FRUIT | General purpose |
| MEAT | General purpose |
| DAIRY | General purpose |
| PROTEIN | Accumulator / pointer |
| CARBO | Memory 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 Opcode | Actual Operation | Description |
|---|---|---|
BOIL REG, imm | MOV REG, imm | Load immediate value |
AES256 imm | mem[CARBO + counter++] = imm | Sequential byte write to memory |
SPELL 1 | write(stdout) | Print mem[CARBO..PROTEIN] |
SPELL 0 | read(stdin) | Read input into mem[CARBO..] |
ROAST R1, R2 | XOR R1, R2 | Bitwise XOR |
QUICKMAFFS addr | PROTEIN = mem[addr] | Load from memory into PROTEIN |
GOODBYE R1, R2 | MOV R1, R2 | Register copy |
WINDOW REG | mem[CARBO] = REG | Write register to memory at CARBO address |
LADDER REG | INC REG | Increment register |
GRIND R1, R2 | assert R1 == R2 | Compare, exit on mismatch |
PEPEFROG R1, R2 | memcmp 32 bytes | Compare 32 bytes of memory, output success |
CHAIR | Debug dump ASCII | Dump memory as ASCII |
Analyzing recipe.asm
The program executes the following logic:
- Lines 1-20: Writes and outputs the prompt "Enter the flag: "
- Line 24: Reads 32-byte user input via
SPELL 0into memory starting at offset 0x14 - 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_
- Line 60: Input length check == 32 via
GRIND PROTEIN, VEGETABLE - Lines 61-188 (Phase 1): In-place permutation within 4-byte blocks:
[a,b,c,d] → [c,b,d,a] - Lines 189-285 (Phase 2): Inter-block permutation — reading from shuffled input at specific addresses and sequential writing to new memory area (0x42+)
- 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:
- Invert Phase 2: Compute inverse permutation, apply to reference string → get Phase 1 output:
5U1c_mI0_4a5_deNLu4M01ntr43_Ufn_ - 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-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR