0CTF/TCTF 2022 RisingStar Final 无中生有
题目附件:无中生有_88ca11dade60f8c3dd26022c61439164.tzst
题目分析
通过分析附件中的 server.py 和 launcher 文件,了解到该题需要我们构造一个ELF可执行文件,其中:
- ELF头部有一些限制
- 限制了进程可使用的系统调用
- 需要将flag输出到stdout:orw
- 以137号exit code退出:exit(137)
然后,重点分析下前两个限制条件。
限制1
限制1 - 对ELF头部的检查在server.py中,主要有:
- 不允许在构造的ELF文件中存在
syscall
或int 0x80
两个指令(b’\xcd\x80’ or b’\x0f\x05’)。 - 不允许在页的边界处存在可能组成以上两个指令的字符(b’\xcd’ or b’\x80’ or b’\x0f’ or b’\x05’)。
- ELF文件类型需要是“可执行文件”或“动态链接库文件”,ELF版本号为1,e_ehsize必须是0x40,e_phoff必须是0x40,e_phnum必须大于0且小于100。
- ELF中每一个段的p_filesz和p_memsz都必须小于0x10000,ELF要求必须是静态链接的,不允许存在即可写又可执行的segment。
限制2
限制2 - 执行该ELF前,launcher内会设置如下seccomp规则:
系统调用做了64位和32位的区分,因此为拿到flag,我们既要构造 syscall
指令,也要构造 int 0x80
指令。思路如下:
- syscall指令:vdso中有syscall指令。但如何获得vdso的基址呢?vdso的基地址可以通过auxv(ELF auxilary vector)数组获得;auxv数组存放在栈的底部,它对应的结构体是struct Elf32_auxv_t;vdso基址在该结构体中的偏移是AT_SYSINFO_EHDR。因此我们可以通过rsp在栈中搜索vdso基址。【参考:About ELF Auxiliary Vectors】
- int 0x80:由于题目限制了64位的ELF,所以在进程空间内原本是没有32位指令的,所以需要构造。如何构造呢?利用64位下的mmap申请一段rwx内存,分次将b’\xcd\x80’写入,即可获得调用32位系统调用的能力。
限制1和2绕过后,之后的两步就是写写shellcode的工作了。
利用步骤
为了清晰起见,将利用步骤分开了,没有合并成一个脚本。先在本地生成一个elf文件,改完ELF头后,通过第二个脚本打远程获取flag。
生成elf文件部分
1 | from pwn import * |
注意:以上生成的ELF文件中PT_LOAD和PT_GNU_STACK的权限都是rwx,可通过010editor等工具将PT_LOAD改成rx,将PT_GNU_STACK改成rw。
打远程部分
1 | from pwn import * |
由于是赛后做出来的题,没机会在远程环境上尝试打一遍,所以不确定脚本能否在远程题目环境中打通。不过,在本地调试中,证明以上利用思路和代码是没有问题的。
除了这道题外,紧接着的 0CTF/TCTF 2023 中还有两道进阶题:
感兴趣的可以做做,目前只找到一篇nothing的wp:Nothing is True