2025年 高校网络安全管理运维赛-电子数据取证分析师赛道 题解¶
Author
NoahTie & Track @ tratra 什么都会
写在前面¶
这次比赛检材和传统的取证比赛有所不同, 更像是 CTF Misc 方向题目中的取证题. 题目附件整体较小, 检材内也几乎没有冗余信息, 除了一些题目设置(例如, 计算修复后的文件的哈希值)和安卓分析难度较高(相对以往的取证比赛而言)之外, 整体难度不算高, 比较适合入门学习使用.
顺便吐槽一下: 上午理论题考核, 给了 50 分钟时间, 一共 300 道题. 做了不到 50 道题的时候, 我默默叉掉了浏览器, 打算摆了. 想着下午还要开组会, 随便看看检材凑个热闹得了, 结果不小心进线下了...去不了线下的我直接尬住. 占了线下名额真是抱歉, 希望组委会会向后顺延名额吧.
比赛信息¶
竞赛内容¶
设置网络安全管理员、网络信息审核员、渗透测试员和电子数据取证分析师等四个竞赛项目,均为单人赛项。每个项目理论知识占30%,技能操作占70%。考察内容参考《网 络安全法》《数据安全法》《个人信息保护法》等相关法律法规以及人力资源社会保障部公布的网络安全管理员、网络信 息审核员(互联网信息审核员)、渗透测试员、电子数据取证分析师职业技能标准要求,同时涉及风险管理、安全应用、 Web与数据库安全、安全编程开发、安全攻防、人工智能安全等多方面理论知识,以及终端安全、网站安全、系统安全、数据安全、电子取证、应急响应等安全操作技能。
检材信息¶
检材解压密码: 202510_3kGHBmXWaaR4yJYb3H2R
电子数据提取与固定 - Fitness (Windows 取证)¶
info
本部分题目参考"检材1.aff".
01 镜像中用户"berserker"的密码哈希为¶
答案
13844b0cc3aa353c8df3fbf8c6aa68f8
火眼查看用户列表, 不过在表格中默认不显示. 点击条目可以在详细信息看到:

02 镜像中是否安装了Linux环境,若有,请给出Linux发行版名称¶
答案
openSUSE
在 AppData 目录中可以看到 WSL 的数据目录, 是 openSUSE:

03 用户桌面上存在一个从运动设备中导出的运动记录文件,该文件存在损坏,请尝试修复并给出修复后的SM3校验值¶
本题存疑
根据使用的修复方法和工具不同, 大概率会得到哈希不同的文件.
官方答案是: d5d6c76f55913e9a3ff7939bca6757ea385963a379f2c1e6c3d166b130d6f8fd
在桌面上有一个 Activity.fit 文件:

出题人贴心地在 Pictures 目录中放了一些提示:


该文件是 Garmin 的运动手表的导出数据.
有一个 Web 工具可用于查看 & 修复 & 修改该文件: https://www.fitfileviewer.com/
不过这个工具修复后会改变文件的一些元数据, 并且修复行为和预期不太符合: 对照手动检查之后发现似乎是文件末尾多出了一条损坏的 Activity, 但在线工具直接将另一条正常的 Activity 复制了一份, 替换了损坏的 Activity.
在 Garmin 的网站 Download the FIT SDK | Garmin Developers 可以下载一套官方的 SDK, 文档提示需要 JRE 8, 但实测 OpenJDK 17 也可以运行:

但是该官方工具也会为修复后的原始文件添加一条 Device Info 信息:

计算出来 SM3 哈希是:

04 解析修复后的文件,请给出该运动记录中最高时速¶
答案
95.7708
用 https://www.fitfileviewer.com/ 查看, 或者用官方 SDK 中的 FitToCsv 工具转换成 CSV 查看.
CSV 中过滤 Feild2 为 speed 的条目, Value2 按降序排序可以看到最快秒速为 26.603.

