BUUCTF-ciscn_2019_s_3
常规check,64位程序,开了栈堆不可执行

进去main函数发现它只是调用了vul,进vul看看

两个系统调用,read和write,需要看汇编辽

相当于执行read(0,buf,0x400),又执行write(1,buf,0x30)。看一下buf的位置,发现距离rbp只有0x10大小,存在栈溢出
而且我们还注意到,vul函数开始时是push rbp;mov rbp,rsp;在调用结束后,却没有leave操作恢复栈,是以ret结束。ret就是pop eip,执行eip中存储的指令(栈顶)。也就是说在退出后,ebp和esp始终指向同一个位置。所以我们在填好垃圾数据后,如果再填入一个地址,它就会乖乖执行!这里偏移为0x10。
还在函数列表里看到gadgets,好奇怪,一般来说不会出现这个函数的,进去看看


这里又出现了两个系统调用,查看一下调用号stub_execve 的调用号 为 59(0x3B) stub_rt_sigreturn 的调用号 为 15(0x0F)。我采用execve(“/bin/sh”,0,0)来解这道题目。另一种方法回来补(太菜了现在只会一种)
执行execve需要给寄存器赋值,那大概栈的布局就是这样的:
$rax59
$rdi“/bin/sh”
$rsi0
$rdx0
syscall
1.给rax赋值59
上面说到发现gadgets,其中有给rax赋值为0x3b(也就是59),后面还跟了一个retn,我直接喊出题人大恩人,解我燃眉之急。
2.给rdi赋值/bin/sh
我们需要给rdi赋值为“/bin/sh”,但是查找后并没有,需要我们自己写入。我们想得到写入/bin/sh后,这一字符串的地址。
write函数的buf距离ebp只有0x10的大小,却可以打印0x30大小的内容,那必然会打印出0x20大小的栈上的内容。我们希望得到一个栈上的地址,我这里把它记作a,便于说明。不论a是个什么地址,我们都可以知道a和我们输入进去的东西的偏移,那么,当我们再在程序中输入/bin/sh后用write泄露那个我们找到的地址,再减去偏移就是/bin/sh的地址。

再解释一下,为什么是选择第二个红箭头处。首先打印函数(这里的write),永远只能泄露栈的内容,但是我们希望它泄露栈地址。看到红箭头处的内存单元指向的是一个地址,故选择。
计算偏移是0x118
3.给rdx赋0
其实是有两种方法,第一种就是公式化ret2scu。(待补充)
另一种其实也差不多,我的理解是简化版的ret2scu(没有完全利用)。这里写一下这个方法
我们看到上面的gadgets中设置很多寄存器的值,我们只要让rsi和rdx=0.
gadget1 gadget2
pop rbx 影响call[r12+rbx8] mov rdx, r13
pop rbp mov rsi, r14
pop r12 mov edi, r15d
pop r13 call qword ptr [r12+rbx8]
pop r14
pop r15
ret addr
gadget1可以将rbx,rbp,r13,r14,r15设置为0;我们要想一下r12,gadget2中有一步call的指令,如果我们将r12赋为move rax 59 这条指令的地址岂不是省去许多麻烦?而且把rdx赋为0,很好
4给rsi赋0
找到指令

完美打完收工!

浙公网安备 33010602011771号