house of husk

house of kiwi & pig & emma & apple不打算写了^(* ̄(oo) ̄)^,感觉别的师傅已经写的很详细了写不出啥新东西,husk感觉还可以盘一下所以写一下

原理

house of husk主要是利用了printf的一个调用链
看别的师傅的讲解都是从__register_printf_function函数开始的,但本机实测并没有调用这个函数,所以我打算从printf的流程开始捋一遍

printf流程

调用链如下:
printf调用__vfprintf_internal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//glibc-2.36\stdio-common\printf.c

int
__printf (const char *format, ...)
{
va_list arg;
int done;

va_start (arg, format);
done = __vfprintf_internal (stdout, format, arg, 0);
va_end (arg);

return done;
}

__vfprintf_internal在有格式化字符串的情况下会进入判断,满足_printf_function_table != NULL会跳至do_positional执行printf_positional

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
//glibc-2.36\stdio-common\vfprintf-internal.c

int
vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
{

……

if (__glibc_unlikely (__printf_function_table != NULL
|| __printf_modifier_table != NULL
|| __printf_va_arg_table != NULL))
goto do_positional;

……

do_positional:
done = printf_positional (s, format, readonly_format, ap, &ap_save,
done, nspecs_done, lead_str_end, work_buffer,
save_errno, grouping, thousands_sep, mode_flags);

all_done:
_IO_funlockfile (s);
_IO_cleanup_region_end (0);

return done;
}

printf_positional调用了__parse_one_specmb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//glibc-2.36\stdio-common\vfprintf-internal.c

static int
printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
va_list ap, va_list *ap_savep, int done, int nspecs_done,
const UCHAR_T *lead_str_end,
CHAR_T *work_buffer, int save_errno,
const char *grouping, THOUSANDS_SEP_T thousands_sep,
unsigned int mode_flags)
{

……

#ifdef COMPILE_WPRINTF
nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
#else
nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
#endif

……

进入__parse_one_specmb后如果不满足__printf_function_table == NULL并且__printf_arginfo_table[spec->info.spec] == NULL就会调用__printf_arginfo_table[spec->info.spec]函数,触发利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
size_t
attribute_hidden
#ifdef COMPILE_WPRINTF
__parse_one_specwc (const UCHAR_T *format, size_t posn,
struct printf_spec *spec, size_t *max_ref_arg)
#else
__parse_one_specmb (const UCHAR_T *format, size_t posn,
struct printf_spec *spec, size_t *max_ref_arg)
#endif
{

……

if (__builtin_expect (__printf_function_table == NULL, 1)
|| spec->info.spec > UCHAR_MAX
|| __printf_arginfo_table[spec->info.spec] == NULL
|| (int) (spec->ndata_args = (*__printf_arginfo_table[spec->info.spec])
(&spec->info, 1, &spec->data_arg_type,
&spec->size)) < 0)

……

利用

我们可以利用unsorted bin attack或large bin attack改写global_max_fast为一个大值,这样所有释放的堆块都会放进fastbin中,我们只要计算好main_arena和__printf_arginfo_table以及__printf_function_table之间的偏移,释放相应大小的chunk,并且在__printf_arginfo_table的chunk中的__printf_arginfo_table[spec->info.spec]的位置写入one_gadget


house of husk
http://akaieurus.github.io/2023/03/03/house-of-husk/
作者
Eurus
发布于
2023年3月3日
许可协议