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