PWNABLE.TW-De-ASLR-WP

Posted on Mar 30, 2021

这个 ROP 非常的麻烦

分析

流程就两句话

看起来似乎非常的简单,就是一个 gets 造成的无限溢出。但是并没有任何输出函数,所以 leak 非常困难。

思路

没有任何的 libc 地址,想要 leak 只能用栈上的残留数据,程序没有开启 PIE,所以我们可以通过栈迁移将栈迁移至地址固定的段上,比如进程末尾的可读可写页。然后在此处执行一个 gets,就可以把一些 libc 的地址留在这里了。

调试一下就可以发现会残留一个与 gets 有固定偏移的地址,这个时候有一个自然的想法,由于 puts 和 gets 相距甚近,那我们可以直接把残留的地址部分覆写,就可以获得一个 puts 的地址,就可以 leak 了。但是 gets 会在末尾补 ‘\x00’,本来只需要爆破 4 位,现在就需要爆破 12 位了,这种解法的 exp 我写了,爆破了一个小时没跑出来,放弃了。

后来换用一个更好的爆破方法,通过 ROPgadget 可以查到这个神奇的 gadget

也就是 add ebx, esi ; ret,我们把残留的地址 pop 到 rbx 里面(可以利用 csu),提前算好偏移,一加就可以获得一个 one_gadget 的地址,就可以 getshell 了,当然这里会碰到一个问题,就是这个 gadget 会自动将 ebx 的高 32 位清零,所以最后还是需要爆破 8 位。不过二百五十六分之一的概率也尚可接受了。

关于这一点,引用 intel 的手册中的说明

Because the upper 32 bits of 64-bit general-purpose registers are undefined in 32-bit modes, the upper 32 bits of any general-purpose register are not preserved when switching from 64-bit mode to a 32-bit mode (to protected mode or compatibility mode). Software must not depend on these bits to maintain a value after a 64-bit to 32-bit mode switch.

大概意思就是由于通用寄存器的高 32 位在 32 位中没有定义,所以对所有的通用寄存器进行 32 位模式操作时都会造成高 32 位的清零。(说是通用寄存器,实际上 rsp 也一样)

为了将 rbx 放回到栈里,仍然可以用 csu 中的 gadget,同时下面的 jz 不需要特别担心,在进入前把 rbp 和 r12 置零就可以实现跳转。

总结

最后的思路就是

  1. 栈迁移到固定可读写页
  2. 在该页上执行 gets
  3. 通过 csu 把 gets 残留的地址 pop 出来
  4. 通过 add ebx, esi ; ret 的 gadget 把 ebx 改成 one_gadget 的低四字节
  5. 通过 csu 把 rbx push 到栈里
  6. 通过 gets 把 push 进去的 one_gadget 地址补全(补两个字节,由于最高位恒定为 0x7F,所以这里只需要爆破 8 位)
  7. 通过 csu call one_gadget 成功 getshell

这样看起来也挺简单的,但是调试和各种地址的布置还是有些麻烦,题目也是值这 500 分的。


exp 建议自己写一下,爆破应该多试试,我爆破了 1000 次左右才出来

exp

这应该属于高分题了,完整 exp 是不能公开的,所以就不放了。当然如果你非常需要,也可以通过联系我来取得。