各版本下libc检测机制和堆利用

admin 2022年6月27日12:55:29评论655 views字数 4580阅读15分16秒阅读模式
各版本下libc检测机制和堆利用

浅谈一下libc2.23,libc2.27,libc2.31,libc2.32,libc2.34,libc,2.35下的检测机制和堆利用手法


libc

2.23



2.23的UAF是比较经典的利用手法了,此时libc还没有引入tcache结构,仅仅通过fastbin来管理较小的chunk。

攻击方式:

1、在libc2.23下可以利用fastbin attack来攻击__malloc_hook来getshell。(一般来说2.23下打__malloc_hook较多,从稳定getshell上来看)具体步骤,是先通过申请一个属于unsorted bin大小的堆块(这时候fd指向的结构体是存在于栈上的,这时候可以找到栈地址),利用UAF+binary的show功能来泄露libc的基地址(没有show的话可以攻击stdout,未开启随机化地址的话找到25dd,开启随机化有16分之一爆破概率(5dd不变,爆破2这半个子节0-F)

各版本下libc检测机制和堆利用然后绕过检测机制(移是-0x40因为头部预留0x10所以构造如下0xfbad1800是改flag,最后是00截断

各版本下libc检测机制和堆利用

即可泄漏出栈地址),再通过uaf申请满足fastbin大小的chunk,并修改其fd指针,将__malloc_hook周围满足检查的地址链到fastbin中,再次申请相同大小的chunk即可将其取出,修改为one_gadget即可getshell。修改__malloc_hook的原因是在__libc_malloc中会先于分配过程检查__malloc_hook是否为空,若不为空则调用。__malloc_hook在首次malloc的时候会用作初始化相关的工作来使用,往后其值为0,因为在从fastbin中取chunk的过程中会检查size是否合法,所以要在__malloc_hook周围找出一块合法的地址,经验来说,在__malloc_hook – 0x23的位置处有一个合法的size位,可以用来伪造chunk(这一步是为了绕过fastbin头部的检查)。

各版本下libc检测机制和堆利用

也可以用通过realloc,对rsp进行微调,因为realloc源代码有rsp+8,就相当于正好对齐了八字节

add(3,0x60,b'A'*(0x13-8) + p64(one) + p64(realloc+0))

2、就是将one_gadget地址选成rsp+50的地址,free两次然后触发double free报错就可以获得shell(原理是报错的时候正好满足了rsp+50正好满足one_gadget条件)

各版本下libc检测机制和堆利用


3、劫持bss段的heap数组,劫持为freehook,稳定getshell。

4、在relro没开启的时候去劫持got表。


libc

2.27



libc2.27在更新后,malloc源码发生了变化,引入了tcache, tcache bin和fastbin的管理方式很像,都采用FILO的单链表(理解为数据结构中的栈),但是tcache的优先级更高,并且在bin中,fastbin的fd指针指向上一个chunk的头部,而tcache会指向上一个chunk的数据部分。旧版libc2.27中,tcache结构体没有引入key指针,可以随意double free,基本上是指哪儿打哪儿,在UAF下,使得利用手法更为容易,并且在分配的过程中没有对size进行检查。

攻击方式:

1、要泄漏地址就需要地址残留,这里两种方式,第一种通过循环创建队块,填满大小0xa0tcache7,然后free,这时候再申请同样大小但是大于fastbin的0x80大小就可以进入unsrtedbin

add(0,0xa0,'AAAA')

for i in range(7):

        add(i+1,0xa0,'AAAA')

for i in range(7):

        free(i+1)

free(0)

show(0)

第二种是请大于0x410,直接进入unsortedbin中

add(0,0x420,'AAAA')

add(1,0x420,'AAAA')

free(0)

show(0)

2、打tcache结构体

add(0,0x90,'AAAA')

add(1,0x90,'AAAA')

add(2,0x90,'AAAA')

free(0)

free(1)

show(1)

io.recvline()

leak = u64(io.recv(8))

heap_addr = leak - 0x1270

success(hex(heap_addr))

success(hex(leak))

edit(1,p64(heap_addr+0x10))

add(1,0x90,'/bin/shx00')

add(3,0x90,'x07'*0x40)#改变结构体,让系统误认为用满了所有的资源

add(4,0x100,'AAAA')

add(5,0x100,'AAAA')

free(4)

show(4)

改tcache结构体,因为他的结构体也在堆里面了,改完后让系统默认装满了7个


意:tacahe不检查头部,是直接在分配的地址上写入的,所以就直接写在了fd的上面,tcache的fd指向是在userdata区域,所以和之前fastbin attack不同(直接使用system_add地址而不是system_add-0x10地址)

在这个版本下稳定getshell一般打__free_hook

打结构体需要注意需要用到堆地址:

用一道题来演示一下:

(1)泄漏libc

各版本下libc检测机制和堆利用

(2)因为我们tcache结构体是0x251,只有2.27和2.31的libc版本才有,这里面管理者里面结构体的数量contents,指向的第一个指针等等

各版本下libc检测机制和堆利用

(3)泄漏堆地址

各版本下libc检测机制和堆利用

这里因为3号堆块fd链接的2号堆块show出来之后就是堆的地址

各版本下libc检测机制和堆利用

然后算出堆的基地址

各版本下libc检测机制和堆利用

各版本下libc检测机制和堆利用

然后我们申请一个110的堆块然后劫持到heapbase+0x10位置(因为低版本的tcache不检测头部,所以这里直接修改fd就过去了)

各版本下libc检测机制和堆利用

然后申请出来,修改contents全部为7

各版本下libc检测机制和堆利用

然后修改一下,这个时候就劫持到freehook地址

各版本下libc检测机制和堆利用

要劫持0x30,就移动下即可,说明在这0x40之前是contents数量,在这之后是指向的第一个指针

各版本下libc检测机制和堆利用

改system

各版本下libc检测机制和堆利用

然后我们释放一个带binsh的堆块即可

各版本下libc检测机制和堆利用


libc

2.31



新版libc2.27和2.31类似,引入了新的内容key,如果key值等于tcache的地址,那么就进入tcache的链表,然后后移,判断当前堆块是否在链表中,如果在链表中,那么很显然就是double free了。绕过方法很简单,利用漏洞改掉key值即可,直接给干掉if判断了,就不会进入这个if分支了。 在UAF下的利用手法为首先填满tcache,然后申请unsorted bin大小的chunk,利用UAF泄露libc基址,最后通过修改tcache的指针轻松的将堆块申请到__free_hook,修改为system地址,然后free一个chunk,chunk的内容为”/bin/shx00”即可轻松getshell。

注意事项:counts在libc2.27中的数据结构是char一个字节,libc2.31被更新为uint16_t类型为2个字节了

typedef struct tcache_perthread_struct{  uint16_t counts[TCACHE_MAX_BINS];  tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;


当我们攻击tcache_perthread_struct时,很常见的一个做法就是来将其记录counts的区域全部覆盖填满,这样我们再次申请的chunk可逃逸出tcache,在libc2.27中counts[TCACHE_MAX_BINS]的类型为char,即在相应size的位置上记录数量的大小是一个字节,而在libc2.31中相应的类型为uint16_t,大小是两个字节,所以我们之前的payload通常是b"x07" * 0x40(从trcache_perthread_struct的数据区开始填充),在libc2.31中,payload需要改写成b"x07" * 0x80,因为大小多了一倍,也相应的需要增加padding。


libc

2.32




2.32引入了一个宏PROTECT_PTR,加入了一个safelink机制,检测了申请地址是否以0x10对齐,fastbin attack的利用办法受到限制,例如经典的通过错位构造”x7f”劫持malloc_hook和IO_FILE的利用办法。

各版本下libc检测机制和堆利用

这个宏定义就是第一个参数右移12位再和第二个参数做一次异或,将当前地址右移后与tcache链表尾部的地址做了一次异或再插入链表尾部。


与libc2.31做对比的话,libc2.31是tcache->entries[tc_idx] = e->next;

而libc2.32是tcache->entries[tc_idx] = REVEAL_PTR (e->next);

在UAF的场景下,我们可以直接用show即可泄露出e->next值,因为最初tcache链表是为空的,也就是说safe-linking机制只相当于用堆地址右移了12位,通过左移即可恢复出堆地址,从而泄露出堆的基址,泄露出堆地址以后就可以来伪造tcache的next位了,我们可以在free态的chunk中修改next为(&next)>>12 & __free_hook(因为我们泄露出堆基址所以可以轻松的获取到&next的值),这样调用完tcacheget之后就可以把_free_hook链入到可供我们申请的链表当中,即可覆写__free_hook来getshell。由于tcache链表头指针存储的是当前释放堆地址的值,即tcache->entries[tc_idx] = e,tcache_stashing_unlink_attack的办法依然有效,注意!注意!注意!只是针对表头,因为表头和0异或


libc

2.34



移除了几个hook符号:

__free_hook

__malloc_hook

__realloc_hook

__memalign_hook

__after_morecore_hook

常见的hook函数没了就没办法做了吗?no!方法:

1、泄漏环境变量去打栈地址

在libc中存在符号environ指向了&argv[argc + 1]的地址,这个地址保存在栈上,而environ地址可以通过libc偏移计算获得,所以如果条件允许通过泄漏environ的值获得栈地址,再通过计算并劫持栈返回地址提前布置好rop链,可以最终达到getshell的目的。

2、劫持exit

3、劫持got

4、打SROP

5、利用IO_vtable
在glibc-2.34中,可以看到vtable拥有可写权限,首先将_IO_DOALLOCATE覆盖成满足条件的one_gadget地址,然后再将stdout结构体中缓冲区指针清空,再次调用puts时getshell。

libc

2.35


相关的虚表已经不可以写了

1、攻击_rtld_global结构体中的link_map链表
2、打got
3、打栈


原文始发于微信公众号(由由学习吧):各版本下libc检测机制和堆利用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月27日12:55:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   各版本下libc检测机制和堆利用http://cn-sec.com/archives/1146599.html

发表评论

匿名网友 填写信息