没有输出函数的情况下magic_gadget的利用
什么是magic_gadget
magic gadget其实是一类gadget的统称,指可以巧妙地实现某些目的的gadget。这里要讲的gadget只是众多magic gadget中的其中一个,可以在没法泄露libc地址的时候达到能够使用libc地址的目的。
这里要讲的gadget位于程序的__do_global_dtors_aux
函数中,偏移是0x18。
1 | add [rbp-3Dh], ebx |
在IDA正常反编译下是看不到这个gadget的,只有重新在0x18处反汇编才能看到。显然,这个gadget可以实现在某一个栈上数据加上一个偏移。假如在rbp-0x3D处存在一个libc地址,并且这个地址每次运行都是一样的(相对偏移),那么我就可以通过控制ebx寄存器,使用这个gadget把那个libc地址变成ogg或者system,从而返回获得一个shell。
但是很显然,rbp-0x3D很难直接是一个返回地址,就算是被调用者的返回地址,也会被覆盖。
如果能知道栈地址,劫持rbp进行栈迁移也许是个不错的选择,但是本来就是在没法泄露地址的情况(一般是连输出函数都没有但是又有栈溢出的情况),所以这个压根没机会。比较常见的使用方法是:
- 如果没有开full relro,劫持rbp到got表,修改got表后进行rop。
- 如果没有开地址随机化,可以栈迁移到bss段,然后调用libc_start_main在bss段上留下libc地址,然后再劫持rbp到那个地址的相应偏移处(依然在bss),修改某个libc地址为ogg或者system后ROP。
2024 BaseCTF ezstack
这道题,爱来自gets师傅(
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
程序除了一个gets函数之外就什么都没有了。没有输出函数。靶机环境是2.35的,但是程序里出现了csu,这应该是出题人故意留的gadget(爱来自gets),刚好符合上面讲到的无输出函数的情况。我们来详细分析一下这题的做题步骤。
首先检查程序的保护情况,发现PIE没开,partial relro,所以优先考虑劫持got表。got表中可以供我们选择的函数并不多,gets函数我们还需要用它来传payload和binsh字符串,所以我们劫持setvbuf这个函数为system。
已经确定要使用add [rbp-3Dh], ebx
这个gadget,先看我们需要控制什么寄存器。首先rbp需要劫持为setvbuf的got表地址+0x3D,ebx应该存setvbuf与system两个函数在libc中的偏移,这样我们就能通过add将setvbuf的got表指向system。
我们利用ret2csu来控制寄存器,实际上我们只需要其中一段就够了
1 | .text:00000000004006EA pop rbx |
前面两个就已经能够控制rbx和rbp了,其他都无所谓。于是写出脚本:
1 | xor_off = (-0x30880) & 0xffffffffffffffff #setvbuf和system之间的偏移,注意符号 |
buf_address处实际上写什么都无所谓。关于偏移的计算,可以手动在libc中查找后计算,也可以借助pwndbg来进行计算
1 | p setvbuf |
设置好寄存器之后就执行magic gadget。然后这时候setvbuf的got表就已经变成了system了,我们断点动调看看。
确实劫持成功了。那接下来就是正常的ROP了,我们先利用gets函数把binsh写进到bss段,然后再传参执行system即可。
EXP
1 | from pwn import * |