第五届“鹏城杯”联邦网络靶场协同攻防演练

第五届“鹏城杯”联邦网络靶场协同攻防演练

Aristore

下午要考六级,考完之后立马收拾东西赶飞机去了,导致只有早上有空做题。只有 SMBwhiteoutThe_Rogue_Beacon 是赛中做的,其余为赛后复现。

题目见 CTF-Archives/2025-pengchengcup-quals

Misc

SMB

Challenge

Do you know SMB?

Solution

使用在线工具 https://apackets.com/upload 提取 NTLMv2 哈希

参考 SwampCTF 2025 的 MuddyWater,我之前写的 WriteUp:https://www.aristore.top/posts/SwampCTF2025/#%F0%9F%94%81Planetary-Storage

PengChengBei2025-1

用 hashcat 爆破哈希,密码本用 rockyou

PengChengBei2025-2

得到密码 12megankirwin12

回到 Wireshark ,编辑 -> 首选项 -> NTLMSSP 填入密码

PengChengBei2025-3

文件 -> 导出对象 -> SMBletter.zip 导出

letter.zip 的加密算法是 ZipCrypto,因此可以尝试明文攻击

1
2
3
4
5
6
7
8
9
10
11
bkcrack.exe -C letter.zip -c letter.exe -x 0 4D5A -x 64 0E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000
bkcrack 1.7.1 - 2024-12-21
[10:04:22] Z reduction using 56 bytes of known plaintext
100.0 % (56 / 56)
[10:04:22] Attack on 141108 Z values at index 71
Keys: 68cc45ab 864060ce ac958caa
75.7 % (106781 / 141108)
Found a solution. Stopping.
You may resume the attack with the option: --continue-attack 106781
[10:07:22] Keys
68cc45ab 864060ce ac958caa

得到 Keys 为 68cc45ab 864060ce ac958caa

运行 bkcrack -C letter.zip -c letter.exe -k 68cc45ab 864060ce ac958caa -d letter.exe 解压缩

逆向工程 letter.exe 发现程序在地址 0x1400A22A8 存储了 19 字节的加密数据,使用了异或加密,XOR key 是 0x42,密文是 24 2e 23 25 39 0c 72 35 1d 17 6f 14 73 21 36 2d 30 3b 3f

PengChengBei2025-4

FLAG

1
flag{N0w_U-V1ctory}

whiteout

Challenge

一份离线备份的 Docker 镜像。

Solution

镜像是为 arm64 架构构建的,但 Kali 是 amd64 架构

先安装 QEMU 用户模式模拟器

1
2
sudo apt update
sudo apt install -y qemu-user-static qemu-user binfmt-support

然后重启系统服务

1
2
sudo systemctl restart systemd-binfmt.service
sudo systemctl restart docker

验证安装是否成功

1
ls /proc/sys/fs/binfmt_misc/ | grep qemu-aarch64

看到 qemu-aarch64 的输出说明已成功配置

以交互模式运行容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
docker run --platform linux/arm64 -it --name whiteout_container misc_docker_whiteout
root@08e66dc2317c:/opt/app# ls
decode.py log.txt
root@08e66dc2317c:/opt/app# cat decode.py
# decode.py
KEY = 0x37

def decode(path):
with open(path, "rb") as f:
data = f.read()
return bytes(b ^ KEY for b in data)

if __name__ == "__main__":
print(decode("/opt/.data/.logs/syslog.bin"))
root@08e66dc2317c:/opt/app# cat log.txt
cleanup done
root@08e66dc2317c:/opt/app# ls -ah /opt/.data/.logs
. ..

在 Docker 中 whiteout 是一种删除文件的机制,文件在容器层中被标记为已删除,实际数据可能仍存在于下层镜像中,因此可以尝试恢复 /opt/.data/.logs/syslog.bin

