返回 文章 学习 CMS 文章

当扫描器错过攻击:Cloudflare 客户端安全如何保护店铺

通过四个真实攻击案例,了解恶意 JavaScript 如何绕过传统扫描器,以及机器学习如何实时检测并保护店铺。

客户端安全恶意JavaScript机器学习Cloudflare
成长分 / 100 75 综合收获、行动、留存与影响

当扫描器错过攻击:Cloudflare 客户端安全如何保护店铺
为什么值得读了解现代店铺面临的隐蔽客户端攻击,这些攻击在传统安全扫描中完全不可见。

学习 Cloudflare 如何利用图神经网络和大型语言模型大规模检测恶意 JavaScript。

关键洞察
  1. 八个恶意载荷中有七个在 VirusTotal 上不存在,URLScan 也未标记,但 Page Shield ML 在实时流量中全部捕获。
  2. 攻击者使用多种规避技术,如设备与时间门控、动态页面监控、点击拦截、多日冷却等,使恶意行为在短暂访问中不被发现。
  3. Cloudflare 使用 GNN 将 JavaScript 作为图分析,结合 LLM 第二意见和教师模型集成,减少误报并保持高召回率。
转成行动

深入阅读

正文与原文对照

原文保真覆盖:全文原文字符:35278

一个现代店铺看上去可能完全健康,而恶意 JavaScript 却在底下运作:窃取联盟营销收入、劫持搜索与点击、篡改分析数据,或向远程服务器询问下一步该执行什么。页面能加载,商品能显示,结账也正常——但浏览器可能正在悄悄做着店主从未授权的事。

这正是我们的客户端安全机器学习(ML)模型要揭露的盲区。本文追踪了 Page Shield ML 在真实环境中发现的四起行动,共涉及八个载荷。

这些恶意载荷的检测是自动完成的;只有在系统标记之后,才由人工核实每一项发现。事后我们用安全扫描工具复查这些攻击活动时发现,八个载荷中有七个在 VirusTotal 上完全没有记录,URLScan 也没有对其中任何一个给出恶意判定。与此同时,Page Shield ML 在实时流量中抓到了全部八个。

例如,安全研究早在数年前就记录了更广泛的 Lnkr 家族,但其中一个特定版本的载荷在 URLScan 上被收录了近两年半,一直标注为“No classification”(未分类),包括 2024 年 1 月的一次直接扫描。只有这一例中,VirusTotal 更早收录了该载荷:虽然它目前将该脚本标为恶意,但公开记录并未显示这一判定最初是何时作出的。与此同时,Page Shield ML 在一家在线零售商的店铺上独立地实时发现了完全相同的字节内容。更广泛地说,一个哈希可能早就为人所知,而其背后的代码却迟迟没有被归类为恶意。如果你的防御要等那个标签,那就已经晚了。你需要的是能够解析 JavaScript 本身并大规模判断它的 ML。

确实,看到一个文件并不等于理解它。棘手之处在于,这四起行动没有共同的特征,也没有通用的隐藏手法。其中一个会保持休眠,除非设备、国家、时间、来源页或浏览器状态与它所等待的条件吻合。另一个把无需点击的联盟请求藏在不可见的 iframe 里。还有的拦截点击、压制监控,或有条件地从远程服务器加载额外代码。要抓住它们,就必须观察这些部分如何协同工作:脚本何时苏醒、隐藏什么、拦截什么,以及接下来获取什么。只检查一次页面是不够的;正如这些案例所示,这类脚本的设计就是要保持沉默,直到合适的受害者出现。这就是为什么持续的浏览器可见性决定了你是抓住攻击,还是彻底错过它。

我们如何大规模检测和标注 JavaScript

本文中标记出这四起行动的同一个 GNN(图神经网络),此前已经抓到过恶意 npm 包和真实环境中的 Magecart 支付信息窃取器。这个 GNN 并不把 JavaScript 当作一段扁平的文本;它把代码当作图来推理:一棵连接代码符号的语法树,能够揭示谁调用了谁、攻击者试图掩埋什么,以及哪些部分仍在向外“回连”。这种结构帮助它在代码经过压缩、重命名和一定程度的混淆后,依然识别出可疑模式,而无需依赖已知的 URL 或字节签名。

