Hexecution
HackTheBox
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.
$ ls tags/ techniques/
Hexecution — HackTheBox
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:cU510m_I54_aNd_eMuL4t10n_4r3_fUn
Step 4: Verification
Direct check: apply both permutations to cU510m_I54_aNd_eMuL4t10n_4r3_fUn:
- 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{cU510m_I54_aNd_eMuL4t10n_4r3_fUn}
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar
Similar writeups
- [pwn][free]Regularity— hackthebox
- [pwn][free]Restaurant— hackthebox
- [reverse][free]Virtually Mad— HackTheBox
- [pwn][free]Getting Started— hackthebox
- [reverse][Pro]Kitchen Sink— tamuctf