

没有后门函数,也没有能用来泄露libc地址的东西

但是看到有execve和一些对寄存器的操作,考虑csu
在栈上写入/bin/sh,之后调用它
数据在栈的位置不是固定的,但是偏移是固定的
可以通过read和write来泄露栈上的地址
scu
泄露地址

buf的起始位置是0x7fffffffdd70,70到80之间是buf的16空间
栈上0x7fffffffdd90对应的数据是一个栈 上的地址所以选择读取这个位置的地址
并计算出它和buf之间的偏移以精准定位buf的头部
且
这个函数返回时直接ret,没有进行pop ebp的操作,所以在构造payload的时候不需要覆盖ebp
脚本
1 | payload=b"a"*16+p64(vuln) |
调用
64位时,设计后门函数需要rax=59(#define__NR_execve 59);rdi=”/bin/sh”;rsi=0;rdx=0;
rax=59地址已经找到了
ROPgadgets命令查看,发现只有对rdi和rsi的s操作
剩下的就是基本的csu了

1 | rbx_rbp_r12_r13_r14_r15 |
设置好rdx、rsi、rdi,然后调用 syscall
首先r14和r13一定要赋值为0,而且r15d的值
函数会跳转到r12+rbx*8,让它最后修改rdi然后执行调用syscall,所以r12要赋值为payload中的pop rdi的地方,rbx赋值0
且只调用一次
所以rbp=1
最终执行
1 | mov rax, 59 ; syscall number for execve |
脚本
1 | payload = b'/bin/sh\x00' + b'b'*8 + p64(pop6) |
注意
✅ mov edi, r15d和 rdi 的关系
1 | mov edi, r15d |
只看语法,是:
把 r15d的值(32 位)赋给 edi(也是 32 位)
但这里有个关键点:
在 x86-64 架构中,对 rdi 的低 32 位写入(edi)时,高 32 位会被清零!
🔍 举个例子
1 | mov rdi, 0x4141414141414141 |
执行完第二条指令后,rdi的值是:
1 | rdi == 0x0000000042424242 |
也就是说:
写
edi相当于先清空rdi,再写低 32 位
mov edi, r15d其实等价于:
1 | rdi = r15 & 0xffffffff |
所以你能用它来设置 rdi 的值前提是你设置的 r15 是一个 32 位以内的地址(或者值),比如 /bin/sh 的地址必须低于 0x100000000,否则会丢高位,导致 execve 崩溃或 syscall 参数错误
所以要之后再放设置rdi的值
脚本
1 | from pwn import * |
SROP

题目中也有sigframe
SROP 攻击的原理

- 内核保存上下文(步骤 ①):当程序接收到信号时,内核会暂停程序的执行,并自动将当前所有寄存器(
rax,rdi,rsi,rdx,rip,rsp等)的状态保存到一个特殊的结构体中,这个结构体叫做sigcontext。然后,内核将这个sigcontext压入栈中,为执行信号处理函数做准备。 - 攻击者伪造上下文:攻击者利用栈溢出漏洞,在程序接收信号之前,就向栈中写入一个伪造的
sigcontext结构体。这个伪造的结构体中包含了攻击者想要设置的所有寄存器值。例如,你可以把rip设置为execve的地址,把rdi设置为/bin/sh字符串的地址。 - 触发
sigreturn系统调用:攻击者通过构造 ROP 链,使程序执行流跳转到一个能够执行sigreturn系统调用的 gadget。sigreturn是一个特殊的系统调用,它的功能就是从栈中读取sigcontext结构体,并恢复寄存器状态。攻击者通常会找到一个mov rax, 0xf; syscall;这样的 gadget,其中0xf是sigreturn系统调用的编号。 - 内核恢复上下文(步骤 ③):当
syscall执行后,内核会发现rax的值为0xf,便会执行sigreturn。正如你所说,内核在执行这一步时不会去校验栈上数据的合法性。它会盲目地信任栈上的数据,从栈顶读取我们伪造的sigcontext结构体,并用里面的值来恢复所有的寄存器。 - 劫持程序执行流(步骤 ④):由于我们已经将伪造的
sigcontext中的rip设置为execve的地址,rdi设置为/bin/sh的地址,程序恢复寄存器后,会立即跳转到execve函数执行,并以/bin/sh作为参数,最终获得一个 Shell。
脚本
1 | from pwn import * |