被 GNN 标记为恶意的少数脚本(不到全部分析流量的 0.3%)会交给 Workers AI 上的轻量级大语言模型(LLM)进行实时复核。这进一步减少了误报,同时保持较高的召回率。当 LLM 印证了 GNN 的判断时,才会向客户发出告警。

为了大规模调查最复杂的脚本,我们使用了一组前沿模型,称之为“教师”(teachers,一个由自动评审组成的集成)。这组模型选自约六个不同系列的领先模型,包括在 Workers AI 上运行的开放权重模型。我们将每个模型作为一个智能体启动,让它在各自全新、独立的会话中分析同一个可疑脚本。必要时,它们的智能体工具权限允许其使用受限的 JavaScript 求值器来解开小段代码,揭示被隐藏的行为。我们很快将借助 Cloudflare Sandbox 扩展这一工作流,在隔离环境中进行更深入的分析。

这些前沿模型有时会产生分歧,尤其是面对最复杂的脚本。我们把这种分歧视为信号,而不是噪声。每个标签都成为一张投票,权重取决于该模型在 Artificial Analysis Intelligence Index 中的得分,最终得到四个标签上的概率分布:良性、支付信息窃取(magecart)、其他恶意软件和加密挖矿。因此,人工审核者只需检查被标为恶意或未形成明确三分之二多数的脚本。随后,我们把这些标签分布反馈到 GNN 的训练中,帮助它区分越来越细微的情形。这个反馈回路目前仍部分依赖人工,不过我们已开始将其自动化。

我们抓到的四起恶意 JavaScript 行动

这四起行动做的事情大相径庭,从佣金窃取,到窃取店铺已经花钱获取的购物者的分析数据。窃取佣金不同于盗刷信用卡;同样,劫持搜索也不同于窃取密码。如果一个 ML 模型只认识其中一种手法,就会对其他手法毫无察觉。因此,我们的 Page Shield ML 必须对每一种敌意行为都保持敏锐。

现在,让我们深入了解每起行动及其运作方式。

行动 对客户的影响 脚本的行为
1) 非工作时段的联盟佣金劫持者 劫持联盟佣金 移动设备 & 时间门控;动态页面监控;点击拦截;多日冷却期
2) 无需点击的联盟窃取 无需用户点击即窃取联盟佣金 屏幕外 iframe;自动点击隐藏链接作为后备;伪造的 IP 查询请求 & 时间门控;每小时轮换联盟
3) 昔日的搜索破坏者,如今的店铺后门 追踪用户,并开启可执行任意远程 JavaScript 的后门 遗留的关键词静默;localStorage 退出标记;遥测;远程代码加载
4) 付费移动流量伪装者 对带有营销活动标记的移动访客蒙蔽店铺,试图替换广告和分析,并隐藏客服入口 主机、视口 & UTM 标签门控;325 项 IP 子串列表;禁用 9 种监控/分析工具;零像素追踪信标

行动 1:非工作时段的联盟佣金劫持者

想象一个安静的周日下午:一位购物者在手机上点按了一件商品。脚本没有让这次点按正常执行,而是从攻击者预设的列表中挑选一个商品或活动落地页,在新标签页中打开,并把原标签页送入一条联盟链路。店铺看起来依然正常。如果购物者完成购买(无论是当时还是之后),这次绕道就劫持了归因,把这笔销售(以及随之产生的佣金)记到一个并未促成这次推荐的账户名下。

店铺损失了什么

店铺可能会向一个并未带来这位购物者的账户支付不应得的佣金。更糟的是,如果这次推荐原本来自一个合法的合作伙伴,被强制发出的请求可能会错误归因,把功劳和潜在的佣金从真正做了工作的合作伙伴手中转走。损害可能不止于一笔佣金:不再信任归因系统的合作伙伴,也可能不再信任其背后的零售商。

攻击链

Qualified mobile visitor → intercepted product tap → script-selected page opens in new tab + original tab follows attacker’s affiliate route

BLOG-3372 2.png

它如何保持隐藏