在宿主机查找镜像层存储位置

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(root㉿kali)-[~]
└─# docker inspect misc_docker_whiteout | grep -A 10 "LowerDir"
"LowerDir": "/var/lib/docker/overlay2/baea9fd8e87c332fd0252c7d0244ce33d764a776186af5d3cd6e0a90f4a0f2e7/diff:/var/lib/docker/overlay2/0307dc3238f0a22adbbec6b0c9014846164f3da11ea10aeda872d446937a77c3/diff:/var/lib/docker/overlay2/ab17a839c8b202fca45d45f3acb2284a82f5c3de316f4de4f32e91d63856566f/diff:/var/lib/docker/overlay2/92b6636503fa03f103c0c737a634e3ab0225df9cc125f8355ecf3a035de9f97b/diff:/var/lib/docker/overlay2/f71662a75b1dd27faf57b4ec3205795ff6dca31240dda9c0d02ca3a0d5db7dc9/diff:/var/lib/docker/overlay2/d75503f05017f8d71d5cd479c9c9b384a20fb33d96fa9679bf0025abcff27c23/diff",
"MergedDir": "/var/lib/docker/overlay2/b01d7efeb8cd62c830ebf157dcdfec628aad47a86a924c641da123057f73781a/merged",
"UpperDir": "/var/lib/docker/overlay2/b01d7efeb8cd62c830ebf157dcdfec628aad47a86a924c641da123057f73781a/diff",
"WorkDir": "/var/lib/docker/overlay2/b01d7efeb8cd62c830ebf157dcdfec628aad47a86a924c641da123057f73781a/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:b3f74c31dcaee85c6efbb1c8b30081ee1e7f2b11e841d4492217caa7d1681539",

在宿主机逐个搜索每个层目录寻找 syslog.bin

1
2
3
4
5
6
7
8
9
find /var/lib/docker/overlay2/baea9fd8e87c332fd0252c7d0244ce33d764a776186af5d3cd6e0a90f4a0f2e7/diff -name "syslog.bin" 2>/dev/null
find /var/lib/docker/overlay2/0307dc3238f0a22adbbec6b0c9014846164f3da11ea10aeda872d446937a77c3/diff -name "syslog.bin" 2>/dev/null
find /var/lib/docker/overlay2/ab17a839c8b202fca45d45f3acb2284a82f5c3de316f4de4f32e91d63856566f/diff -name "syslog.bin" 2>/dev/null
find /var/lib/docker/overlay2/92b6636503fa03f103c0c737a634e3ab0225df9cc125f8355ecf3a035de9f97b/diff -name "syslog.bin" 2>/dev/null
find /var/lib/docker/overlay2/f71662a75b1dd27faf57b4ec3205795ff6dca31240dda9c0d02ca3a0d5db7dc9/diff -name "syslog.bin" 2>/dev/null
find /var/lib/docker/overlay2/d75503f05017f8d71d5cd479c9c9b384a20fb33d96fa9679bf0025abcff27c23/diff -name "syslog.bin" 2>/dev/null
┌──(root㉿kali)-[~]
└─# find /var/lib/docker/overlay2/ab17a839c8b202fca45d45f3acb2284a82f5c3de316f4de4f32e91d63856566f/diff -name "syslog.bin" 2>/dev/null
/var/lib/docker/overlay2/ab17a839c8b202fca45d45f3acb2284a82f5c3de316f4de4f32e91d63856566f/diff/opt/.data/.logs/syslog.bin

将文件复制到宿主机

1
2
3
4
5
6
7
8
9
10
11
12
┌──(root㉿kali)-[~]
└─# cp /var/lib/docker/overlay2/ab17a839c8b202fca45d45f3acb2284a82f5c3de316f4de4f32e91d63856566f/diff/opt/.data/.logs/syslog.bin /home/kali/Desktop

┌──(root㉿kali)-[~]
└─# cd /home/kali/Desktop/

