3.1 KiB
CoPaaS — Compiler-oriented Programming as a Service
A startup offers an emulation service with a JIT engine for a custom instruction set. The ISA is small and has no jumps, branches, or function calls — so it is really just a glorified pocket calculator. You hand it a program, it compiles that program to native x86-64 at runtime, runs it, and tells you the result.
In the free tier, every program runs inside a sandbox. Pay for the premium tier and the sandbox comes off — the startup trusts its paying customers.
How it works
On connect you may activate premium mode, then you enter a loop: submit a program, the service JIT-compiles and runs it, and prints the result. Repeat as often as you like.
Each round:
- You are asked for a program length
n(1 ≤ n ≤ 0x1000). - You send
ninstructions as raw fixed-size records (see format below). - The service validates the program, compiles it to x86-64, and executes it.
- It prints
Your program exited with <code>!, where<code>is the result.
The machine
- Registers (14): named
A–N. All registers are zeroed before your program runs. - Result: the program's exit code is the low 8 bits of register
A. - No control flow: there are no jumps, branches, or calls.
Instructions
Each instruction is a fixed-size record with the fields:
opcode | reg1 | (padding) | reg2_or_immediate
reg2_or_immediate is interpreted as a second register for register-form
instructions, or as a 32-bit immediate for immediate-form instructions.
| Opcode | Form | Effect |
|---|---|---|
LOADI |
reg1, imm |
reg1 = imm |
ADDI |
reg1, imm |
reg1 += imm |
ADD |
reg1, reg2 |
reg1 += reg2 |
SUB |
reg1, reg2 |
reg1 -= reg2 |
COPY |
reg1, reg2 |
reg1 = reg2 |
Programs are validated before compilation: any out-of-range opcode or register
is rejected and the round is discarded. The compiler also applies small
optimizations — consecutive ADDIs to the same register are folded together,
redundant consecutive LOADIs collapse to one, and a COPY of a register onto
itself is dropped.
Execution model
- Free / demo tier: the compiled code runs in a forked child under a
seccomp-BPF sandbox that permits only
read,write,execve, andexit_group. The child runs on a fresh stack at a randomized address, andstdin,stdout, andstderrare all closed first — so the only information that leaves the child is its exit code (the low byte ofA). - Premium tier: unlocked by supplying the correct activation key. Compiled code then runs in-process with no sandbox.
Mitigations
Full RELRO · Stack Canary · NX · PIE
Goal
Break out of the calculator and run code of your choosing — capture the flag via
/bin/get_flag (or a shell). The interesting surface is the JIT: what the code
generator emits for your instructions is not always what you'd expect.