我们发现了五个相关的脚本构建版本:捕获时两个处于活跃状态,三个已暂停。每个活跃变体在行动之前使用不同的一组门控,检查诸如访客的设备和本地时间、该手法近期是否已运行、商品按钮是否已出现,以及是否真有人点击它。这一迷宫般的规则使得恶意行为在短暂的自动化访问期间不会暴露,除非满足该变体的特定条件。活跃脚本使用 MutationObserver(一种 JavaScript API)来监视页面初次加载后动态出现的商品卡片和按钮。这使它们能够拦截这些后到元素上的点击,而只加载一次 HTML 就停下的爬虫可能完全错过这条重定向路径。

在后期的活跃变体中,脚本会拦截符合条件的点击,并向 localStorage 写入一个三天的冷却期(使其在该设备上休眠数天)。随后它执行双标签页操作:弹出一个攻击者选定的商品页到新标签页中,让购物者保持投入,而原标签页则快速、不被察觉地经由攻击者的联盟追踪链接绕一圈再回到店铺,在后台植入攻击者的归因 cookie。控制台屏蔽和自我防御的源码检查使检视更加困难,而冷却期和狭窄的时间表则限制了恶意路径在正常购物过程中出现的频率。

下面这段经过脱敏的摘录展示了该载荷如何挂钩动态商品卡片并执行双标签页绕道。为便于阅读,我们简化了标识符、重新排版了代码,并将目标 URL 做了无害化处理。

// Watch for late-rendering product elements and hook clicks
new MutationObserver((_, observer) => {
  const tile = document.querySelector(TARGET_SELECTOR);
  if (!tile) return;
  observer.disconnect();

  tile.addEventListener("click", (e) => {
    // Bail out if cooldown is still active on this device
    const stored = JSON.parse(localStorage.getItem(STORAGE_KEY) || "null");
    if (stored && stored.expires > Date.now()) return;

    e.preventDefault();
    e.stopPropagation();
    localStorage.setItem(
      STORAGE_KEY,
      JSON.stringify({ value: "tracked", expires: Date.now() + COOLDOWN_MS }),
    );

    // Keep shopper engaged in new tab...
    window.open(target.link, "_blank");

    // ... while routing original tab through the attacker's affiliate link
    setTimeout(() => {
      window.location.href = target.redirectUrl;
    }, 200);
  }); // Note: some variants added { once: true } to detach after the first tap
}).observe(document.body, { childList: true, subtree: true });

暂停的构建版本展示了这场攻击活动如何在不移除脚本的情况下“熄火”。它们内嵌的配置设置了 status: "paused",因此在安装点击处理程序之前就退出了。这些暂停的脚本带有不同的单个购物者冷却配置(3 天、4 天和 5 天)。其中一个暂停的脚本甚至留有一条版本历史注释,明确记录该活动在黑色星期五之后被暂停。

为了首先触达访客,这起行动利用了站点的营销供应链:电商网站嵌入的、用于追踪广告活动和分析的第三方脚本与标签管理器。一条已确认的投递路径经过两个看似普通的标签管理器:Google Tag Manager → 另一个标签管理器 → 恶意脚本。这说明了载荷是如何到达浏览器的,并不证明任何一个标签管理器遭到了入侵。

攻击者甚至伪装了托管脚本的域名,以通过快速的营销审查。一个投递主机藏在众目睽睽之下:adtargett[.]com 与 adtarget[.]com 只差一个字母“t”,后者是一个 1998 年注册的广告域名。这个仿冒域名注册于 2025 年,我们检查时,其主页自称“Adtarget.com - Performance Marketing Agency”。这就是域名抢注(typosquatting):通过模仿一家真实的广告公司,该主机混入了日常营销标签之中,悄悄投递那个劫持购物者点击、并将其重定向到联盟分成链接的恶意载荷。

行动 2:无需点击的联盟窃取

第一个骗局仍然需要一次点击,而这一个需要的更少。购物者可以打开一个预订页面,在各个商品选项上流连,而从未碰过任何广告。然而在后台,脚本可能已经发出了一个联盟请求,让之后的销售看起来像是别人推荐了这位购物者。确实,当脚本的条件得到满足时,该载荷会通过隐藏的 iframe 或一个会自己点击的链接发出这个请求。

店铺损失了什么

对于受影响的旅游企业,这次攻击可能扭曲获客的经济账:一笔合法的预订或购买可能被记到一个不应得的联盟账户名下。代码证明了隐蔽的自动化联盟请求,但在实际中,是否有任何具体请求导致了完成的归因、账户入账或已支付的佣金,仍然没有观察到。

