ISCTF2025 病毒分析 Writeup

在 ISCTF2025 Day 2 的挑战中,我们遇到了一组病毒分析题,题目主要考察关于鱼叉邮件 PDF 快捷方式与 MSI TRANSFORMS 参数的恶意利用分析。以下是个人 Writeup。

题目提示如下:

  • 注1:本题通过模仿某近期活跃的 APT(Advanced Presistent Threat,高级持续性威胁)组织的攻击手法,使题目更符合真实环境,题目设计为不会对系统进行破坏,即使真机运行也不影响,清除方法将在官方 WriteUp 中给出
  • 注2:为使题目正常运行,请将文件解压到 C:\Windows\System32
  • 注3:本系列除最后一题外其他题目均为问答,不需要包裹 ISCTF{}

题目附件是名为 ISCTF.rar 的压缩包,包含附件 fR6WlTJe1wISCTF基础规则说明文档.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 后就会静默安装这些附件。

病毒分析 触发 UAC 提示

在静默安装 TJe1w 时,fR6Wl 的内容会被修补到安装中。基于这一前提,我们可以对题目所给病毒附件进行分析。

病毒分析-题目1

题目模仿的APT组织中文代号为?

查询资料可知,奇安信威胁情报中心在最近的日常运营过程中发现其从 2022 年中就开始持续跟踪的新海莲花组织开始重新活跃,并使用了 MSI 文件滥用的新手法,尽管 MSI TRANSFORMS 技术理论上在 2022 年已经被披露,但这是首次在针对国内政企的 APT 活动中捕获到

答案:海莲花

来源:MSI文件滥用新趋势:新海莲花组织首度利用MST文件投递特马

病毒分析-题目2

第一阶段载荷中的入口文件全名为?

已知打开 ISCTF基础规则说明文档.pdf.lnk 后就会触发静默安装流程,释放合法的 Zoom 软件和恶意载荷。攻击者为避免引起用户怀疑,在安装结束后会自动打开被释放到 用户文档(Documents) 目录下的 ISCTF基础规则说明文档.pdf 文件。

病毒分析 释放正常 PDF 附件

病毒分析 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 的流程

病毒分析 Orca 分析 1

病毒分析 Orca 分析 2

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

病毒分析 Orca 分析 3

又注意到修补后的 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() 函数调用的三个方法具有共同特征,均为释放载荷里的有关资源,并进行下一步操作。每个释放函数都遵循相同模式:

  1. 使用 FindResourceW 定位资源
  2. LoadResource 加载资源
  3. SizeofResource 获取大小
  4. LockResource 锁定资源数据
  5. 使用 SHGetFolderPathW 获取系统路径
  6. 构建完整文件路径
  7. 创建并写入文件(使用 C++ 文件流操作)
  8. 错误处理(使用 C++ 异常机制)

病毒分析 zTool 释放文件机制

由此我们得知,第一阶段中恶意载荷释放了三个文件,它们的文件名即为本题答案。

答案:zRC.datzRCAppCore.dllISCTF2025基础规则说明文档.pdf

病毒分析-题目5

第二阶段使用了一种常见的白加黑技巧,其中黑文件名为?

白加黑中的白就是指被杀软列入到可信任列表中的文件,而黑就是指我们自己的文件,没有有效证书签名,也不是微软文件。通常黑文件会被杀软加入到可疑名单中。很多杀毒软件会对白文件的操作进行放行,如果我们将黑程序和白程序在一个进程中,就可以绕过一些杀软的检测。

参考:Pachimker | 免杀技术之白加黑的攻击防御

我们注意到第一阶段恶意载荷释放了一个名为 zRCAppCore.dll 的文件,它的文件名和 Zoom 安装目录下名为 zRCAppCore.dll 的合法文件是一致的。在第一阶段恶意载荷的释放流程中,它被释放到 \\ZoomRemoteControl\\bin\\zRCAppCore.dll,取代了原版安装包中 1.8 MB 大小的 zRCAppCore.dll 为 111 KB 大小的恶意载荷 zRCAppCore.dll。而原本的 zRCAppCore.dll 则被 zRC.dll 所替代。

TJe1w(正常 Zoom 安装包)原本的 zRCAppCore.dll

注入恶意组件的安装包中的 zRC.dll 和 zRCAppCore.dll

实际上,zRC.dll 和正常 Zoom 安装包中的 zRCAppCore.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 保护

答案:UPX

病毒分析-题目9

第三阶段载荷首次回连域名为?

我们先用 upx -d 去掉第三阶段载荷的加壳。分析发现,第三阶段载荷首先会向一个 SharePoint 地址发送 GET 请求,从这个地址下载配置文件 c2.dat 以与 C2 服务器建立通信。

病毒分析 第三阶段载荷与 SharePoint 服务通信

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 的内容进行解密。

病毒分析 第三阶段载荷按关键标记解密

具体而言,第三阶段载荷定义了两个关键标记 lD1bZ0E9dE7d,第三阶段载荷会解密这两个标记之间的 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 与服务器通信

答案:get_cmd

病毒分析-题目12

访问最终回连地址得到flag

47.252.28.78:37204 就是最终回连地址。访问即可下载最终 Flag。

病毒分析 访问最终回连地址 获得 Flag

最终 Flag 为:

ISCTF{Wow!_Y0u_F0uNd_C2_AdDr3sssss!}
posted @ 2025-12-12 17:26  星时雨歇  阅读(117)  评论(0)    收藏  举报