┌──(root㉿kali)-[/home/kali/Desktop]
└─# hexdump -C syslog.bin
00000000 51 5b 56 50 4c 53 58 54 5c 52 45 68 40 5f 5e 43 |Q[VPLSXT\REh@_^C|
00000010 52 58 42 43 68 51 58 45 52 59 44 5e 54 44 68 5e |RXBChQXERYD^TDh^|
00000020 44 68 51 42 59 4a |DhQBYJ|
00000026

用 Docker 容器中的 decode.py 解密即可

FLAG

1
flag{docker_whiteout_forensics_is_fun}

The_Rogue_Beacon

Challenge

某自动驾驶初创公司的测试车在进行极限速度测试时发生了数剧中断。竞争对手截获了测试期间的 CAN 总线广播数据。我们需要评估这辆原型车的动力性能极限。附件是一个.pcap 文件,记录了底盘动力域(Chassis Domain)的所有通信。请剥离出干扰数据,锁定车速信号,并向总部报告车辆达到的峰值速度发生的确切位置。

Solution

流量数据包带有空格在命令行可能出现问题,重命名为 The_Rogue_Beacon.pcapng

用 tshark 导出 CAN 数据

1
tshark -r The_Rogue_Beacon.pcapng -Y can -T fields -e frame.number -e can.id -e data > can_data.txt

这是一个 CAN 总线数据分析问题

  1. 干扰数据识别逻辑

    • 在 CAN 总线中正常底盘数据是周期性的
    • 干扰数据通常具有统计异常性,要么发送频率极低,要么发送间隔极度不规律
    • 统计每个 ID 的出现频率,频率显著低于平均水平且 ID 孤立的判定为干扰帧
  2. 车速信号识别逻辑

    • 排除控制/开关量:控制指令只有几种状态(0/1,开/关),数值种类极少
    • 排除循环计数器:计数器数值随时间呈锯齿状线性增长,但由于其位数短,唯一值数量远少于物理量
    • 锁定物理模拟量:车速是一个高频变化的物理值,在测试中它是唯一一个不重复数值比例最高且数值分布呈现单峰平滑变化的信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import os
from collections import Counter

def can_analysis(file_path):
if not os.path.exists(file_path):
print(f"Error: {file_path} not found.")
return

# 1. 原始数据解析
raw_packets = []
id_list = []

with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
parts = line.strip().split()
if len(parts) < 3: continue
try:
raw_packets.append({
'line': int(parts[0]),
'id': parts[1],
'hex': parts[2],
'val': int(parts[2], 16)
})
id_list.append(parts[1])
except ValueError:
continue

# 2. 统计每个 ID 的出现频率
id_counts = Counter(id_list)
avg_freq = sum(id_counts.values()) / len(id_counts)

# 3. 锁定干扰数据
# 干扰数据通常是发送次数最少,或者明显偏离正常周期频率的 ID
# 这里我们取出现频率最低的 ID
sorted_by_freq = sorted(id_counts.items(), key=lambda x: x[1])
interference_id = sorted_by_freq[0][0]

# 4. 锁定车速信号
# 物理信号 ID 拥有最多的“唯一数值数量”
# 因为它在加速和减速过程中几乎每一帧产生的物理值都不同
id_uniqueness = {}
for packet in raw_packets:
if packet['id'] == interference_id: continue # 排除干扰帧

if packet['id'] not in id_uniqueness:
id_uniqueness[packet['id']] = set()
id_uniqueness[packet['id']].add(packet['hex'])

# 找到不重复数值最多的 ID
speed_id = max(id_uniqueness, key=lambda k: len(id_uniqueness[k]))

# 5. 在锁定的车速信号中寻找峰值
speed_packets = [p for p in raw_packets if p['id'] == speed_id]
peak_packet = max(speed_packets, key=lambda x: x['val'])

# --- 输出结论 ---
print(f"[*] 发现干扰数据 ID : {interference_id}")
print(f" (判定依据: 该 ID 出现频率最低,共 {id_counts[interference_id]} 次)")

print(f"[*] 锁定车速信号 ID : {speed_id}")
print(f" (判定依据: 数据熵最高,拥有 {len(id_uniqueness[speed_id])} 个独立物理状态)")

print("-" * 50)
print(f"[*] 车辆达到的极限峰值 : {peak_packet['val']}")
print(f"[*] 峰值数据原始载荷 : {peak_packet['hex']}")
print(f"[*] 峰值发生的行位置 : 第 {peak_packet['line']} 行")

if __name__ == "__main__":
can_analysis('can_data.txt')

得到数据包序号为 12149

结果格式为 flag{sha256(数据包序号)}

FLAG

1
flag{9db878fd06dd7587a91c0fb600e0e9f7c3ea310e75f36253ef57ac2d92dd8c29}

ZipCracker

Challenge

do u know zip and fm?

Solution

拿到压缩包的第一件事当然是试试是不是伪加密了,事实证明确实是

首先用 foremost 从 something in it.jpg 里提取出一个压缩包,里面有文件 flag1.txtflag2.txt

接着看 do u know it,这是一个看起来像是配置文件的东西,直接拿去问 AI 就能判断出这是什么

PengChengBei2025-5

从题目描述里的 fm 就能猜到 AI 给的答复应该没什么大问题

flowchart TD
    subgraph Input_Sources [输入源]
        A[blocks_wavfile_source_0
password.wav音频文件] B[analog_sig_source_x_0
PL Tone生成器
频率: pl_freq] end subgraph Audio_Processing [音频处理链] C[blocks_repeat_0_0_0
12倍重复采样] D[band_pass_filter_0
带通滤波
300-5000Hz] E[blocks_multiply_const_vxx_0
音量增益
倍数: volume] end subgraph Modulation [调制与处理] F[blocks_add_xx_0
混合音频与PL Tone] G[analog_nbfm_tx_0
NBFM调制器
5kHz频偏] H[virtual_sink_0 → virtual_source_0
虚拟连接] end subgraph Output_Processing [输出处理] I[low_pass_filter_0
低通滤波器] subgraph Output_Branch_1 [输出分支1: 文件输出] J[blocks_complex_to_float_0
复数转浮点] K1[blocks_file_sink_0
保存到flag1.txt] K2[blocks_file_sink_0_0
保存到flag2.txt] end subgraph Output_Branch_2 [输出分支2: 实时输出] L[blocks_repeat_0_0
重复采样
usrp_rate/if_rate倍] M[zeromq_pub_sink_0
ZeroMQ发布
tcp://127.0.0.1:49201] end subgraph Output_Branch_3 [输出分支3: 可视化] N[qtgui_sink_x_0
频谱显示器] end end subgraph Control_Variables [控制变量] V1[samp_rate = 48000] V2[usrp_rate = 576000] V3[if_rate = usrp_rate/3] V4[volume = 0-10可调] V5[pl_freq = 0-250.3Hz可选] end %% 信号流连接 A --> C C --> D D --> E E --> F B --> F F --> G G --> H H --> I I --> J J --> K1 J --> K2 I --> L L --> M I --> N %% 变量连接(控制关系) V1 -.-> B V1 -.-> D V2 -.-> V3 V3 -.-> G V3 -.-> L V4 -.-> E V5 -.-> B style Input_Sources fill:#e1f5fe style Audio_Processing fill:#f3e5f5 style Modulation fill:#e8f5e8 style Output_Processing fill:#fff3e0 style Control_Variables fill:#fce4ec

根据正向逻辑编写脚本还原输入的音频文件 password.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
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
import numpy as np
from scipy.io import wavfile
from scipy import signal

def restore_audio():
print("[-] Loading raw data files...")
# GNU Radio File Sink 保存的是 raw binary float32
try:
i_data = np.fromfile('flag1.txt', dtype=np.float32)
q_data = np.fromfile('flag2.txt', dtype=np.float32)
except FileNotFoundError:
print("[!] Error: flag1.txt or flag2.txt not found.")
return

# 确保两个文件长度一致
min_len = min(len(i_data), len(q_data))
i_data = i_data[:min_len]
q_data = q_data[:min_len]

print(f"[-] Data loaded. Samples: {min_len}")

# 1. 组合 I/Q 信号 (Complex IQ)
# Z = I + jQ
iq_signal = i_data + 1j * q_data

print("[-] Demodulating FM signal...")

# 2. FM 解调 (Quadrature Demodulation)
# 原理: FM 信号的信息包含在瞬时频率中。
# 离散域中,瞬时频率与相邻采样点的相位差成正比。
#通过计算 sample[n] * conj(sample[n-1]) 的角度来获得相位差。

# 将信号错位 1 位进行共轭相乘
# angle( Z[n] * Z[n-1]* )
demodulated_signal = np.angle(iq_signal[1:] * np.conj(iq_signal[:-1]))

# 3. 降采样 (Decimation)
# 流图参数:
# USRP Rate = 576k
# IF Rate (quad_rate) = 576k / 3 = 192k (这是 flag 文件的采样率)
# Audio Rate (samp_rate) = 48k
# 因此需要进行 192k -> 48k 的降采样,因子为 4。
# 使用 scipy.signal.decimate 可以自动进行低通滤波防止混叠。

print("[-] Decimating (192kHz -> 48kHz)...")
decimation_factor = 4
audio_output = signal.decimate(demodulated_signal, decimation_factor)

# 4. 去加重 (Optional but Recommended)
# NBFM 发射端通常有预加重 (Pre-emphasis),接收端应该去加重 (De-emphasis)。
# 这里可以简单地使用一个低通滤波器,或者如果不做也能听清内容。
# 为了简化,这里我们只依靠 decimate 自带的滤波,通常足够还原语音。

# 5. 归一化并保存
# 归一化到 -1.0 到 1.0 之间,防止爆音
print("[-] Normalizing and saving...")
audio_output = audio_output / np.max(np.abs(audio_output))

# 写入 WAV 文件 (48kHz)
output_filename = 'password.wav'
wavfile.write(output_filename, 48000, audio_output.astype(np.float32))

print(f"[+] Audio saved to {output_filename}")

if __name__ == '__main__':
restore_audio()

很明显的摩斯电码,用在线工具 Morse Code World 解密

PengChengBei2025-6

结果的最后一位有问题,但不难看出是 114514350234114514

用这个字符串作为密码解压缩 flag.zip

flag.zip 里的 flag.txt 内容是 flag{Y0u*****************!!!}

flag.zip 里的 flag.zip 里的 flag.txt 使用的加密算法是 ZipCrypto,压缩方法是 Store,原始大小是 29(与 flag{Y0u*****************!!!} 长度一致),已知前 8 字节和后 4 字节,满足已知连续 8 字节以及总共已知 12 字节的要求,很显然是明显得不能再明显的明文攻击了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bkcrack.exe -C flag.zip -c flag.txt -x 0 666c61677b593075 -x 25 2121217d
bkcrack 1.7.1 - 2024-12-21
[00:46:51] Z reduction using 1 bytes of known plaintext
100.0 % (1 / 1)
[00:46:52] Attack on 2555904 Z values at index 6
Keys: 33b19021 93c4a78d 9ceed931
13.3 % (339321 / 2555904)
Found a solution. Stopping.
You may resume the attack with the option: --continue-attack 339321
[00:51:55] Keys
33b19021 93c4a78d 9ceed931

bkcrack -C flag.zip -c flag.txt -k 33b19021 93c4a78d 9ceed931 -d flag.txt
bkcrack 1.7.1 - 2024-12-21
[00:53:11] Writing deciphered data flag.txt
Wrote deciphered data (not compressed).

FLAG

1
flag{Y0u_r_th3_Z1p_k1ng!!!!!}

Hidden

Challenge

我们从一名嫌疑人的硬盘中恢复了这张图片。我们认为它不仅仅是一张普通的图片。对文件属性的初步分析没有结果,但我们怀疑需要一个密码才能解开秘密。请找出其中隐藏的数据。

Solution

zsteg 看看有没有 LSB 隐写先

1
2
3
4
5
6
7
8
9
10
11
12
zsteg treasure.bmp
[.] treasure.bmp
imagedata .. text: "z`}~`}~_"
b2,lsb,bY .. text: "$,7#F,`f"
b1,rgb,lsb,xY .. text: "PixelWhisper"
b3,g,msb,xY .. text: "$@IR\"Kr\""
b4,r,lsb,xY .. text: "gwvfgvgfgwvg"
b4,r,msb,xY .. text: "UUUUUUUUU"
b4,g,lsb,xY .. text: "\"\"\"\"#DEUDVUUfwffeeDC3"
b4,b,lsb,xY .. text: "FDETUDEETDEEV"
b4,rgb,lsb,xY .. text: "iGugVthG"
b4,bgr,lsb,xY .. text: "IewWdvHd"

拿到明显有语义的字符串 PixelWhisper

题目描述说“需要一个密码才能解开秘密”,猜测这个密码指的就是 PixelWhisper,那么这个图片就有可能还用到了别的隐写,并且还是支持加密的,首先试试常见的 steghide

1
2
steghide extract -sf treasure.bmp -p PixelWhisper
wrote extracted data to "flag.txt".

FLAG

1
flag{a9a3c2872e428b6d859a0e63458a43f8}

blue

Challenge

蓝色的世界。

Solution

PengChengBei2025-7

勾选整个蓝色位平面会发现藏了一个 Zip 压缩包,需要间隔一位提取出来,编写脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
from PIL import Image

img = Image.open('blue.png')
img_array = np.array(img)
blue_channel = img_array[:, :, 2]
blue_bytes = blue_channel.tobytes()

high_nibbles = []
for byte in blue_bytes:
high_nibble = (byte >> 4) & 0x0F
high_nibbles.append(high_nibble)

combined_bytes = bytearray()
for i in range(0, len(high_nibbles) - len(high_nibbles) % 2, 2):
combined_byte = (high_nibbles[i] << 4) | high_nibbles[i + 1]
combined_bytes.append(combined_byte)

with open('blue.zip', 'wb') as f:
f.write(combined_bytes)

内有 xor.png,压缩包的加密算法是 ZipCrypto,使用明文攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bkcrack -C blue.zip -c xor.png -x 0 89504E470D0A1A0A0000000D49484452
bkcrack 1.7.1 - 2024-12-21
[22:32:15] Z reduction using 9 bytes of known plaintext
100.0 % (9 / 9)
[22:32:16] Attack on 707085 Z values at index 6
Keys: 68cc45ab 864060ce ac958caa
29.1 % (206111 / 707085)
Found a solution. Stopping.
You may resume the attack with the option: --continue-attack 206111
[22:34:01] Keys
68cc45ab 864060ce ac958caa

bkcrack -C blue.zip -k 68cc45ab 864060ce ac958caa -D blue_.zip
bkcrack 1.7.1 - 2024-12-21
[22:35:03] Writing decrypted archive blue_.zip
100.0 % (1 / 1)
Zip error: could not find end of central directory record.

直接解压缩发现有问题,用 foremost 分离出两张图片

PengChengBei2025-8

先用 StegSolve 的 Analyse - Image Combiner - XOR00000000.png00010969.png 合成得到 xor.bmp

双图盲水印,这里用到的项目是 chishaxie/BlindWaterMark 原图是 00000000.png,有盲水印的图是 xor.bmp

1
2
3
python bwmforpy3.py decode 00000000.png xor.bmp output.png
image<00000000.png> + image(encoded)<xor.bmp> -> watermark<output.png>
[ WARN:0@4.538] global loadsave.cpp:848 cv::imwrite_ Unsupported depth image for selected encoder is fallbacked to CV_8U.

PengChengBei2025-9

FLAG

1
flag{a5e2ffeb-133e-4eb0-9855-d4d0078326ee}

PunkFace | 未完成

Challenge

look。

Solution

直接用 Word 打开会卡退,所以把后缀改成 zip 之后看里面的数据

document.xml 格外地大,先分析这个吧

前面是正常的内容,后面出现了大量的 <w:r> 标签,结构如下:

1
2
3
4
5
6
<w:r>
<w:rPr>
<w:color w:val="1A23A5"/>
</w:rPr>
<w:t xml:space="preserve"> </w:t>
</w:r>

这里每一个 <w:r> 代表文档中的一个字符:

  • 内容<w:t xml:space="preserve"> </w:t> 说明这个字符只是一个空格
  • 颜色<w:color w:val="XXXXXX"/> 说明这个空格被赋予了特定的 十六进制颜色代码

很明显接下来需要提取 w:val 值,将它们视为 RGB 颜色值,然后拼接还原成一张图片

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
import re
from PIL import Image
import math

def get_closest_dimensions(total):
root = int(math.sqrt(total))
for w in range(root, 0, -1):
if total % w == 0:
h = total // w
return w, h
return 1, total

def main():
try:
with open('document.xml', 'r', encoding='utf-8') as f:
content = f.read()
except FileNotFoundError:
return

# 提取被单独封装在一个 Run 里的彩色空格
pattern = r'<w:r><w:rPr><w:color w:val="([0-9A-Fa-f]{6})"/></w:rPr><w:t xml:space="preserve"> </w:t></w:r>'

colors = re.findall(pattern, content)
total_pixels = len(colors)

if total_pixels == 0:
return

# 计算宽高
w_base, h_base = get_closest_dimensions(total_pixels)

dimensions = [(w_base, h_base)]
if w_base != h_base:
dimensions.append((h_base, w_base))

for _, (width, height) in enumerate(dimensions):
img = Image.new('RGB', (width, height))

for i in range(total_pixels):
hex_color = colors[i]
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)