攻击链

Time-gated browser → covert affiliate request (off-screen iframe) → 1-hour throttle cookie → when blocked, automated hidden-link click fallback

BLOG-3372 3.png

它如何保持隐藏

脚本通过两层方式隐藏联盟请求:选择性执行(预检网络门控和每小时的时间表),以及隐蔽投递(屏幕外 iframe)。第一层令人意外,因为它的国家标签与真实地理位置毫无关联:无论是购物者还是店铺所在的位置,都不决定选择结果。

首先,脚本调用一个公共的基于 IP 的地理定位服务,却无视它返回的一切内容,包括购物者的国家。我们无法确定为什么它要求得到成功响应,却又忽略返回的数据;这可能是为了迷惑调查人员,也可能只是早期版本的遗留。有趣的是,如果地理定位请求失败,脚本会悄然停止;其 promise 链以 .catch(() => {}) 结束。尽管意图未经证实,这种失败即关闭(fail-closed)的行为可能有助于脚本规避网络受限的沙箱。

接下来,载荷并没有使用获取到的地理定位数据,而是包含三个 TradeDoubler(一家联盟营销网络)配置对象,分别标记为 {AU、US 和 UK}。这些设置块嵌入在代码中,每个都包含一个联盟 URL 以及起止时间。脚本在 JavaScript 中计算 Asia/Kolkata 时间,检查这些配置的时间窗口,然后应用固定的奇偶小时规则,从三者中选择一个,否则本次运行就跳过联盟请求。这个选择是确定性的。

时间表与浏览器状态检查共同构成了按时间门控的选择性执行,这是一种伪装(cloaking)。当这些条件对不上时,联盟行为保持休眠,因此一次性检查可能会错过它。

一旦脚本选定某个配置,它会写入一个名为 affiliateClicked_<market> 的本地 cookie,作为一小时的重试节流,这样就不会立刻对该区域再次触发(这是一个为避免噪声的客户端节流,并非联盟网络的归因 cookie)。接着,它在屏幕外的 iframe 中加载该联盟 URL,并抑制 referrer。iframe 是主要的投递路径,但带有激进的后备方案:如果 iframe 出错,或在一到两秒后仍未加载完成,脚本会创建一个不带 target 属性的隐藏链接(<a>)并以编程方式点击它,这可能导致用户当前的标签页发生跳转。对符合条件的购物者而言,一切看起来都很正常:他们从未看到广告,无需点击,也可以像什么都没发生一样关闭标签页。

至于脚本的混淆,手法简单但有效:连属性名都是一次一个字符拼接出来的。下面这段经过脱敏的摘录展示了载荷创建一个不可见的屏幕外 iframe。为便于阅读,我们重命名了关键标识符并重新排版了代码。目标地址已移除。

function loadAttribution(target) {
  const frame = document['c'+'r'+'e'+'a'+'t'+'e'+'E'+'l'+'e'+'m'+'e'+'n'+'t'](
    'i'+'f'+'r'+'a'+'m'+'e'
  );
  frame['s'+'r'+'c'] = target;
  frame['r'+'e'+'f'+'e'+'r'+'r'+'e'+'r'+'P'+'o'+'l'+'i'+'c'+'y'] =
    'n'+'o'+'-'+'r'+'e'+'f'+'e'+'r'+'r'+'e'+'r';
  frame['s'+'t'+'y'+'l'+'e']['c'+'s'+'s'+'T'+'e'+'x'+'t'] =
    'w'+'i'+'d'+'t'+'h'+':'+'1'+'p'+'x'+';'+'h'+'e'+'i'+'g'+'h'+'t'+':'+'1'+'p'+'x'+';'+
    'p'+'o'+'s'+'i'+'t'+'i'+'o'+'n'+':'+'a'+'b'+'s'+'o'+'l'+'u'+'t'+'e'+';'+
    'l'+'e'+'f'+'t'+':'+'-'+'9'+'9'+'9'+'9'+'p'+'x'+';'+
    'v'+'i'+'s'+'i'+'b'+'i'+'l'+'i'+'t'+'y'+':'+'h'+'i'+'d'+'d'+'e'+'n';
  document['b'+'o'+'d'+'y']['a'+'p'+'p'+'e'+'n'+'d'+'C'+'h'+'i'+'l'+'d'](frame);
}

