前言

  • 学习了UAFdouble_freeunlinkoff-by-one,之后基本上对堆稍微有点了解。

  • 个人感觉,堆漏洞的成因就是UAF堆溢出,而栈的漏洞的成因基本上就是栈溢出,其他的就是格式化字符串漏洞,漏洞的成因感觉就是这几种。但是利用方式很多,所以学来学去都是在学被人挖掘出来的利用方法。在学这些利用方法的时候会不由感慨,那些开创漏洞利用的人脑洞真大。所以怎么感觉pwn到最后变misc那些脑洞题了…

  • 初步了解了一些漏洞成因和漏洞利用后,就可以开始学习house of 系列的利用方法了。目前打算主线直接学习house of系列,按照Glibc堆利用之house of系列总结 - roderick - record and learn!这篇文章的顺序学习,学到哪些利用,之前没涉及到的再分出支线去学习。

  • 堆的漏洞利用可以进行以下三种分类:这些分类相互有交集,并不互相独立,目前我做的是使用house of系列进行堆利用的分类,等house of系列学习完之后,有时间再进行其他的分类

介绍

  • house of系列就是堆利用和IO利用的一个称呼,和xxx的家根本连边都沾不上。

  • house of sprirt 的利用范围是glibc2.23---至今

  • 漏洞成因是堆溢出,所以漏洞的利用技巧可能会用上off-by的技巧或者unlink_attack的技巧和UAF的漏洞

  • 针对的是fast_bin attack tcache_bin_attack

  • 主要就是想尽办法伪造fake_chunk然后通过一些堆风水(堆排布),利用fast_bin或者利用tcache_bin机制将堆申请到fake_chunk上,之后再进行一系列的操作

示例

  • 这里的实验就不怎么详细讲了,自行体会,细讲的地方都会放在支线中仔细讲解。这里的实验内容主要以堆的检查机制为主,加深堆排布的感觉

实验1

  • 在ubuntu16.04的Docker环境下使用gcc编译如下程序,从该示例上看house of sprirt有点像double free的利用,但是house of sprirt可以在没有UAF漏洞的情况下进行利用,因为是对已分配的堆块进行内容上的修改,而不是对已释放的堆块进行内容上的修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
long long int a[100];
int main()
{ unsigned char *p1;
long long int *p2,*p3,*p4,*p5,*p6;
p1 = (char*)malloc(0x30);
p2 = malloc(0x30);
p3 = malloc(0x30);
p4 = malloc(0x30);
p1[0x38] = 0x81;
//free(p1);
free(p2);
p5 = malloc(0x70);
free(p3);
free(p1);//绕过double free的检查机制
free(p3);
p6 = malloc(0x30);
p6[0] = a;
a[0] = 0x0;
a[1] = 0x41;
a[2] = "aaaa";
p6 = malloc(0x30);
p6 = malloc(0x30);
p6 = malloc(0x30);
printf("%s",p6);
return 0;
}

题目

house_of_sprirt_level1

  • 本题来自:2014_hack.lu_oreo,本题的libc是Ubuntu GLIBC 2.23-0ubuntu10,由于不想找该文件,就直接用Docker中的glibc文件,堆管理器应该没太大改动(都是glibc2.23版本的)。
  • 使用Docker命令将文件复制到Docker容器里面 docker cp ./oreo 2aeebfd7bb0f:/home/ctf/house-of/house-of-sprirt
  • 题目附件:https://wwsq.lanzoue.com/iUdaf2d8rych 密码:9951

分析1

  • 先查看一下文件,发现是32位的程序

image-20241024142856397

  • 使用IDA将这个32位的程序进行逆向操作,main函数就一个开始,main函数没啥好看的

image-20241024142952953

  • 然后进入run()函数,该函数名称是我逆向该程序时重新命名过的,发现是一个堆菜单题目,并且每个函数的功能我都重命名过了

image-20241024143050064

  • 在看函数之前,先进行逆向了解程序中出现的结构体。这样更有助于我们读懂程序
  • 首先查看到dword_804A288这个位置,该变量是一个字符串指针的全局变量,所以给它重命名位char_ptr

image-20241024143421783

  • 再看到这里dword_804A2A4,通过解读程序可以得知这个应该是32位的整型或者无符号整型,作用是统计add的次数重命名为int32_v1

  • 再看到这边dword_804A2A0,通过程序解读,可以知道该变量也是一个32位的整型或者无符号整型,作用是统计delete的次数,重命名为int32_v2

  • 再看到dword_804A2A8,发现该变量也是一个字符串指针,用于指向unk_804A2C0,所以将dword_804A2A8命名为edit_ptr,将unk_804A2C0命名为edit_context

  • 修改完之后来分各个函数的作用add函数

    • 申请固定大小的0x38(10进制为56)大小的堆块,实际的堆块size位应该为0x41
    • 然后在堆块内部程序会将其分为三部分,一部分是description(char_ptr开始),一部分是char_ptr(存放前一个堆块的指针,从char_ptr+13开始,占4字节),还有一部分是name(char_ptr+25开始)
    • 这里可以注意到在对name输入的时候存在溢出

  • 所以该堆块的用户使用的区域结构如下如下

image-20241024150522510

  • 接下来查看show函数,就是将已申请的堆块从后往前打印出来

image-20241024150210348

  • delete函数,按照后申请的堆块先释放的原则将已经申请的堆块全部释放,并且将char_ptr设置为0,增加一次free操作的次数

image-20241024150630644

  • edit函数,edit函数并不是修改已申请的堆块,而是修改一个全局变量(应该是一个字符数组),用一个字符串指针指向全局变量字符数组

image-20241024151345259

  • stats函数,展现edit_ptr指向地址的内容和展现申请堆块的次数和释放堆块的次数

image-20241024151843829

利用1

  • 我们在add函数中会发现溢出点,

house_of_sprirt_level2

  • 在ubuntu16.04的环境下编译该程序
1
2
3
4
5
6
7
8
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void