x = i % width
y = i // width
img.putpixel((x, y), (r, g, b))

img.save('result.png')

if __name__ == '__main__':
main()

没看懂是何意味

然后在搜 wp 的时候找到了这篇博客 2025 第五届 “鹏城杯” 联邦网络靶场协同攻防演练(初赛)个人 Writeup | GamerNoTitle

这篇 wp 里面说 word/media/image1.png 的 4 通道有东西(也不知道是怎么找到的,暂且先用着吧)

PengChengBei2025-10

Save Bin 导出图片

PengChengBei2025-11

OCR 识别文字得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4e 65 74 77 6f 72 6b 20 73 65 63 75 72 69 74 79 20 69 73 20 61 6e 20 75 6d 62 72
65 6c 6c 61 20 74 65 72 6d 20 74 6f 20 64 65 73 63 72 69 62 65 20 73 65 63 75 72
69 74 79 20 63 6f 6e 74 72 6f 6c 73 2c 20 70 6f 6c 69 63 69 65 73 2c 20 70 72 6f 63
65 73 73 65 73 20 61 6e 64 20 70 72 61 63 74 69 63 65 73 20 61 64 6f 70 74 65 64
20 74 6f 20 70 72 65 76 65 6e 74 2c 20 64 65 74 65 63 74 20 61 6e 64 20 6d 6f 6e 69
74 6f 72 20 75 6e 61 75 74 68 6f 72 69 7a 65 64 20 61 63 63 65 73 73 2c 20 6d 69 73
75 73 65 2c 20 6d 6f 64 69 66 69 63 61 74 69 6f 6e 2c 20 6f 72 20 64 65 6e 69 61 6c
20 6f 66 20 61 20 63 6f 6d 70 75 74 65 72 20 6e 65 74 77 6f 72 6b 20 61 6e 64 20 6e
65 74 77 6f 72 6b 2d 61 63 63 65 73 73 69 62 6c 65 20 72 65 73 6f 75 72 63 65 73 2e
4e 65 74 77 6f 72 6b 20 73 65 63 75 72 69 74 79 20 69 6e 76 6f 6c 76 65 73 20 74 68
65 20 61 75 74 68 6f 72 69 7a 61 74 69 6f 6e 20 6f 66 20 61 63 63 65 73 73 20 74 6f
20 64 61 74 61 20 69 6e 20 61 20 6e 65 74 77 6f 72 6b 20 76 65 72 61 63 72 79 70
74 2c 20 77 68 69 63 68 20 69 73 20 63 6f 6e 74 72 6f 6c 6c 65 64 20 62 79 20 74 68
65 20 6e 65 74 77 6f 72 6b 20 61 20 69 73 20 36 3f 2c 20 62 20 69 73 20 3f 37 2c 20
73 20 69 73 20 3f 2c 20 61 64 6d 69 6e 69 73 74 72 61 74 6f 72 2e 20 55 73 65 72 73
20 63 68 6f 6f 73 65 20 6f 72 20 61 72 65 20 61 73 73 69 67 6e 65 64

