浅谈一下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)
然后绕过检测机制(偏移是-0x40因为头部预留0x10所以构造如下:0xfbad1800是改flag位,最后是00截断)
即可泄漏出栈地址),再通过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头部的检查)。
也可以用通过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条件)
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、要泄漏地址就需要地址残留,这里两种方式,第一种通过循环创建队块,填满大小0xa0的tcache7个,然后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
(2)因为我们tcache结构体是0x251,只有2.27和2.31的libc版本才有,这里面管理者里面结构体的数量contents,指向的第一个指针等等
(3)泄漏堆地址
这里因为3号堆块fd链接的2号堆块show出来之后就是堆的地址
然后算出堆的基地址
然后我们申请一个110的堆块然后劫持到heapbase+0x10位置(因为低版本的tcache不检测头部,所以这里直接修改fd就过去了)
然后申请出来,再修改contents全部为7
然后再修改一下,这个时候就劫持到freehook地址
要劫持0x30,就移动下即可,说明在这0x40之前是contents数量,在这之后是指向的第一个指针
改system
然后我们释放一个带binsh的堆块即可
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
PROTECT_PTR,加入了一个safelink机制,检测了申请地址是否以0x10对齐,fastbin attack的利用办法受到限制,例如经典的通过错位构造”x7f”劫持malloc_hook和IO_FILE的利用办法。
这个宏定义就是第一个参数右移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
libc
2.35
相关的虚表已经不可以写了
原文始发于微信公众号(由由学习吧):各版本下libc检测机制和堆利用
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论