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 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 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 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