【PWN】ret2backdoor by Jmp.Cliff
你好,请加入Jmp.Cliff教
鹅鹅鹅好乱的一篇…
检查
checksec

啊,不认识的项,学习
Intel CET
控制流强制技术 (Controlflow Enforcement Technology, CET)
https://pwnerik.cn/index.php/archives/19/
https://linuxkernel.org.cn/doc/html/latest/arch/x86/shstk.html
SHSTK
SHSTK: SHadow STacK, 阴影栈。
SHSTK 是一种后向控制流完整性(Backward-edge CFI)检查(简单来说是在“返回”时的检查)。
影子堆栈是从内存中分配的二级堆栈,应用程序无法直接修改。执行 CALL 指令时,处理器会将返回地址推送到普通堆栈和影子堆栈。函数返回时,处理器会弹出影子堆栈副本并将其与普通堆栈副本进行比较。如果两者不同,处理器会引发控制保护故障。
所以所有ROP攻击都会失效
IBT
IBT: Indirect Branch Tracking, 间接分支追踪。
IBT 是一种前向控制流完整性(Forward-edge CFI)检查(简单来说是在“调用”时的检查)。
CPU 每当执行间接调用及跳转时都检查目标的指令是否为
endbr64/32,如果是就不执行任何操作(类似nop),如果不是就产生异常。
通过间接分支(call rax / jmp rax)跳到任意位置若目标没有 ENDBR,会被 IBT 拒绝
endbr64/32
endbr64(以及 32 位版本的 endbr32)是IBT机制的核心指令。作用:告诉 CPU“这里才是合法的间接跳转/调用目标”,否则就触发异常终止程序。
每个可能被间接调用的函数入口都会自动生成一条 endbr64
分析
main
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
vuln 有溢出
1 | __int64 vuln() |
backdoor
1 | int backdoor() |
endbr64长这样,新鲜

lock。全局变量,改不了

不明白,去问了思路。说是看backdoor的汇编,可以跳过对lock的检查(意思是跳过了lea rax, aBinSh ; "/bin/sh"也没事),直接到给command赋值的地方,command指针赋值是栈上地址,可以通过修改rbp值来设定command,所以可以想办法找一个/bin/sh贴上去。
backdoor汇编:
1 | .text:00000000004011E1 ; Attributes: bp-based frame |
也就是执行流是
1 | push rbp |
找一下/bin/sh,在0x402008有

分析一下command应该怎么改。command是从[rbp+command]处取的,也就是[rbp-10h]
这里是神的学弟的wp的解释:https://blog.akyuu.space/2023/09/24/NEUQCSA/NEUQCSA2023FirstGameWriteUp/
而且它读取的地址是 rbp-0x10. 因此我们可以把 shell 放在 rbp-0x10 的地址上面。
具体实现,发现汇编指令里面有两条指令
1
2
3 0x4011e5 push rbp
0x4011e6 mov rbp, rsp
0x4011e9 sub rsp, 0x10那么执行过这两条指令之后,rbp-0x10 就是 rsp。因此,我们可以把 shell 的地址放在栈里面的 rbp 上面,这样在 shell 会先在 rsp 上面,然后在被 mov 到 rbp 上面
非常好,我直到摸得差不多了才意识到这一点,都怪神误导我
调试
包含大量错误尝试。但是都是路嘛我也懒得整理了
算了还是别看了
不是,您怎么到这的。有这方法我还去改什么command

调到最后已经进system并且参数是binsh了,但是没对齐哈哈
刚进backdoor时,因为刚执行完push rbp; mov rbp, rsp,rbp和rsp相等,指向旧rbp

rsp减完变成0x7ffd27bfcea8,内容是buffer的40偏移处

执行完rax赋值,mov rax, qword ptr [rbp - 0x10],rbp - 0x10就是rsp那个地址,所以rax取了0x7ffd27bfcea8处的值,buffer中的kaaalaaa

但是这是怎么搞的呢。于是又去重新看了,buffer溢出的情况。输入用cyclic拉满先。断点下在了0x40124B,vuln里刚call完read回来的那条指令


继续


payload += p64(0x402008) # addr of /bin/sh -> rsp
payload += p64(0x402018) # rbp
payload += p64(0x401210) # ret
payload += p64(0x4011E9) # eip
试着加了个ret,结果呢也没对齐,而且rbp是0x68732f6e69622f,/bin/sh的字符串编码,很神奇了(哦,因为前面写错了

看看这个是怎么来的

rax变成存值了。当前的rbp就是0x402018,[rbp - 0x10]确实是/bin/sh的值,也确实

但是为什么加个ret会有这种变化。之前rbp存的是指向0x402018的地址,这会直接是0x402018了
通了我去
payload += p64(0x402008) # rbp
payload += p64(0x401210) # ret
payload += p64(0x4011E1)

此时其实是rsp -> 0x402008,而rsp=rbp-0x10,取rbp - 0x10的值也就是取rsp处的值0x402008。最后system参数就是0x402008。而且可以看到是对齐的。
这题要点是rsp处的值,而对比上面两种情况(嗯此处指的对齐和没对齐)可以发现,rsp不管怎样都会先和rbp一起指向0x402008,后面-0x10,向上长两格,所以rsp就会指向,在payload中离返回地址差两个地址处的值
对齐的情况,返回地址是0x4011E1,往前走两个就是0x402008,所以rsp指向0x402008
1 | payload = b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaa' |
没对齐的情况,返回地址是0x4011E1,往前走两个就是kaaalaaa,所以rsp指向kaaalaaa
1 | payload = b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaa' |
算了别管我在说什么
exp
1 | from pwn import * |