转下单位就是 95.7708 km/h.
05 解析修复后的文件,文件中记录的该设备的制造商名称¶
答案
strava
参考 [[#03 用户桌面上存在一个从运动设备中导出的运动记录文件,该文件存在损坏,请尝试修复并给出修复后的SM3校验值]] 图, 在 Feild ID 中可以看到 manufacturer 为 strava.
电子数据提取与固定 - Synology (NAS & Linux 取证)¶
info
本部分题目参考"检材2-1.qcow"及"检材2-2-NAS.img".
01 从浏览器中提取 dsm 的设备名¶
答案
catdiskrecorder
题目的提示已经比较明显了, 在 Linux 的 Firefox 浏览器的历史记录里可以看到访问 NAS 的 WebUI 的历史记录:

并且可以看到用户导出了 shared_info 文件:

文件存储在 /home/debian/Downloads/shared_info.key:

02 从系统中提取群晖 smb 共享的密码¶
答案
6CzRWsUNYUpp
在 bash 的历史记录中:

还有 1 条远程挂载 NAS 共享目录的指令 sudo mount -t cifs //192.168.120.203/shared_info /mnt -o username=admin11,password=$SMB_PASSWORD.
03 从群晖系统中获取加密共享文件夹中最早创建的文件的创建时间¶
答案
2025-10-12 22:16:48
关于群晖的文件系统, 可以参考 2024 年美亚"John 的 NAS"部分的题目.
在 @shared_info@ 里, 看文件修改日期:

04 该加密文件系统使用的加密软件套件¶
答案
eCryptFS
从上题的文件/目录名称中就能看出来, 使用的是 eCryptFS 进行的加密, 这也是群晖的 NAS 提供的加密方式. 使用 UFS 可以比较容易地解密 eCryptFS 加密文件.
首先导出用户从 WebUI 下载的 shared_info.key 文件.
使用 UFS 的 "Tools" -> "eCryptFS password unwrap" 从 key 文件中提取得到明文口令 kchSnAQQmpVe:

在镜像的根目录上右键 -> "Transform file system files", 使用刚才提取的明文口令:


UFS 会自动识别 eCryptFS 加密的文件, 并进行解密:

导出文件之后查看:

05 从群晖加密文件夹中的excel文件获取第一个受害人的手机号码¶
答案
1391783251
见上题.
电子数据分析 - aipowah (Linux 服务器)¶
info
本部分题目参考"检材4.E01".
01 服务器 rootfs 采用的文件系统格式¶
答案
xfs
在火眼查看分区详情, 看到 lvm 中的 root 分区是 xfs 格式:

02 AI 诈骗站点的域名¶
答案
y8fmin.wf941021.org
nginx 配置看到站点域名

03 服务器运维人员的主机名¶
答案
akiyamachine
用户目录的 sshkey 配置中看到用户名和主机名:

04 用于诈骗聊天的 AI 模型名称¶
答案
Qwen3-30B-A3B-Instruct-2507
服务器上有一个 docker 容器, 容器配置文件在 分区0/var/lib/docker/containers/aff447086622cce1a4c9e9d713108de0622f33cd9de729d5389c548789683925/config.v2.json, 看到入口点和工作路径:

和文件挂载信息:

在 /var/lib/docker/overlay2/ 搜索 app 目录:
导出所有 app 目录, 保存在同一路径下, 自动重命名:

将 /分区0/opt/aipowah/documents.milvus 导出, 存放到导出目录, 重命名为 documents.db.
将 /分区0/var/lib/docker/overlay2/9776b4c8eda53f7f4840655a5fbda6fb9897f12624924ca24215d943f0ec083b/diff/usr/local/lib/python3.13/site-packages/powahquery/__init__.py 导出到 app/powahquery.py.
接着可以在 Linux 环境中启动服务.
在 app.py 中可以看到使用的模型是 Qwen3-30B-A3B-Instruct-2507:

05 操作系统日志外发的服务器 IP 地址¶
答案
10.0.38.211
全局搜索 rsyslog.conf, 找到 2 个配置, 第 2 个是 dracut 的配置, 主要分析的是 /etc/rsyslog.conf.


06 开发人员调用 AI 模型服务所使用的密钥¶
答案
an-m2h0xum6l59cgmx7hy3ctolligvkf
在 app.py 中可以看到从 .ak 文件读取了 APIKEY:

07 开发人员的邮箱¶
答案
feishunzhang@anshanxia.live
[[#04 用于诈骗聊天的 AI 模型名称]] 中提及的 /usr/local/lib/python3.13/site-packages/powahquery/ 是开发者自己编写的 python lib, 其中存在 .git 目录, 查看 git 信息:

08 "机票退款"类型诈骗页的域名¶
答案
hjmnanberudo60.vn
documents.db 是一个 Milvus 的数据库文件, 其中存储了 embedding 之后的向量和与向量关联的数据, 可用于构建 LLM 的知识库. 可以使用 pymilvus 库进行读取.
import pymilvus
from base64 import b64decode
client = pymilvus.MilvusClient("./documents.db")
res = milvus_client.query(collection_name="documents", filter="id >= 0", output_feilds=["id", "text"])
for i in res:
print(b64decode(i.get("text").encode()).decode("u8"))
"""
您好!我們極度重視您的帳戶安全。如果您懷疑帳戶出現異常,請立即前往我們的安全中心 hjmnanberudo60.vn 進行處理,以最大限度降低您的風險。
進入安全中心: 在頁面中選擇「舉報帳戶異常活動」選項。
描述異常情況: 請您提供相關資訊,例如您看到的異常登入時間/地點,或可疑的訂單編號。
啟動安全驗證: 為了確認是您本人在操作,我們將啟動最高級別的身分驗證。系統會向您的註冊信箱和備用手機同時發送不同的驗證碼。
鎖定帳戶並重設密碼:
輸入兩個驗證碼後,您可以選擇立即凍結帳戶以阻止任何進一步的未授權操作。
系統會引導您強制重設一個全新的、高強度的密碼。
安全提示與重要聲明:
【緊急】 這是保護您帳戶的關鍵步驟,請立即操作。
官方不會索要密碼: 我們的任何工作人員,在任何情況下,都絕對不會向您索取您的密碼或完整的驗證碼。
檢查設備安全: 操作完成後,建議您檢查您的電腦或手機是否存在病毒。
結束對話:
請您立即按照指引操作。如果您在驗證過程中無法收到驗證碼,或者需要進一步的人工協助,請直接告訴我,我會為您轉接至安全專家團隊。請放心,我們會全力協助您保護帳戶安全。
"""
电子数据恢复 - ARRAY (Raid 重组)¶
info
本部分题目参考"检材3-1.E01", "检材3-2.E01"及"检材3-3.E01".
01 该磁盘阵列的文件系统名为¶
答案
zfs
xwf 或者 UFS 挂载都能在卷标看到是 zfs:

02 该磁盘阵列的 raid 模式为¶
答案
RaidZ2
在 UFS 可以看到 Raid 模式:

UFS 可以自动重组, 但有一些比较玄学的问题. 如果手动依次添加镜像会无法自动重组; 但如果一次性从 Windows 资源管理器将 3 个镜像一起拖动到 UFS 中, 则可以自动重组 Raid.

03 该存储池的 guid 与存储池物理存储总大小为¶
答案
GUID: 18330567339733651604
物理存储池总大小: 26306674688
本题和下题需要使用 zpool 挂载存储池. 关于 ZFS 和 Zpool, 参考 2022 年美亚杯团体赛的"AGC 的流媒体服务器"第 11 题.
使用 FTK Imager 挂载 3 个 EWF 镜像, 仅挂载物理卷(Physical Only), 可写(Block Device / Writable).

在安装了 ZFS 的 Linux 虚拟机上添加 FTK Imager 挂载的物理卷.

安装 ZFS (以 Debian 为例)
Linux 系统不会预装 ZFS, 需要手动安装.
Debian 的 zfs 软件包是 stable-backports/zfsutil-linux. 由于默认的 apt source 不包含 stable-backports 源, 需要手动添加:
codename=$(lsb_release -cs)
mirror="mirrors.tuna.tsinghua.edu.cn"
echo "deb http://$mirror/debian $codename-backports main contrib non-free" | sudo tee -a /etc/apt/sources.list
sudo apt update
接着需要安装编译 zfs 模块的依赖:
安装 zfsutils-linux, 由于很多模块需要安装时编译, 耗时较长:
使用 sudo zpool import 识别并挂载 zfs 存储池, 此处的 id 即为 zfs 存储池的 GUID:

有报错, 因为挂载的盘和原盘的 PCI 端口号不同. 但 zpool 的输出也提示了"The pool can be imported despite missing or damaged devices.", 使用 sudo zpool import -f tank 可以忽略错误, 强制挂载 zfs 存储池:

通过 zpool list [-p] 可以查看已挂载的 zfs 存储池基本信息:

04 存储池内根文件系统的 guid¶
答案
5920691364380755974
使用 zfs get guid [-p] 可以获取到存储池内文件系统的 guid 信息:

05 文件系统中 evdb64.txt 中包含的有效信息¶
答案
ecff737c62052218b1988a5d1216ed
UFS 重组好的 Raid 的分区:

Base64 解码之后是一张 PNG 图片:


电子数据分析 - ezdroid (安卓逆向)¶
info
本题参考检材"检材5.apk"
答案
flag{r4nd_and_r4nd_s4me_but_n0t_s4me?__Why!_Why,_baby!_Why!}
分析 libeasyjni.so 的 Java_cn_sjtu_oops_easyjni_MainActivity_checkFlag, 核心是一个 custom base64, 动态调用的部分更改函数签名后无法定位, 好在没有环境检测手段.
先用 frida hook android_dlopen_ext 找到基地址, 然后在表的偏移位置插入硬件断点 watch point, 最后读表解密即可.
var targetLib = 'libeasyjni.so';
var libLoaded = false;
var targetFunc = 'Java_cn_sjtu_oops_easyjni_MainActivity_checkFlag';
let unsetWatchPoint = false;
let _addr, _size, _conditions;
let threads = null;
function installWatchpoint(addr, size, conditions) {
_addr = addr;
_size = size;
_conditions = conditions;
threads = Process.enumerateThreads().filter(t => t.name === "tu.oops.easyjni");
for (const thread of threads) {
Process.setExceptionHandler(e => {
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr}`);
console.log(hexdump(ptr(_addr), { length: 64 }));
if (['breakpoint', 'single-step'].includes(e.type)) {
thread.unsetHardwareWatchpoint(0);
unsetWatchPoint = true;
return true;
}
return false;
});
thread.setHardwareWatchpoint(0, addr, size, conditions);
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
}
}
function hookFunction() {
Interceptor.attach(Module.findExportByName(null, 'android_dlopen_ext'), {
onEnter: function(args) {
var libName = Memory.readUtf8String(args[0]);
if (libName.includes(targetLib)) {
libLoaded = true;
console.log('[+] ' + targetLib + ' is being loaded: ' + libName);
}
},
onLeave: function(retval) {
if (libLoaded) {
var nativeLib = Module.findBaseAddress(targetLib);
console.log('[+] ' + targetLib + ' base address: ' + nativeLib);
var b64Tab = nativeLib.add(0x0000000000037F10);
installWatchpoint(b64Tab, 16, 'w');
}
}
});
}
function hookStrcmp() {
var strcmp = Module.findExportByName('libc.so', 'strcmp');
if (strcmp) {
Interceptor.attach(strcmp, {
onEnter: function(args) {
var str1 = ptr(args[0]).readCString();
var str2 = ptr(args[1]).readCString();
if (str2 ===
'qKSdqWPkohOXbw1mq1akohOXbWVl59AiCHAlbw8rf1aMohxc4xaiAwdONAabDtX7bwUdCHXdbxfLgn1a') {
console.log(
'[strcmp] Comparing: \'' + str1 + '\' vs \'' + str2 + '\'');
}
}
});
}
}
Java.perform(function() {
hookFunction();
hookStrcmp();
});
// 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
// 7a830bdf10 7a 54 73 49 30 31 68 74 4e 55 46 2f 56 6f 45 34 zTsI01htNUF/VoE4
// 7a830bdf20 75 51 6e 70 79 41 39 62 43 71 44 35 33 66 67 69 uQnpyA9bCqD53fgi
// 7a830bdf30 00 64 36 47 58 63 4b 48 4c 6a 4a 76 37 50 6d 32 .d6GXcKHLjJv7Pm2
// 7a830bdf40 72 53 6b 4d 6c 78 77 57 38 4f 65 52 5a 61 2b 59 rSkMlxwW8OeRZa+Y
// zTsI01htNUF/VoE4uQnpyA9bCqD53fgiBd6GXcKHLjJv7Pm2rSkMlxwW8OeRZa+Y
最终结果为 flag{r4nd_and_r4nd_s4me_but_n0t_s4me?__Why!_Why,_baby!_Why!}.