chunk extend and overlapping

文章发布时间:

最后更新时间:

文章总字数:
1k

预计阅读时间:
4 分钟

chunk extend的利用手法是基于plmalloc对于堆块的各种宏定义 其是通过计算chunk首地址和size大小来推断出上一个chunk或者是下一个chunk的地址

1
2
/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr)(((char *) (p)) + chunksize(p)))

获取下一个chunk的地址既是通过当前chunk地址加上当前chunk大小

1
2
3
4
5
/* Size of the chunk below P.  Only valid if prev_inuse (P).  */
#define prev_size(p) ((p)->mchunk_prev_size)

/* Ptr to previous physical malloc_chunk. Only valid if prev_inuse (P). */
#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p)))

获取上一个chunk的地址则是通过当前chunk的地址减去前一个chunk的大小

也就是说 只要我们修改了chunk的size域和prev_size域 就可以使plmalloc误判chunk

堆溢出覆盖下一个chunk的size域

这个手法主要有两种适用的题型 一种是有伴随堆块的情况下 并且没有堆溢出 通过这种手法可以修改伴随堆块的内容

第二种是libc2.27的情况下 tcachebin的存在会使得我们打unsortedbin造成很大的影响 要么就是填满链表要么就是申请一个超过tcachebin大小的chunk并释放 有的题目会对这两种解决办法进行限制 这个时候就可以利用这种办法来合并chunk 从而获得一个超过tcachebin范围的堆块

下面来分别演示一下

环境:ubuntu22 (二进制文件依赖的libc2.27) 目测2.23以上的版本都可以

1
2
3
4
add(0x20,b'aaaa')
add(0x20,b'aaaa')
add(0x10,b'aaaa')
debug()

首先申请三个堆块 chunk1用来堆溢出 覆盖chunk2的size域 chunk3用来和chunk2合并

唯一要注意的是覆盖size域的值 需要包括两个堆块的prev_size域和size域

所以此时用来覆盖chunk2的size域的数值应为0x51

1
2
3
4
5
6
add(0x20,b'aaaa')
add(0x20,b'aaaa')
add(0x10,b'aaaa')
payload = cyclic(0x28)+b'\x51'
edit(0,len(payload),payload)
debug()

此时chunk2就和chunk3合并了

此时chunk2的指针指向的仍然是chunk2的首地址 但是plmalloc已经误判了chunk2 原本其是一个0x31大小的chunk 此时plmalloc误判其还包含了chunk3 所以释放chunk2就会一并释放chunk3

可以看到一并放入了tcache 这里之所以没有和top chunk合并 是因为tcache中的chunk Inuse位仍然为1

此时我们申请一个0x40大小的chunk 就可以获得原本chunk3的空间 从而对chunk3的内容进行任意修改

此外还有一种情况 如果chunk2先被释放进入tcachebin后再更改size域会发生什么呢

1
2
3
4
5
6
7
add(0x20,b'aaaa')
add(0x410,b'aaaa')
add(0x10,b'aaaa')
payload = cyclic(0x28)+p64(0x441)
delete(1)
edit(0,len(payload),payload)
debug()

此时注意 chunk2不能位于fastbin或者是tcachebin中 如果位于二者中 下一个chunk的Inuse位就不会为0 这样就不会合并

gdb动调看一下是否合并成功

环境: ubuntu16 二进制依赖libc2.23

这类的利用手法被称为overpadding

你可以理解为反方向的合并 刚才是由chunk2吞并chunk3 修改的是chunk2的size域 现在我们来修改chunk3的prev_size域和size域 从而使得chunk2合并chunk3

这里的知识点其实和unlink有点相似 unlink也是通过构造fake chunk 伪造好next chunk的prev_size和size

1
2
3
4
5
add(0x80,b'aaaa') #0
add(0x10,b'aaaa') #1
add(0x80,b'aaaa') #2

add(0x10,b'aaaa') #3

首先申请四个chunk 前三个用来负责合并 第四个用来保证不和top chunk合并

我们以chunk1来为堆溢出的起点 覆盖chunk2的prev_size和size域 不过在此之前还需要先释放chunk0到unsortedbin中(只要不是fastbin和tcachebin就可以) 这样才能使得后面的合并生效

1
2
3
4
5
6
7
8
add(0x80,b'aaaa')
add(0x10,b'aaaa')
add(0x80,b'aaaa')

add(0x10,b'aaaa')
delete(0)
payload = cyclic(0x10)+p64(0xb0)+p64(0x90)
edit(1,len(payload),payload)

注意一下chunk2的size域 Inuse位一定要为0 否则不会合并

此时我们释放chunk2 来看看是否合并成功

这种一般是伴随堆块先申请的情况可以利用 从而获取到低地址处堆块任意写的机会