IS-pwn-Linux-glibc-house-of-apple-2

glibc-2.29

house of apple 2的利用条件:

  1. 程序从main函数返回,或能调用exit函数,或能触发assert.
  2. 能泄露出heap地址和libc地址.
  3. 能任意地址写一个堆地址,如largebin_attack.

在调用_IO_wide_data中的_wide_vtable虚表里面的函数时,没有关于vtable的合法性检查.

因此,可以劫持IO_FILEvtable_IO_wfile_jumps,控制_wide_data为可控的堆地址空间,进而控制_wide_data->_wide_vtable为可控的堆地址空间.

然后使程序执行IO流函数调用,最终调用到_IO_Wxxxxx函数即可控制程序的执行流.

一些不知道哪里来的条件:

  • fp->_wide_data->_IO_read_ptr != fp->_wide_data->_IO_read_end.
  • fp->_lock是一个可写地址.
  • fp->_mode = 0.

利用_IO_wfile_overflow函数控制程序执行流

IO_FILE的设置如下:

  • _flags:2,8,0x800对应标志位为0即可.
  • vtable:设置为_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap加减偏移,使其能成功调用_IO_wfile_overflow即可.
  • _wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A.
  • _wide_data->_IO_write_base设置为0,即满足*(A + 0x18) = 0.
  • _wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0.
  • _wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B.
  • _wide_data->_wide_vtable->doallocate设置为目标地址target用于劫持RIP,即满足*(B + 0x68) = target.

函数的调用链:_IO_wfile_overflow -> _IO_wdoallocbuf -> _IO_WDOALLOCATE -> *(fp->_wide_data->_wide_vtable + 0x68)(fp).

