
Solve start and get flage and give me writeup
I'll help you solve this CTF challenge. Let me start by setting up a task list and analyzing the binary.
todowrite
drbinary_run_sandbox
skill
drbinary_dump_data
read
read
read
todowrite
semantic_function_search
semantic_function_search
read
read
read
read
read
Now let me extract the VM bytecode and understand what it's doing:
drbinary_run_sandbox
grep
Let me extract the actual data arrays used for VM bytecode generation:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
Now I can see the data! Let me write a Python script to extract the flag:
drbinary_run_sandbox
Let me verify the flag by running the binary:
todowrite
drbinary_run_sandbox
todowrite
Solution: OrbitalVM (SpaceCTF)
Flag
SpaceCTF{vm_byp4ss_m4st3r}
Analysis
Binary Type: PE32+ executable (x86-64 Windows console application)
The challenge presents a custom Virtual Machine (VM) that validates a password before revealing the flag. The VM uses a stack-based architecture with its own instruction set.
VM Architecture
Through decompilation of the main function (0x140007b60), I identified the following VM components:
VM Opcodes:
| Opcode | Operation | Description |
|---|---|---|
| 0x00 | NOP | No operation |
| 0x01/0x08 | PUSH imm | Push immediate byte onto stack |
| 0x02 | LOAD idx | Load input[idx] onto stack |
| 0x03 | STORE idx | Pop stack and store to input[idx] |
| 0x04 | XOR | Pop two values, XOR them, push result |
| 0x05 | ADD | Pop two values, add them, push result |
| 0x06 | CMP | Pop two values, compare and set flag |
| 0x07 | JNZ offset | Jump if comparison was not equal |
| 0xFF | HALT | Exit VM with return code |
VM State:
- Stack:
auStack_258[64]with stack pointeruVar20 - Instruction Pointer:
uVar7 - Comparison Flag:
bVar27 - Input Buffer:
local_150[16](16 bytes after "SpaceCTF{")
Bytecode Analysis
The VM bytecode is dynamically generated at runtime from two data arrays in the .rdata section:
// Initialization loop (16 iterations)
do {
bVar1 = (&DAT_14000a1d0)[lVar17]; // Constants array
bVar2 = (&DAT_14000a1f0)[lVar17]; // Key array
// Build VM instruction (10 bytes each):
pbVar10[0] = 2; // LOAD input[i]
pbVar10[1] = lVar17; // index
pbVar10[2] = 1; // PUSH
pbVar10[3] = bVar1; // constant[i]
pbVar10[4] = 4; // XOR
pbVar10[5] = 1; // PUSH
pbVar10[6] = bVar2 ^ bVar1; // key[i] ^ constant[i]
pbVar10[7] = 6; // CMP
pbVar10[8] = 7; // JNZ (conditional jump)
pbVar10[9] = bVar15; // jump offset (decrements by 9)
} while (lVar17 != 0x10);
Data Arrays extracted from binary:
DAT_14000a1d0:ab 3c 77 f1 5d 22 99 4e b0 61 1a de 08 93 55 c7DAT_14000a1f0:76 6d 5f 62 79 70 34 73 73 5f 6d 34 73 74 33 72
The second array decodes to ASCII: vm_byp4ss_m4st3r
VM Logic Reversal
Each VM instruction performs this check:
LOAD input[i] ; Push input[i]
PUSH constant[i] ; Push constant
XOR ; input[i] ^ constant[i]
PUSH (key[i] ^ constant[i])
CMP ; Compare: (input[i] ^ const) == (key[i] ^ const)
JNZ error ; Jump to error if not equal
Mathematically:
input[i] ^ constant[i] == key[i] ^ constant[i]
Since XOR is reversible:
input[i] == key[i]
The VM simply checks if the input matches the key bytes directly!
Solution Script
# Data arrays from binary
constants = bytes([0xab, 0x3c, 0x77, 0xf1, 0x5d, 0x22, 0x99, 0x4e,
0xb0, 0x61, 0x1a, 0xde, 0x08, 0x93, 0x55, 0xc7])
key_bytes = bytes([0x76, 0x6d, 0x5f, 0x62, 0x79, 0x70, 0x34, 0x73,
0x73, 0x5f, 0x6d, 0x34, 0x73, 0x74, 0x33, 0x72])
# Extract flag
flag_content = key_bytes.decode('latin-1')
flag = f"SpaceCTF{{{flag_content}}}"
print(flag) # SpaceCTF{vm_byp4ss_m4st3r}
Verification
Running the binary with the extracted flag confirms correctness:
=========================================
OrbitalVM - SpaceCTF Challenge
Can you escape the VM?
=========================================
Enter the flag (format: SpaceCTF{...}):
[+] Correct! The flag is: SpaceCTF{vm_byp4ss_m4st3r}
Key Takeaways
- VM Analysis: The challenge implements a simple stack-based VM with basic arithmetic and control flow operations
- Bytecode Generation: The VM instructions are dynamically generated from data arrays rather than being hardcoded
- Crypto Simplification: Despite the XOR operations, the constraint simplifies to direct byte comparison due to the commutative property of XOR
- Static Analysis: Using Ghidra decompilation was sufficient to understand the VM logic without dynamic debugging
