ret2csu
使用条件:
在64位程序中,我们往往要控制各种函数来执行一些我们想要执行的操作,而控制函数程序的前六个参数是通过控制寄存器来控制的,(分别是rdi,rsi,rdx,rcx,r8,r9),在小于三个参数的时候,我们往往可以通过ROPgadget来找到寄存器的gadgets,但当参数到达三个,我们就很难找到全部寄存器相应的gadgets。
但我们有时又无可避免的需要控制其他几个寄存器中的参数,这我们就需要用到ret2csu。
__libc_csu_init函数:
我们可以找到一个名为 __libc_csu_init的函数,这是一个对libc库进行初始化的函数,而基本上每个程序都需要用到libc库中的函数,所以基本每个程序都会有这么一个函数。
而这个函数里面,我们可以看到大概是这个样子
我们先分别看看我们所能控制的寄存器,分别有rbx,rbp,r12,r13,r14,r15。
再来看看我们通过这几个寄存器又可以来干些什么。
可以发现,我们能分别控制r14,r13,r12来控制rdx,rsi,edi寄存器(这里虽然我们控制的是edi寄存器的值,但因为rdi寄存器的高32位都是0,所以我们其实可以控制rdi的值,只不过只能是低32位)。控制r15和rbx的值来使用的我们想用的函数。
除此之外,我们需要注意rbx和rbp的值,来确保程序能够正常的运行。
具体的实现:
1.从0x4012EA开始,会pop出来rbx等寄存器的值;而上边的0x4012D0又会将r14,r13,r12等值分别赋给rdx,rsi,rdi。所以我们想到可以构造一段栈上数据将想要设置的rdi,rsi,rdx的值分别给r12,r13,r14(这里需要注意一下,可能每个程序的__libc_csu_init函数会略微有些区别,依照具体情况而定);直接跳转到0x4012EA处,来控制rbx等寄存器的值。之后再跳转到0x4012D0处从而控制rdx,rsi和rdi。
2.根据0x4012D9处的call指令,我们可以得知,我们所要控制的函数地址即为r15+rbx*8。此外下面还要控制rbx+1=rbp,这样才能保证程序能够继续正常的执行下去。(所以我们通常为了方便起见,会把rbx设置为0,把rbp设置成1,这样在满足我们的条件下,我们只需要将r15设置成我们需要控制的函数的地址即可。)
3.在控制完我们所需要的函数并判断完rbx和rbp的值后,我们继续下去,并不需要重新更改rbx等寄存器的值,所以我们可以填充一段无用数据将其跳过到后面的ret语句。(这里一共有7句,每一句有8位,所以我们应该填充7x8=56位,即0x38)ret语句我们只需要返回到我们所需要返回到的函数里面即可。
总结一下每个寄存器里应该填充的是什么东西:
rbx:0(方便用于控制地址和比较)
rbp:1(方便用于比较)
r12:rdi寄存器的值
r13:rsi寄存器的值
r14:rdx寄存器的值
r15:所要使用的函数的地址
csu_end_addr = 0x4012EA
csu_str_addr = 0x4012D0
rbx = 0x0
rbp = 0x1
payload=b'a'*(0x??+8)+p64(csu_end_addr)
+p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)
+p64(function_addr)+p64(csu_str_addr)
+b'a'*(0x38)+p64(ret_addr)
ret2csu的多次利用:
1.在某些时候,我们需要调用csu但可能能够利用的字节长度不够用。我们可以考虑一下ret2csu的多次使用,很明显,ret2csu的的gadgets是分为两段的,那么只要在两次利用之间寄存器的值没有发生过变化,且漏洞能够重复触发,我们就可以分成多次来利用。
2.如果漏洞无法重复触发,我们可以尝试先提前控制rbx,rbp寄存器的值。
关于对ret2csu的一些思考:
这种方法能够在我们找不到前三个寄存器的情况下,帮助我们控制前三个寄存器的值。但仍然是没有办法控制后三个寄存器。而且大多数的情况下,rdi和rsi往往可以通过ROPgadgets来获得;rdx寄存器往往也不需要我们控制,或者说也可以通过ROPgadgets来得到。而且这种方法所需要的字节数并不少,分为多次利用条件又过于苛刻,并没有想象中那么的万能。
欢迎加入 Typecho 大家族
看的我热血沸腾啊https://www.ea55.com/