🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
目录* House of apple 一种新的glibc中IO攻击方法
+ 前言
+ 利用条件
+ 利用原理
+ 利用思路
- 思路一:修改tcache
线程变量
- 思路二:修改mp_
结构体
- 思路三:修改pointer_guard
线程变量之house of emma
- 思路四:修改global_max_fast
全局变量
+ 例题分析
- 题目分析
- 利用过程
+ 总结
House of apple 一种新的glibc中IO攻击方法
提出一种新的glibc
中IO
利用思路,暂且命名为house of apple
。
前言
众所周知,glibc
高版本逐渐移除了__malloc_hook/__free_hook/__realloc_hook
等等一众hook
全局变量,ctf
中pwn
题对hook
钩子的利用将逐渐成为过去式。而想要在高版本利用成功,基本上就离不开对IO_FILE
结构体的伪造与IO
流的攻击。之前很多师傅都提出了一些优秀的攻击方法,比如house of pig、house of kiwi 和 house of emma等。
其中,house of pig
除了需要劫持IO_FILE
结构体,还需要劫持tcache_perthread_struct
结构体或者能控制任意地址分配;house of kiwi
则至少需要修改三个地方的值:_IO_helper_jumps + 0xA0
和_IO_helper_jumps + 0xA8
,另外还要劫持_IO_file_jumps + 0x60
处的_IO_file_sync
指针;而house of emma
则至少需要修改两个地方的值,一个是tls
结构体的point_guard
(或者想办法泄露出来),另外需要伪造一个IO_FILE
或替换vtavle
为xxx_cookie_jumps
的地址。
总的来看,如果想使用上述方法成功地攻击IO
,至少需要两次写或者一次写和一次任意地址读。而在只给一次任意地址写(如一次largebin attack
)的情景下是很难利用成功的。
largebin attack
是高版本中为数不多的可以任意地址写一个堆地址的方法,并常常和上述三种方法结合起来利用。本文将给出一种新的利用方法,在仅使用一次largebin attack
并限制读写次数的条件下进行FSOP
利用。顺便说一下,house of banana 也只需要一次largebin attack
,但是其攻击的是rtld_global
结构体,而不是IO
流。
上述方法利用成功的前提均是已经泄露出libc
地址和heap
地址。本文的方法也不例外。
利用条件
使用house of apple
的条件为:
1、程序从main
函数返回或能调用exit
函数
2、能泄露出heap
地址和libc
地址
3、 能使用一次largebin attack
(一次即可)
利用原理
原理解释均基于amd64
程序。
当程序从main
函数返回或者执行exit
函数的时候,均会调用fcloseall
函数,该调用链为:
- exit
-
fcloseall
-
_IO_cleanup
- _IO_flush_all_lockp
- _IO_OVERFLOW
- _IO_flush_all_lockp
-
-
最后会遍历_IO_list_all
存放的每一个IO_FILE
结构体,如果满足条件的话,会调用每个结构体中vtable->_overflow
函数指针指向的函数。
使用largebin attack
可以劫持_IO_list_all
变量,将其替换为伪造的IO_FILE
结构体,而在此时,我们其实仍可以继续利用某些IO
流函数去修改其他地方的值。要想修改其他地方的值,就离不开_IO_FILE
的一个成员_wide_data
的利用。
struct \_IO\_FILE\_complete
{
struct \_IO\_FILE \_file;
\_\_off64\_t _offset;
/* Wide character stream stuff. */
struct \_IO\_codecvt *\_codecvt;
struct \_IO\_wide\_data *\_wide\_data; // 劫持这个变量
struct \_IO\_FILE *\_freeres\_list;
void *_freeres_buf;
size\_t __pad5;
int _mode;
/* Make sure we don't get into trouble again. */
char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size\_t)];
};
amd64
程序下,struct _IO_wide_data *_wide_data
在_IO_FILE
中的偏移为0xa0
:
amd64:
0x0:'\_flags',
0x8:'\_IO\_read\_ptr',
0x10:'\_IO\_read\_end',
0x18:'\_IO\_read\_base',
0x20:'\_IO\_write\_base',
0x28:'\_IO\_write\_ptr',
0x30:'\_IO\_write\_end',
0x38:'\_IO\_buf\_base',
0x40:'\_IO\_buf\_end',
0x48:'\_IO\_save\_base',
0x50:'\_IO\_backup\_base',
0x58:'\_IO\_save\_end',
0x60:'\_markers',
0x68:'\_chain',
0x70:'\_fileno',
0x74:'\_flags2',
0x78:'\_old\_offset',
0x80:'\_cur\_column',
0x82:'\_vtable\_offset',
0x83:'\_shortbuf',
0x88:'\_lock',
0x90:'\_offset',
0x98:'\_codecvt',
0xa0:'\_wide\_data',
0xa8:'\_freeres\_list',
0xb0:'\_freeres\_buf',
0xb8:'\_\_pad5',
0xc0:'\_mode',
0xc4:'\_unused2',
0xd8:'vtable'
我们在伪造_IO_FILE
结构体的时候,伪造_wide_data
变量,然后通过某些函数,比如_IO_wstrn_overflow
就可以将已知地址空间上的某些值修改为一个已知值。
static wint\_t
_IO_wstrn_overflow (FILE *fp, wint\_t c)
{
/* When we come to here this means the user supplied buffer is
filled. But since we must return the number of characters which
would have been written in total we must provide a buffer for
further use. We can do this by writing on and on in the overflow
buffer in the \_IO\_wstrnfile structure. */
_IO_wstrnfile *snf = (_IO_wstrnfile *) fp;
if (fp->_wide_data->_IO_buf_base != snf->overflow_buf)
{
_IO_wsetb (fp, snf->overflow_buf,
snf->overflow_buf + (sizeof (snf->overflow_buf)
/ sizeof (wchar\_t)), 0);
fp->_wide_data->_IO_write_base = snf->overflow_buf;
fp->_wide_data->_IO_read_base = snf->overflow_buf;
fp->_wide_data->_IO_read_ptr = snf->overflow_buf;
fp->_wide_data->_IO_read_end = (snf->overflow_buf
+ (sizeof (snf->overflow_buf)
/ sizeof (wchar\_t)));
}
fp->_wide_data->_IO_write_ptr = snf->overflow_buf;
fp->