ret2syscall
简介
ret2syscall,就是执行系统调用以达到 getshell 的目的。
选择利用的情况:没有system
后门函数并且开启了 NX 保护。
系统调用,指 用户程序 向 操作系统内核 请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。大多数系统交互式操作需求在内核态执行。如设备IO操作或者进程间通信。
系统调用通过中断命令调用,通过系统调用号来区分不同的系统函数。
一般 ret2syscall 中我们都是通过调用execve
系统调用获取 shell。
- 用途:在父进程中
fork
一个子进程,在子进程中调用exec
函数启动新的程序。
调用execve
就相当于调用system
函数,因为system
函数在 libc库 中是通过调用execve
来实现的。
它的调用过程是:
- 将系统调用的编号存入
ax
寄存器; - 函数参数存入其它通用寄存器;
- 触发中断。
这是通用过程,但是 x86 和 x64 下的中断命令和寄存器是不同同的,系统调用号也是不同的。
中断
- x86:
int 80
- x64:
syscall
寄存器
- x86:
eax
- x64:
rax
所以我们需要对症下药。
Linux 下查看系统调用号:
- x86
1 |
|
- x64
1 |
|
x86
原理
Linux 的 32 位系统的中断通过int 0x80
实现。
调用系统调用的过程是:
- 把系统调用的编号存入
eax
。 - 第一个参数放入
ebx
- 第二个参数放入
ecx
- 第三个参数放入
edx
- 触发
int 0x80
号中断。
调用execve
就需要:
1 |
|
eax
存放系统调用号ebx
存放/bin/sh
地址ecx
置0edx
置0
之后通过将eip
指向int 0x80
的地址触发中断。
所以我们要找到pop eax
指令的地址和pop ebx
、pop ecx
、pop edx
的地址。最后找到一个int 0x80
指令的地址。
利用 ROPgadget 搜索相应的 gadget 的地址。
例题
来自于CTF-Wiki:ret2syscall
checksec查保护,发现程序为 32位 开启了 NX 保护
ida打开分析
发现危险函数gets
,很明显可以看出是一个栈溢出。
接下来我们寻找system
函数,但是没有发现system
函数。
调用execve
获取shell。
1 |
|
该程序是 32 位的,所以我们需要
- 系统调用号,即
eax
应该为0xb
。 - 第一个参数,即当前ebx应该指向
/bin/sh
的地址。 - 第二个参数,即
ecx
应该为0
。 - 第三个参数,即
edx
应该为0。
所以我们需要通过gadget控制这些寄存器的值。
搜索程序 gadget
控制eax
的gadget:
控制ebx
的gadget:
其中一段 gadget 可以控制三个传参寄存器:
加上前面控制eax
的gadget,我们已经控制了所有影响寄存器的 gadget。
接下来寻找字符串和中断地址
/bin/sh
字符串地址
触发中断的 gadget
pop_eax
覆盖为eip
,将栈顶的0xb
弹入eax
pop_edx_ecx_ebx
将第一个0
弹入edx
,然后将第二个0
弹入ecx
,之后将/bin/sh
字符串的地址弹入ebx
。- 最后,
eip
覆盖为int 0x80
地址触发中断。
通过这些 gadget 根据程序逻辑构造exp。
- exp
1 |
|
x64
原理
64 位与 32 位不同,需要注意以下几点
execve
系统调用号不同- 64 位系统调用号为
0x3b
- 64 位系统调用号为
- 存放系统调用号的寄存器不同
- 64 位系统调用号存放在
rax
- 64 位系统调用号存放在
- 存储参数的寄存器不同
- 64 位存储参数的寄存器为:
rdi
,rsi
,rdx
,r10
,r8
,r9
- 64 位存储参数的寄存器为:
- 中断不同
- 32 位为
int 80h
,64位为syscall
- 32 位为
例题
[CISCN 2023 初赛]烧烤摊儿
- 分析
checksec查保护,发现存在栈溢出保护和地址随机化保护。
执行一下程序,发现是一个购买烧烤的程序
ida打开分析
程序可以选则购买啤酒和烤串这样会减少余额,而如果余额超过一定数量则可以承包摊位。
如果承包摊位后就可以选择给摊位重命名。
详细分析程序发现两处整数溢出漏洞,分别为购买啤酒和购买烤串。
这里只介绍第一处
购买啤酒的程序通过输入数字来选择购买的啤酒,并且通过数字来选择购买的数量。
如果购买啤酒的数量的金额超过余额则输出钱不够了。
如果余额足够,则从余额中减去金额。
但是程序没有限制输入正数还是负数,如果输入负数则判断条件仍然会正确,经过计算后余额还会增加。
所以我们可以通过这个漏洞来增加余额达到足以承包烧烤摊的标准。
当承包烧烤摊后,我们便可以给烧烤摊改名字。
而给烧烤摊改名字的函数中存在栈溢出漏洞。
虽然程序开启了栈溢出,但是函数中没有检查canary的值,所以就相当于没开启。
正常开启栈溢出保护的程序,通过readfsqword检查canary的值
并且输入没有限制,之后将输入的内容复制到表示烧烤摊名称的全局变量中。
我们可以通过改名输入/bin/sh
到栈中,改名称的时候会将/bin/sh
复制到全局变量。
这样我们就可以得到/bin/sh
的地址。
并且在/bin/sh
后面接上一个\x00
字符串结束符,用于截取/bin/sh
。
之后在其后拼接足够栈溢出的字符,通过栈溢出我们可以通过 gadget 实现系统调用。
- 程序返回执行
pop_rax_rdx_rcx
,将0xb
弹入rax
,将0
弹入rdx
,将0
弹入rcx
。 pop_rdi
将字符串地址弹入rdi
。pop_rsi
将0
弹入rsi
。- 之后
rip
的值覆盖为syscall
地址,触发中断。
- exp
1 |
|