行动 3:昔日的搜索破坏者,如今的店铺后门

多年前,Lnkr 恶意软件家族因藏身于可疑的浏览器扩展、拦截 Google 和 Bing 搜索以重定向结果并赚取广告收入而登上新闻。如今,攻击者重新利用这套代码库,在一家在线零售商的网站中植入了后门。

由于脚本运行在店铺而非搜索引擎上,它旧有的重定向伎俩处于休眠状态。这一次,脚本被用来把遥测数据发回给攻击者。更危险的是,它给了攻击者一扇远程大门,可以随时在顾客的浏览器中任意下载并运行新的 JavaScript,而无需触碰服务器上的任何文件。它甚至保留了扩展时代的一个老把戏:如果有人在 Google 中输入“virus”或“popup”之类的词,它就会自行关闭。从外部看,店铺照常营业,丝毫看不出有什么不对。

店铺损失了什么

店铺失去了对顾客浏览器中运行什么代码的控制。攻击者暗中追踪访客的会话,并拥有一个直接的后门,可以随时向店铺推送并运行任何他们想要的 JavaScript。

攻击链

HTML-referenced script → analyst evasion gates → parallel host-gated branches (dormant search vs. live backdoor) → arbitrary remote JavaScript execution

BLOG-3372 4.png

它如何保持隐藏

与通过标签管理器投递的攻击活动不同,这个脚本是直接嵌入商家 HTML 中的。我们无法确定最初的入侵途径;实际上,直接插入 HTML 通常通过被盗用的店铺管理员凭据、未经授权的模板编辑,或被感染的第三方主题或插件实现。

从内部看,这个脚本是一个模块化工具包,同时携带活跃代码和休眠代码。它较旧的模块(透明点击覆盖层、搜索引擎查询拦截器、扩展商店链接改写器,以及针对抢注域名的重定向,例如用 buking[.]com 代替 booking[.]com)只会在特定目标站点上苏醒,因此在这个店铺上保持关闭。若干内嵌域名(sugabit[.]net、votetoda[.]com、cdnpps[.]us 和遥测端点 hanstrackr[.]com)就位于这些被禁用的模块之中。

在这家店铺上,活跃的分支专注于规避、遥测和远程控制:

  • 对安全研究人员装死。这是继承自浏览器扩展时代的规避手法:脚本监视搜索输入和 URL 查询中是否出现典型的广告软件相关词汇。搜索一个安全类关键词会让脚本在本次访问中暂停。搜索两个或更多则会向 localStorage 写入一条持久的退出记录,永久让该分析人员机器上的脚本保持沉默,使重复测试一无所获。虽然这最初是为了在搜索引擎上躲避分析人员,但据我们所能确定,这项检查被硬编码为专门针对 Google 搜索 URL,在商家店铺上一直处于休眠状态。
  • 动态远程代码执行。脚本无需修改店铺就能改变自身行为。尽管硬编码的域名(scrprime[.]com、youronlinesearches[.]com、jullyambery[.]net)与较早的捕获样本完全相同,但这些端点返回什么则完全由攻击者决定。脚本可以将访客遥测数据回传,向那些服务器索取新指令,并把新的 JavaScript 直接拉取到购物者的浏览器中。实际上,这给了攻击者一个在店铺上运行任意代码的实时后门。我们无法确定实际下发了哪些第二阶段载荷。

总而言之,对网站做静态快照只能看到正常的店铺,而底层的状态检查、反分析陷阱和远程加载分支才暴露了这个后门。

行动 4:付费移动流量伪装者

店铺已经花钱从移动广告或营销活动中引来了这位访客。恶意脚本放这次访问通过,然后切断商家的可见性。分析数据中断,在线客服聊天消失,而一个流氓观察者开始记录店铺刚刚花钱买来的这次会话的遥测数据。

在幕后,除非这次访问满足一套精心设计的条件,否则载荷拒绝运行:确切的目标店铺、较窄的移动端屏幕,以及访问前两个页面内出现的营销活动标记。它在笔记本电脑、企业网络、云服务商和 VPN 上保持休眠,因此最可能调试该页面的工程师永远看不到它触发。脚本还在选定的美国城市和地区保持休眠,并借助一份手工编制的、包含 325 个 IP 字符串的拒绝名单,来躲避自动化扫描器和安全分析人员。只有在此之后,脚本才会尝试拆除店铺的监控,换上替代的广告和分析身份,并向外回连。换一台设备或换一个网络再看一眼,永远不会触发它。与此同时,店铺照常销售。

