本文通过一道例题,详细解释了 ret2csu 漏洞原理及其有关的攻击方法。
思路
检查保护,开了NX保护

源码:
signed __int64 vuln() |
程序中有两个系统调用,read可栈溢出,程序中没有后门函数。但是我们看到了下面这个,3bh是64位程序execve的系统调用号,程序中有系统调用则肯定有syscall即我们可以利用gadgets布置execve的参数,然后syscall获得shell。

系统调用execve,我们必须要知道/bin/sh的地址。本题中,buf的长度为16个字节,而write打印0x30个字节,又因为buf在栈上,所以buf下面的一些栈中的数据也会被打印出来。
我们填满buf的16个字节,然后发送vuln函数的第一个指令的地址,执行流会重新执行一遍vuln函数。发送payload = b'/bin/sh\x00'+b'a'*8+p64(vuln_addr),然后看栈中的内容,其中0x7ffd3e0cdce8是栈地址同时也能被泄漏。

payload如下:
vuln_addr = 0x4004ed |
接下来就是传参,不过我们没有直接找到rdx相关的gadget地址,好吧,其实这就是道关于ret2csu类型的题。
下面是csu片段,我们就是要利用这两个片段,对寄存器进行各种操作,最后使rdx中的值为0(execve的第3个参数)。

csu②都是一些pop,重要的是怎么绕过csu①,首先我们要弄懂csu①中的汇编代码是什么意思。其中,call指令是调用函数,这里的[]是间接寻址,call [r12+rbx*8]这个指令的意思是调用一个函数,这个函数在哪,这个函数的地址在r12+rbx*8这个地址里装着。也就是说,如果r12和rbx的值分别为0x123456和0,0x123456又指向0x400123这个地址,那么我们要调用的函数的地址不是0x123456而是0x400123。
cmp指令用于比较两个操作数的值,cmp rbx,rbp指令的意思是比较rbx和rbp这两个寄存器的值。如果rbx和rbp的值不相等,那么jnz会跳转到相应的地址执行,反之执行流继续执行下一条指令。
payload如下:
csu1_addr = 0x400580 |
我们在ida中看到term_proc是一个空函数,其地址为0x4005b4,当然这个肯定不是我们想要的,我们想要的是哪个地址指向0x4005b4。

在gdb中,我们利用search -p 0x4005b4这个命令看到0x600e50这个地址中装的是0x4005b4。

一开始按照上面的payload发送一直打不通,调试的时候发现,调用完这个空函数后会ret到返回地址0x40058d也就是csu①中的add rbx, 1指令,后面会影响栈的有add rsp,8和6个pop指令,所以我们还需要再发送7*8个垃圾数据。


修改后payload如下:
csu1_addr = 0x400580 |
exp
from tools import * |
拿到flag

更换libc
本题中本地和远程的libc不一样,所以偏移量也不同,本题中本地的是0x148远程为0x118。我们可以利用pathelf和glibc-all-in-one更换本地依赖的libc。
首先用strings ./libc-2.23.so | grep "GNU"命令查看远程libc的GLIBC版本

然后在glibc-all-in-one中找到对应的版本号,64位程序对应amd64,32位程序对应i386

执行./download 2.23-0ubuntu11.3_amd64,便会在glibc-all-all-in-one目录下的libs目录中生成一个新目录

最后便可利用patchelf中--set-interpreter 和--add-needed选项分别设置链接器和依赖的路径即可。