Pwn With longjmp
前言
这个是 seccon-ctf-quals-2016 的一个题,利用方式还是挺特殊的记录一下。
题目链接
正文
首先看看程序的安全措施
|
|
开了 Full RELRO, 所以不能修改 got 表了。

分配了两个大内存,一个作为一个全局表,用于存放程序中用的结构体指针, 一个作为 jmpbuf, 用于在 longjmp 时跳转回来。
接下来就是 f 函数,程序主要的逻辑在这里面。
Add student

最多的可以创建 0x1d 个 student, 如果已经创建满了的话就 longjmp 返回到 main 函数结束程序。创建时会分配两个堆内存。
结构体类型大概为

分配第一个 student 时的 内存布局为

Name student

输入 id, 然后在 myclass 里面找到相应的地址,取出 name_ptr ,向里面写入内容,注意循环条件
|
|
我们可以写入 0x21 个字节,我们分配的内存为 0x20, 可以有 一字节的 溢出,不过这里我们不能控制 分配的大小以及 释放堆块, 无法使用 overlap-heap 利用。继续往下看。
Write memo

类似的操作,依旧可以溢出 memo 的 一个字节, 在 memo 后面存放的是 name_ptr 所以我们可以修改 name_ptr 的最低字节.
有一个小知识,如果内存分配的顺序大小不变,各个内存块相对于堆基地址的偏移是固定的,所以修改 name_ptr 的最低字节,我们可以使得 name_ptr 指向和 它距离较近的堆块。
这里的话直接修改为下一个堆块的 name_ptr 的地址, 然后利用 name student 就可以修改下一个堆块的 name_ptr,再利用后面的 Show Name 功能就可以实现 任意地址读写。


以后通过
|
|
就可以实现任意地址读
通过
就可以实现任意地址写
现在的问题是往哪写,写什么。
当新增的student的人数到限制后,会调用longjmp, 我们来看看 调用 longjmp 时做了什么

进入函数时 rdi 为 jmpbuf 的地址,可以看到,在 jmpbuf + 0x38 处存放了加密后的 rip, 进入 longjmp会先解密 出 rip 然后跳转。
|
|
jmpbuf 在堆中,如果我们可以 拿到 fs:30h 然后修改 jmpbuf + 0x38 ,我们就可以控制执行流了。
longjmp 跳转的地址其实就是 调用 setjmp 的下一条指令(0x400C31)

又由于 xor 是可逆的,所以我们可以通过
|
|
得到 fs:30h

至于重新的加密 rip 的过程,可以看 setjmp 的实现

所以总的利用思路
- 利用
off-by-one获取任意地址读写的能力 - 利用
student 2的name_ptr泄露堆地址 - 获取
jmpbuf + 0x38的值,计算fs:30h的值 - 重新计算值写入
jmpbuf + 0x38, 同时往jmpbuf开头写入 ``/bin/sh\x00
参考
https://github.com/ctfs/write-ups-2016/tree/master/seccon-ctf-quals-2016/exploit/cheer-msg-100
最后的 exp:
|
|
本站文章均原创, 转载注明来源
本文链接:http://blog.hac425.top/2018/03/20/pwn_with_longjmp.html