十六进制转字符得到:

1
Network security is an umbrella term to describe security controls, policies, processes and practices adopted to prevent, detect and monitor unauthorized access, misuse, modification, or denial of a computer network and network-accessible resources.Network security involves the authorization of access to data in a network veracrypt, which is controlled by the network a is 6?, b is ?7, s is ?, administrator. Users choose or are assigned

其中有一个关键信息 a is 6?, b is ?7, s is ?

联想到这个文档的另一张图片 image2.png,这个很明显是猫变换的参数,掩码攻击即可

用这个参数爆破得到的图片没一张是对的,经过 Alexander 师傅的提醒得知 a 和 b 是反的(?出题人不会出题就别出)

经过爆破得到参数是 a is 68, b is 77, s is 8

(这里爆破使用的工具是 aristorechina/arnold_decoder,如果觉得好用的话还请点点 star)

PengChengBei2025-12

得到 CCC35EF6EC2A7F5C67B2A3DA51C78DBB

  • 标题: 第五届“鹏城杯”联邦网络靶场协同攻防演练
  • 作者: Aristore
  • 创建于 : 2025-12-20 23:30:00
  • 更新于 : 2025-12-21 22:48:12
  • 链接: https://www.aristore.top/posts/PengChengBei2025/
  • 版权声明: 版权所有 © Aristore,禁止转载。
评论