店铺损失了什么

对于一家直接面向消费者的零售商,这个恶意软件专门针对店铺通过付费搜索和营销活动(ppc、cpc、sms、paid)花钱获取的高价值流量。这些顾客仍然可以购买。然而,店铺面临三个明确的威胁:广告归因被转移,发布商获得不应得的付款;九种可观测性工具上的关键会话分析数据丢失;以及帮助聊天和联系表单被压制(使购物者无法提问或报告异常)。在沙箱浏览器环境中的动态分析确认,替代的分析脚本加载并触发了一个追踪信标(一种用于记录访客活动的不可见网络请求),但攻击者在实践中是否成功捕获了会话遥测数据或转移了广告收入,仍未得到证实。

攻击链

Campaign-tagged mobile arrival → multi-tier cloaking & network gates → monitoring sabotaged → advertising, analytics, and support controls rewritten

BLOG-3372 5.png

它如何保持隐藏

为了融入店铺的营销供应链,攻击者从 sdk-amazonaws[.]com 投递载荷,这是一个注册于 2024 年的仿冒域名,与官方的 Amazon Web Services 域名(amazonaws.com,注册于 2005 年)毫无关联。为了加深欺骗性,攻击者还在域名前加了一个模仿某流行电商营销平台的子域名。这种叠加了两个受信任品牌的域名抢注营造出令人信服的伪装,旨在躲过快速的标签审查。无论是 Amazon Web Services 还是被冒充的营销平台,都没有参与这次攻击,也没有遭到任何入侵。

脚本一旦在浏览器中加载,就会先经过一连串异常密集的伪装门控,然后才触发主载荷:

  • 目标主机与浏览上下文。脚本验证 window.location.hostname 与其所针对的特定商家主机相符(在其他任何地方立即退出),确保当前窗口是顶层窗口(而非嵌入的 iframe),并检查路径中不包含 /challenge。它还验证追踪标记 cookie(_cart_dr 和 logoalt)尚未存在于浏览器中。
  • 设备与营销活动过滤。访客的视口宽度必须小于 477 像素(手持智能手机)。此外,访客必须是通过首次接触(访客的初始来源)的营销活动到达的,且该活动带有六种特定 UTM medium(Urchin Tracking Module,用于追踪营销活动的标准 URL 标签)之一:ppc、cpc、sms、paid、flow 或 campaign。同时,这还必须是其会话的第一次或第二次页面加载。奇怪的是,虽然代码中包含一条名义上的非 UTM 路径,但它要求会话页面计数同时大于 -1 且小于 -2(这在数学上不可能,使该分支完全无法到达)。这可能是又一种迷惑手段,也可能是代码修改后的遗留。
  • 永远通过的“随机”门控。代码中有一个看起来像概率节流的部分(Math.random() <= threshold),让执行显得时有时无。然而,当我们解出去混淆后的算术时,阈值恰好化简为 1。由于 JavaScript 的 Math.random() 总是返回严格小于 1 的值,这个门控总是求值为真。与那个无法到达的非营销活动分支一样,这是一个实际上从不做任何决定的条件。它可能是遗留的节流,也可能是故意给阅读混淆算术的人制造的伪装。无论哪种情况,捕获到的载荷都从未用它来跳过符合条件的访客。
  • 第三方 IP 情报。脚本使用内嵌的密钥查询一个外部的第三方 IP 情报 API。它要求访客使用美国的移动消费者连接,同时只要响应将其标记为商业网络、托管设施、云服务商、保留地址(bogon)、Tor 出口节点、VPN、代理、中继或一般威胁指标,就立即退出。
  • 地理排除。载荷不会对来自特定地区(US-NY、US-CA 和 US-NHDD)的访客,或来自 San Francisco、Plymouth、Compton、Hopkinton 和 Lafayette 这几个城市的访客激活。
  • 包含 325 项的 IP 子串陷阱。脚本将访客的 IP 与一个内嵌的拒绝名单比对,名单包含 325 个完整的 IPv4 地址字符串。去重后,这些字符串代表 313 个唯一地址,分布在 249 个不同的三段前缀中。作者没有执行结构化的 CIDR(无类别域间路由)子网匹配,而是简单地去掉访客 IPv4 地址的最后一段,然后做原始的子串搜索:!denylistString.includes(visitorPrefix)。

