Skip to content

Commit 624a7f2

Browse files
authored
Merge pull request #19 from vrishabav/main
multiarch-2 writeup
2 parents 045a263 + 472f0af commit 624a7f2

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
+++
2+
title = "MultiArch2"
3+
date = "2025-09-03"
4+
authors = ["Vrishab"]
5+
+++
6+
## Summary of the Exploit
7+
8+
This challenge is built around exploiting a custom VM built with two separate instruction sets (stack‑based and register‑based)
9+
10+
Inside the register‑based instruction set, a key bug in the XOR instruction lets us write outside the space reserved for registers. This allows us to trick the VM so that a normal VM memory address (`0xA000`) actually points to the VM’s own internal state struct `masm_struct`. We can leak useful memory addresses from inside the VM, overwrite the VM function pointer `get_flag` to make it point to our shellcode and thereafter trigger that function pointer and get code execution
11+
12+
---
13+
14+
## VM Setup
15+
16+
When the VM runs, it loads a custom file format called `.masm`, each of which consists of several regions that get mapped when the VM starts:
17+
- **Code segment**: the instructions the VM runs
18+
- **Data segment**: attacker‑controlled, marked as rwx i.e. read, write, execute
19+
- **Stack segment**: where stack‑arch instructions operate
20+
- **Arch table**: tells the VM about whether each instruction runs in stack mode or register mode
21+
22+
This design means we can place custom shellcode directly into the data segment, then aim to transfer execution there
23+
24+
---
25+
26+
### VM State: `masm_struct`
27+
Internally, the VM maintains everything in a large structure `masm_struct`. It contains:
28+
- pointers to the code, data, and arch table in memory
29+
- four general registers, a stack pointer, and program counter
30+
- function pointer called **`get_flag`** at offset `0x28`, which we want to hijack
31+
- metadata about heap allocations (`heap_array`)
32+
33+
---
34+
35+
## Vulnerability: Out‑of‑Bounds XOR
36+
37+
Inside the register instruction set, there is an instruction
38+
```0x41 <idx> <imm32>` is supposed to do `registers[idx] ^= imm32```
39+
40+
The issue is that the VM never checks whether `idx` is a valid register index. By giving a large enough index, we can XOR into memory well beyond the register array, which overlaps with the `heap_array`. By crafting values, we can change one of the heap metadata pointers stored there
41+
42+
---
43+
44+
## Exploit
45+
46+
### Step 1 – Making Heap Allocation
47+
Using stack‑arch syscall `6`, we allocate a heap block. The VM maps it at virtual address `0xA000`, and internally records the mapping in `heap_array[0]` - `{ real_heap_ptr, 0xA000 }`
48+
49+
---
50+
51+
### Step 2 – Corrupting Heap Metadata
52+
We switch to register‑arch and use the buggy XOR on a big index so we land inside and overwrite `heap_array.real_ptr`. Instead of pointing to the real heap, we flip it so it points to the `masm_struct` itself. So now whenever we use `0xA000`, we are actually accessing the VM’s internal state struct
53+
54+
---
55+
56+
### Step 3 – Leaking Information
57+
We use stack‑arch syscall 2 (fwrite) to dump memory starting from `0xA000`. But `0xA000` maps `masm_struct`, so this gives us a leak of critical VM internals i.e. the real memory address of the **data segment** (`seg1`), which we control. This is the RWX memory where shellcode is already stored
58+
59+
---
60+
61+
### Step 4 – Overwriting the get_flag Pointer
62+
We now use register‑arch syscall 1 (fread) to write into VM memory (at `0xA000 + 0x28`, which resolves to `&masm_struct->get_flag` due to our corruption). We overwrite that with the leaked `seg1` address. Now whenever the VM tries to call `get_flag`, it will instead jump to our data segment where we've placed our shellcode
63+
64+
---
65+
66+
### Step 5 – Running Shellcode
67+
Finally, we use stack‑arch syscall 5, which usually calls `get_flag`. But it now points to our RWX data segment, so the syscall runs our injected shellcode
68+
69+
---
70+
71+
## Overall Exploit Flow
72+
1. Allocate heap chunk at `0xA000`
73+
2. Use buggy XOR to poison heap_array so `0xA000` maps to `masm_struct`
74+
3. Dump (`fwrite`) `masm_struct` and find seg1 address
75+
4. Overwrite `get_flag` with that address
76+
5. Call `syscall 5` → shellcode runs
77+
78+
79+
**Credits**: 堇姬 Naup (https://naup.mygo.tw/2025/07/05/2025-Google-CTF-writeup/)

0 commit comments

Comments
 (0)