mp_.tcache_bins基本漏洞利用

Thursday, February 23, 2023
本文共1345字
3分钟阅读时长

⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/principle/mp_.tcache_bins%E5%9F%BA%E6%9C%AC%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/。商业转载请联系作者获得授权,非商业转载请注明出处!

It’s not what you look at that matters, it’s what you see. — Henry David Thoreau

什么是mp_结构体?

我们可以在identifier - Glibc source code (glibc-2.37) - Bootlin搜索mp_,会发现它是一个内部链接的全局变量,类型是malloc_par结构体:

文内图片

这说明它不会出现在libc的符号表中,需要通过gdb调试获取偏移:

文内图片

而通过它的名字和赋值我们也可以很清晰地猜测到,这个tcache_bins域是用来记录tcache bin的最大索引值的,超过这个索引值的堆块都不属于tcache bin。

这个索引值是通过堆块的大小计算的。之所以要计算这个索引值,是因为所有的bin存在一个大小为136的数组里面,tcache bin、large bin等都管理一定范围内的bin。通过大小计算这个索引可以很方便地判断一个堆块应属哪个bin

文内图片

文内图片

mp_.tcache_bins有什么用?

那么这个mp_.tcache_bins有什么用呢?

文内图片

可以看到,如果我们想要伪造一个大chunk,让它以tcache的方式被获取(这样就可以使用与tcache相关的攻击手段攻击),我们需要伪造以下两个值:

  1. mp_.tcache_bins足够大。可以通过unsorted bin attack(高版本不可用)或者large bin attack等方式向mp_.tcache_bins内写入一个足够大的值实现
  2. tcache->counts[tc_idx] > 0。

第二点如何实现呢?我们如何更改这个counts值呢?

因为tcache结构体是存放在堆上的(通常是第一个堆块),当然可以使用其他方式覆写counts值,但在这里其实可以利用tcache bin本身的漏洞。

文内图片

当mp_.tcache_bins足够大的时候,我们是可以将一个大chunk放进tcache bin中的,只是由于tcache分配在堆上,这个索引值又太大,所以这个大chunk的地址(链表头)会被放进相邻的chunk中

文内图片

文内图片

同时,tcache_get里也没有对越界的检查:

PS. 其中,if (__glibc_unlikely (!aligned_OK (e)))malloc_printerr ("malloc(): unaligned tcache chunk detected");这段代码的作用是检查从tcache中获取的内存块的地址是否按照指定的对齐方式对齐(__glibc_unlikely用于编译器优化),这些都和越界无关

文内图片

同时,tcache_put也允许我们放入一个大chunk,甚至链表头都是不加密的

文内图片

因此,如果我们可以控制相邻堆块,那么我们释放的大堆块所属的链表头也是可控的。

具体应该如何操作?

以上这一通操作能够实现的前提是mp_.tcache_bins通过unsorted bin attack或者large bin attack等方式增大

mp_.tcache_bins = mp_ + 0x50 = mp_ + 80,如果用large bin attack覆写,要写入的地址是mp_ + 0x30。large bin attack的具体解析见我的另一篇文章Largebin_Attack - P3troL1er 的个人博客

文内图片

然后我们要释放一个大小为N的大堆块p1进入tcache bin,然后记下它的地址(可以通过下断点到free那里查看,也可以通过heap等命令查看堆分布获取)

文内图片

然后通过x/150gx或者tele命令找到这个地址存放的位置和我们可控的相邻chunk头部的差值

文内图片

文内图片

这里感谢群里L0tus和h4kuy4等师傅的耐心指导,这三张图也是来自他们~

然后我们就可以将链表头地址覆盖为__free_hook等,实现getshell。

如果有什么问题或者建议,欢迎在评论区评论指出~