用精简的伪代码表示,这个多层的主激活漏斗如下所示:

// 1. Context, device, and campaign gates
let eligible = isTopWindow && host === EXPECTED_HOST && !path.includes("/challenge");
eligible &&= !hasCookie("_cart_dr") && !hasCookie("_logo_alt");
eligible &&= viewportWidth < 477 && [1, 2].includes(sessionPage);
eligible &&= ["ppc", "cpc", "sms", "paid", "flow", "campaign"].includes(utmMedium);
eligible &&= Math.random() <= 1; // Apparent random gate always resolves to true

// 2. IP intelligence & geographic gates (fetching external API)
eligible &&= ipInfo.country === "US" && ipInfo.isMobile && !ipInfo.isBusiness;
eligible &&= !ipInfo.isCloud && !ipInfo.isProxy && !ipInfo.isVpn && !ipInfo.isTor && !ipInfo.isThreat;
eligible &&= !["US-NY", "US-CA", "US-NHDD"].includes(ipInfo.region);
eligible &&= !EXCLUDED_CITIES.includes(ipInfo.city);

// 3. 325-entry IP prefix check (raw substring matching)
let clientPrefix = ipInfo.ip.slice(0, ipInfo.ip.lastIndexOf("."));
eligible &&= !DENYLIST_STRING.includes(clientPrefix);

if (!eligible) return; // Cloak passes only for qualifying consumer mobile sessions

破坏可观测性并劫持身份:

只有在所有主要门控都通过之后,脚本才会执行其载荷:

  • 让监控工具失明。它在 DOM 中搜索并移除九种不同可观测性和分析服务的脚本标签:Lucky Orange、Segment、Optimizely、New Relic、Bugsnag、LogRocket、Hotjar、Microsoft Clarity,以及店铺的 Google Tag Manager 容器(GTM-<redacted>)。在剩余的内联脚本中,它把对这些工具的引用字符串替换为未定义的虚设标识符(hji0),使对它们的调用悄然失败,试图让店铺的错误报告和监控失明。
  • 压制客户支持。它注入 CSS 并移除元素,以隐藏其支持聊天和联系表单容器,切断顾客与店铺客服之间的直接联系。
  • 替换广告和分析身份。它清除 Google Ads 的全局变量(google_ad_modifications、adsbygoogle),拆除现有的广告位(ca-pub-<original>),并以替代的发布商 ID(ca-pub-<replacement>)加载 Google Ads。随后它注入一个新的 Microsoft Clarity 会话回放脚本,并配置了一个流氓的替代项目 ID。

更简单的独立信标与 600 天标记:

与精心设计的主要伪装形成鲜明对比的是,载荷还包含次要的信标分支(悄悄向外部服务器发送 ping 以确认访问的独立例程),它们完全绕过了视口、主机名、营销活动、地理和 IP 门控。如果访客处于第二页或更后的页面,脚本会写入一个持久 cookie(_cart_dr=1),有效期恰好为 600 天(51,840,000,000 毫秒),并向 maper[.]info 上的远程遥测端点发出一个不可见的零像素图片请求(一个用于记录浏览器已到达这一步的追踪信标)。

另一个独立分支会检查一个备用标记(_logo_alt),它将触发第二个遥测 .png 信标(这个 cookie 是脚本查找但从未自己写入的;很可能由配套脚本植入)。这给了攻击者一个简单、持久的访问计数器,用于记录整个店铺所有访客的基本流量(端点处记录 IP 和 User-Agent),同时让其高风险的广告劫持例程严格隐藏在移动端伪装之后(高价值的付费到访)。这说明了为什么只分析某一个可见效果,无法揭示多用途载荷的全貌。

入侵指标(IOC)

我们发布这些指标,是为了帮助安全团队和研究人员在自己的环境中检测并追猎这些攻击活动。所有指标均直接取自捕获的载荷及其网络连接。所列 URL 已做失效处理(defanged)。部分指标已被隐去或泛化,因为公布它们可能无意中泄露受影响组织的身份。所列域名反映了在这些攻击期间被观察到参与投递、重定向或遥测链的基础设施;列入并不意味着某个共享服务或托管提供商完全是恶意的。

