ciscn&&ccb半决赛复现
typo
ubuntu20.04,glibc2.31
题目分析
题目中用到的结构体:
其他函数就不看了,看一下有漏洞的函数(edit):
可以看到在 snprintf((char *)chunklist[v1], (size_t)"%lu", s, 8LL);
存在问题
snprintf
的函数原型为:int snprintf(char *s, size_t maxlen, const char *format, ...)
题目将 (size_t)"%lu"
(即它的地址)作为了maxlen
传入,format
为用户输入,于是这里同时存在格式化字符串漏洞和堆溢出漏洞
利用方法
由于没有 show,没有什么直观的方法可以泄露 libc_base,不过见过的小伙伴一定可以第一实际想到用 _IO_2_1_stdout_ 进行泄露,在下文中,我也是这样实现的。
具体的利用步骤如下:
- 利用堆溢出构造 chunk overlapping,接下来相当于可以实现double free(有的说是UAF,这里称呼为 A,B,并且 A,B 实际上为同一个 chunk)。
- 先将 A 放入 tcahebin,用堆溢出修改这个 chunk 大小,再放入 unsortedbin 中,这个时候,tcahebin 就残留了 main_arena 附近的地址。
- 再用堆溢出部分覆盖原 main_arena+96 地址为 _IO_2_1_stdout_ 附近地址,接下来就修改 _flag 为
0xFBAD1800
, 将_IO_write_base 的地址末尾改为 00,即可泄露 libc。 - 有了 libc_base,后面就简单了,这里我改 __free_hook 为 system,接下来 getshell。
1 | #!/usr/bin/env python3 |
1 | [*] Switching to interactive mode |
exp分析
exp思路理解分析:
- 这道题没有show()函数就不能像正常那样泄露地址,所以我们要劫持stdout
- 劫持stdout也需要libc地址,那么我们就需要将libc地址想办法布置到tcachebins中的next指针(或者其它)
- 存在堆溢出,可以通过overlapping来制造doubel free(同一个chunk即在tcache中,又在unsortedbin中),从而实现申请一个堆块在stdout附近
- 最后,通过one_gadget等方式来getshell
unlink 合并到 top chunk
unlink 合并到 top chunk(overlapping)
1 | # 1. unlink 合并到 top chunk (overlapping) |
我们的目标是让chunk 9实现double free
这里通过堆溢出制造overlapping_chunk9将chunk8 - 10都放入top chunk中
构造同一个 chunk 的 UAF
构造同一个 chunk 的 UAF(两大小不同的堆块控制)
1 | # 2. 构造同一个 chunk 的 UAF (两大小不同的堆块控制) |
这里就是再将top chunk中的chunk 9再次申请出来,我们通过一给chunk就控制了chunklist中两个chunk
1 | pwndbg> x/24gx 0x555555554000+0x4060 |
double free
将一个放入 tcahebin ,一个放入 unsortedbin 中, 让 tcahebin->next 为 libc 相关
1 | add(13,0x50)#这个是为了让tcachebins中0x60处next指针有效(即有两个以上的chunk) |
我们直接进行delete(9)和delete(10)一样也会被检测出来double free;
所以可以通过修改chunk 9和chunk 10的大小,再进行free,是他们一个进入tcachebins中,一个进入unsortedbins中。
这也就实现了我们的目的,在tcahebins中的那个chunk的next指针上留下了libc地址。
1 | tcachebins |
hijack stdout
1 | # # 4. 测试, 爆破 _IO_2_1_stdout_ 的地址,成功概率 1/16 |
这里为什么是0xa5?
这里的0xa5恰好可以将下面的chunk8的old_chunk_size覆盖为一个不大不小的数 0x0000006161616161 太大了会出问题(如果全覆盖的话read好像字节数太多了读取不了)
会导致这里的\x90\x26无法写入
需要把chunk8的人工size填充为一个大小合理的值,否则太大了read读取不了,太小了又不够覆盖
1 | edit(12, b'a'*0xa5, b'\x00') |
篡改了chunk 8的old_chunk_size,是他可以直接写入大量数据
1 | edit(8, b'11111', b'b'*0x58+b'\x90\x26') |
这个是改了chunk 9的next指针低位,使它概率指向 stdout - 0x10 的地方
申请出 stdout - 0x10 的地方,改 stdout 的_flag
为 0xfbad1800,和 write_base_ptr 的低位为 00,实现泄露出libc地址
get shell
1 | # 5.getshell |
用上前面申请的0x40的3个 chunk 改 free_hook 为 one_gadget 或改 free_hook 为 system_addr 来 get shell
总结
unlink还可以这样用,有点类似house of botcake…