1 概述
Challenge - smallest (pwn 300) - 429 ichunqiu ctf 2017
http://2017429ctf.ichunqiu.com/competition/index
64位SROP很好的练习题。
2 程序分析
millionsky@ubuntu-16:~/tmp/smallest$ objdump -d smallest
smallest: 文件格式 elf64-x86-64
Disassembly of section .text:
00000000004000b0 <.text>: 4000b0: 48 31 c0 xor %rax,%rax 4000b3: ba 00 04 00 00 mov $0x400,%edx 4000b8: 48 89 e6 mov %rsp,%rsi 4000bb: 48 89 c7 mov %rax,%rdi 4000be: 0f 05 syscall 4000c0: c3 retq |
3 漏洞利用
3.1 关键点
1. 可以通过发送字节的数量控制read的返回值控制RAX
这里可以利用的系统调用为:
#define __NR_write 1
#define __NR_rt_sigreturn 15
2. 如何进行SROP
问题:sigreturn在64位syscall号为15,Signal Frame的大小位0xF8,明显比15要大,如何传输SignalFrame?
方案:两次read,第一次传输SignalFrame,第二次设置rax为15
第一次read时传输的数据格式为
Return Address or SYS_read
PlaceHolder for syscall
SignalFrame
第二次read时传输的数据格式为(总共15个字节)
syscall_addr
7个字节的填充
Syscall gadget
程序本身有,如果没有,vsyscall中有
3. SROP中RSP的设置
RSP必须设置为一个可写的地址。
栈中的某些数据是指向栈的指针,泄露这些数据可以得到栈的值或一个可写的地址。
l 通过argv[0]和envp获取栈所在的页
int main(int argc, char *argv[], char *envp[])
argv[0]是一个栈地址,指向的是程序的名称;
envp中保存的都是环境变量的地址,都位于栈中;
addr = leak() & 0xfffffffffffffff000
l 通过附加向量的AT_RANDOM/AT_PLATFORM获取RSP的值
附加向量中的AT_RANDOM指示栈中16字节随机数的地址。在栈中位于附加向量的后面,环境字符串的前面。可以通过这个地址计算栈中数据的地址。
AT_PLATFORM位于AT_RANDOM后面,同样可以计算RSP的值。
l SROP指向mprotect系统调用,将.text变为可写的
这样也可以得到一个可写的地址;
特别是EFL头中有程序的入口,通过它可以再次跳转到read gadget。
4. 如何泄露栈中的数据
通过read控制RAX的值为1(SYS_write),进而调用write系统调用。
3.2 思路1
1. Sigreturn
Read返回值控制EAX,设置为sigreturn的系统调用号0x0f
栈溢出,发送Signal Frame,执行sigreturn系统调用;
Sigreturn设置RSP指向ELF header中的entry point
2. Mprotect
sigreturn执行mprotect系统调用,将text段设置为RWX
3. read gadget
通过Entry point再次执行read gadget
4. Shellcode
read读取shellcode放入栈中,执行shellcode
3.3 思路2
1. Write泄露envp,获取栈地址
2. Sigreturn设置RSP为获取的地址,RIP设置为read gadget
3. Read gadget发送Signal Frame和/bin/sh
4. Sigreturn执行execve系统调用
3.4 思路3
1. Write泄露auxv AT_RANDOM/AT_PLATFORM,获取栈地址
2. Read gadget发送Signal Frame和/bin/sh
3. Sigreturn执行execve系统调用
4 EXP1
1. Sigreturn&mprotect&设置RSP
Sigreturn设置RSP指向ELF header中的entry point
RIP: 400c0(sys_read(pg)执行完毕) |
||
+1 |
Return addr |
|
+2 |
PlaceHolder |
'A' * 8 |
+3 |
Signal Frame |
|
+4 |
'\n' |
|
RIP: 400c0(sys_read(1)执行完毕) |
||
+2 |
Return addr |
sigreturn |
+3 |
Signal Frame |
前7个字节被覆盖为6个\x11和1个\n |
+4 |
'\n' |
|
2. 通过Entry point再次执行read gadget
RIP: 400c0(sys_sigreturn&mprotect执行完毕) |
||
N+0 |
Return addr |
|
N+1 |
|
|
3. 传输并执行shellcode
RIP: 400c0(sys_read(2)执行完毕) |
||
N+1 |
Return addr |
0x400028 |
N+1 |
shellcode |
|
|
0x0a |
|
5 EXP2
代码来自http://anciety.cn/2017/04/21/2017429ctf-smallest-writeup/
见附件
1. 泄露argv[0]
RIP: 400c0(sys_read(pg)执行完毕) |
||
+0 |
Return addr |