行动 指标 类型与作用
1) 非工作时段的联盟佣金劫持者 adtargett[.]com 脚本投递与联盟重定向的抢注域名
1) 非工作时段的联盟佣金劫持者 gdataroute[.]com 在攻击链中观察到的联盟重定向短链接服务
3) 昔日的搜索破坏者,如今的店铺后门 scrprime[.]com 浏览器劫持脚本投递域名
3) 昔日的搜索破坏者,如今的店铺后门 searchvalidation[.]com 搜索劫持与流量重定向域名
3) 昔日的搜索破坏者,如今的店铺后门 sugabit[.]net 强制搜索重定向域名
3) 昔日的搜索破坏者,如今的店铺后门 youronlinesearches[.]com 条件式远程脚本投递域名
3)老牌搜索破坏者,如今变身店铺后门 hublosk[.]com 远程脚本投递域名
3)老牌搜索破坏者,如今变身店铺后门 jullyambery[.]net 远程 JavaScript API 与命令域名
3)老牌搜索破坏者,如今变身店铺后门 votetoda[.]com 注入载荷投递域名
3)老牌搜索破坏者,如今变身店铺后门 hanstrackr[.]com 隐藏的访客遥测域名
3)老牌搜索破坏者,如今变身店铺后门 adrs[.]me 攻击链中观察到的抢注域名(typosquat)流量重定向服务
3)老牌搜索破坏者,如今变身店铺后门 youradexchange[.]com 攻击链中观察到的变现重定向服务
3)老牌搜索破坏者,如今变身店铺后门 cdnpps[.]us 注入广告框架的投递域名
4)付费移动流量伪装者(cloaker) sdk-amazonaws[.]com 滥用品牌信任的仿冒脚本投递域名
4)付费移动流量伪装者(cloaker) maper[.]info 条件式访客遥测信标域名

给防御者的四点启示

综合来看,这几起行动讲述了一个不断升级的故事:攻击者改变了目标、投递路径和伪装方式,但浏览器仍然必须执行他们的逻辑。以下四点尤为突出。

行为胜过特征签名。这些行动追求的变现与操纵方式各不相同,但每个载荷仍然必须在浏览器中行动:观察事件、检查状态、修改页面、调度任务、发起网络请求,或加载下一阶段。这正是结构化分析所关注的:无论 URL、签名和目标如何变化,恶意载荷都必须携带的那部分逻辑。

选择性执行是攻击的一部分,而非无关紧要的细节。设备、时间、地理位置、来源页(referrer)、会话、网络和冷却时间等门控条件,都可能让只访问一次并抓取静态快照的爬虫失灵。持续的可见性之所以重要,是因为攻击可能只在某一个浏览器、某一种状态、某一时刻才出现。

混淆提高了分析成本,但在这些案例中并未阻止检测。自我防御循环、控制台抑制、debugger 陷阱、轮换的字符串表和死代码分支都让分析变得复杂。尽管有这些障碍,Page Shield ML 仍然发现了全部四起行动。我们自研的快速模型负责大规模发现可疑代码,前沿模型则负责调查最棘手的案例。它们之间的分歧恰恰标出了最难处理的混淆和逻辑,帮助我们缩小关注范围。

上下文让画面完整。孤立来看平平无奇的代码,一旦防御者把静态分析与动态上下文关联起来,就可能暴露其恶意角色:它是如何到达的、哪种浏览器状态激活了它、它建立了哪些连接,以及它在运行时究竟做了什么。

对客户端执行的持续可见性

这四起行动依赖不同层次的误导手段,但都受同一个约束:它们的 JavaScript 必须在浏览器中执行。公开扫描器和静态抓取可能会漏掉被门控的行为。持续观察有助于弄清真实访客与页面交互时,代码实际做了什么

Cloudflare Client-Side Security 在所有套餐中都提供这种可见性。你可以在安全设置中开启持续脚本监控(Continuous script monitoring),跟踪店铺上的第一方和第三方脚本;自动恶意脚本检测与告警则由 Client-Side Security Advanced 提供。你可以直接在 Cloudflare 仪表板中查看脚本活动并管理检测结果。