LiacCTF2026-bytezoo(Revenge)
题目简介
Liac 2026的一道shellcode题,之前没见过这种shellcode限制,对着Wp复现出来的
程序分析
漏洞点很直接,输入并执行代码,需要注意的有以下几点:
- 在代码页的最后两个字节对应syscall指令
- 限制策略没遇到过——每个字节使用次数不能超过“2个nibble(半字节)中最小的那个”,如图:
/validate.png)
比如0x48这个字节只允许在shellcode里出现4次,0x1f这个字节只允许出现1次…… - 在执行之前会取消执行页的写入权限:
/unexec.png)
- 程序开了沙箱,禁用了execve系列,只能orw:
/sandbox.png)
程序最后会清空绝大部分寄存器,并且将最开始mmap的一块rw页作为栈空间,然后执行输入的字节:/reset-reg.png)
漏洞利用
寄存器部分运算
首先由于指令对齐的存在,我们需要尽量避免对rax,rdi这样的64位长寄存器直接进行操作,这样会导致大量为了对齐产生的0x00
恰好寄存器存在低位部分操作的别名,在shellcode中可以利用类似ebx、bh、bl这样的低位进行部分操作
这种操作甚至可以实现+一个2的整数次幂的效果且尽可能的不引入0字节
比如像下面这样,可以实现+0x300,但相比直接的add不会引入0字节
1 | |
fs寄存器泄露ELF基地址
通过部分运算的思路,可用的指令数量大幅提高
但是,由于syscall指令对应0x05 0x0f,因此在输入中仍然无法使用任何系统调用
经过Wp的提示,针对这种情况可以使用PLT表已有的函数完成对应的syscall功能,常见的如glibc的read——>系统调用read、glibc的write——>系统调用write
剩下的就是定位PLT表(或者GOT表),也就是泄露一个elf的地址,而Wp提到fs寄存器附近存在一个栈地址,而且这个栈地址附近存在一个main函数地址(这里需要注意的是,这个栈上残留的main函数地址必须是在docker环境下才能看到)/fs-leak.png)
有了main函数地址之后就很好办了,可以获取任意PLT表地址
read+mprotect二次读
有了ELF地址之后,按照:
- 调用mprotect@plt赋予代码页写权限
- 调用read@plt二次读
即可完成orw
最终exp
完整的exp如下:
1 | |
LiacCTF2026-bytezoo(Revenge)
http://0x4a-210.github.io/2026/02/14/pwn刷题记录/比赛题解/LiacCTF2026-bytezoo(Revenge)/