1
2
3
4
5
6
7
8
9
10
11
12
13
wint_t _IO_wfile_overflow (FILE *f, wint_t wch) {
if (f->_flags & _IO_NO_WRITES) {
f->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return WEOF;
}
if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0) {
if (f->_wide_data->_IO_write_base == 0) {
_IO_wdoallocbuf (f);
// f->_flags & _IO_NO_WRITES == 0
// f->_flags & _IO_CURRENTLY_PUTTING == 0
// f->_wide_data->_IO_write_base == 0
// other code
1
2
3
4
5
6
7
8
9
10
11
12
void _IO_wdoallocbuf (FILE *fp) {
if (fp->_wide_data->_IO_buf_base)
return;
if (!(fp->_flags & _IO_UNBUFFERED))
if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)
// fp->_wide_data->_IO_buf_base == 0
// fp->_flags & _IO_UNBUFFERED == 0
// _IO_WDOALLOCATE (fp) => *(fp->_wide_data->_wide_vtable + 0x68)(fp)
return;
// other code
}
libc_hidden_def (_IO_wdoallocbuf)

利用_IO_wfile_underflow_mmap函数控制程序执行流

IO_FILE的设置如下:

  • _flags:4对应标志位为0即可.
  • vtable设置为_IO_wfile_jumps_mmap加减偏移,使其能成功调用_IO_wfile_underflow_mmap即可.
  • _IO_read_ptr < _IO_read_end,即满足*(fp + 8) < *(fp + 0x10).
  • _wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A.
  • _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end,即满足*A >= *(A + 8).
  • _wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0.
  • _wide_data->_IO_save_base设置为0或者合法的可被free的地址,即满足*(A + 0x40) = 0.
  • _wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B.
  • _wide_data->_wide_vtable->doallocate设置为目标地址target用于劫持RIP,即满足*(B + 0x68) = target.

函数的调用链:_IO_wfile_underflow_mmap -> _IO_wdoallocbuf -> _IO_WDOALLOCATE -> *(fp->_wide_data->_wide_vtable + 0x68)(fp).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static wint_t
_IO_wfile_underflow_mmap (FILE *fp) {
struct _IO_codecvt *cd;
const char *read_stop;

if (__glibc_unlikely (fp->_flags & _IO_NO_READS)) {
fp->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return WEOF;
}
if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
return *fp->_wide_data->_IO_read_ptr;
cd = fp->_codecvt;
if (fp->_IO_read_ptr >= fp->_IO_read_end && _IO_file_underflow_mmap (fp) == EOF)
return WEOF;
read_stop = (const char *) fp->_IO_read_ptr;
if (fp->_wide_data->_IO_buf_base == NULL) {
if (fp->_wide_data->_IO_save_base != NULL) {
free (fp->_wide_data->_IO_save_base);
fp->_flags &= ~_IO_IN_BACKUP;
}
_IO_wdoallocbuf (fp);// 需要走到这里
// fp->_flags & _IO_NO_READS == 0
// fp->_wide_data->_IO_read_ptr >= fp->_wide_data->_IO_read_end
// fp->_IO_read_ptr < fp->_IO_read_end
// fp->_wide_data->_IO_buf_base == NULL
// fp->_wide_data->_IO_save_base == NULL
// other code
1
2
3
4
5
6
7
8
9
10
11
12
void _IO_wdoallocbuf (FILE *fp) {
if (fp->_wide_data->_IO_buf_base)
return;
if (!(fp->_flags & _IO_UNBUFFERED))
if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)
// fp->_wide_data->_IO_buf_base == 0
// fp->_flags & _IO_UNBUFFERED == 0
// _IO_WDOALLOCATE (fp) => *(fp->_wide_data->_wide_vtable + 0x68)(fp)
return;
// other code
}
libc_hidden_def (_IO_wdoallocbuf)

利用_IO_wdefault_xsgetn函数控制程序执行流

这条链执行的条件是调用到_IO_wdefault_xsgetnrdx寄存器不为0.

IO_FILE的设置如下:

  • _flags设置为0x800.
  • vtable设置为_IO_wstrn_jumps/_IO_wmem_jumps/_IO_wstr_jumps加减偏移,使其能成功调用_IO_wdefault_xsgetn即可.
  • _mode设置为大于0,即满足*(fp + 0xc0) > 0.
  • _wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A.
  • _wide_data->_IO_read_end == _wide_data->_IO_read_ptr设置为0,即满足*(A + 8) = *A.
  • _wide_data->_IO_write_ptr > _wide_data->_IO_write_base,即满足*(A + 0x20) > *(A + 0x18).
  • _wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B.
  • _wide_data->_wide_vtable->overflow设置为目标地址target用于劫持RIP,即满足*(B + 0x18) = target.

函数的调用链:_IO_wdefault_xsgetn -> __wunderflow -> _IO_switch_to_wget_mode -> _IO_WOVERFLOW -> *(fp->_wide_data->_wide_vtable + 0x18)(fp).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
size_t _IO_wdefault_xsgetn (FILE *fp, void *data, size_t n) {
size_t more = n;
wchar_t *s = (wchar_t*) data;
for (;;) {
ssize_t count = (fp->_wide_data->_IO_read_end - fp->_wide_data->_IO_read_ptr);
if (count > 0) {
if ((size_t) count > more)
count = more;
if (count > 20) {
s = __wmempcpy (s, fp->_wide_data->_IO_read_ptr, count);
fp->_wide_data->_IO_read_ptr += count;
} else if (count <= 0)
count = 0;
else {
wchar_t *p = fp->_wide_data->_IO_read_ptr;
int i = (int) count;
while (--i >= 0)
*s++ = *p++;
fp->_wide_data->_IO_read_ptr = p;
}
more -= count;
}
if (more == 0 || __wunderflow (fp) == WEOF)
// fp->_wide_data->_IO_read_ptr == fp->_wide_data->_IO_read_end
// 调用__wunderflow
break;
}
return n - more;
}
libc_hidden_def (_IO_wdefault_xsgetn)
1
2
3
4
5
6
7
8
9
10
11
12
wint_t __wunderflow (FILE *fp) {
if (fp->_mode < 0 || (fp->_mode == 0 && _IO_fwide (fp, 1) != 1))
return WEOF;
if (fp->_mode == 0)
_IO_fwide (fp, 1);
if (_IO_in_put_mode (fp))
if (_IO_switch_to_wget_mode (fp) == EOF)
// fp->mode > 0
// fp->_flags & _IO_CURRENTLY_PUTTING != 0
return WEOF;
// other code
}
1
2
3
4
5
6
7
int _IO_switch_to_wget_mode (FILE *fp) {
if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF)
// fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
return EOF;
// other code
}
0%
;