拆解JWR钓鱼框架——一个实时操控受害者浏览器的钓鱼武器

admin 2026-08-23 05:05:00 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: CiscoTalos发现JWR钓鱼框架,通过AES-CTR加密WebSocket实时操控受害者浏览器,伪装成Shopify、PayPal等平台窃取银行卡、登录凭据等敏感数据。该框架可能是TheOutsider钓鱼即服务平台的变体,在东南亚和中东活跃。建议加强WebSocket流量监控和用户安全意识培训。 综合评分: 84 文章分类: 恶意软件,威胁情报,WEB安全,安全工具,红队


cover_image

拆解JWR钓鱼框架——一个实时操控受害者浏览器的钓鱼武器

幻泉之洲

2026年8月18日 15:28 北京

在小说阅读器读本章

去阅读

Cisco Talos发现了一个此前未公开的钓鱼框架”JWR”,它能伪装成Shopify、PayPal、Apple、Klarna等平台的支付和登录页面。与普通钓鱼页不同,JWR通过AES-CTR加密的WebSocket保持实时连接,攻击者可以像操控木偶一样指挥受害者会话。本文拆解其技术架构、指令系统、数据窃取场景,以及正在东南亚和中东活跃的短信钓鱼活动。

JWR是什么来头

JWR是一个钓鱼框架,开发者自己起了这么个内部代号。它能实时收割完整的银行卡数据、登录凭据、身份证件照片和图像。客户端引擎冒充Shopify、PayPal、Apple、Klarna和多家银行的登录及结账流程,同时通过一条AES-CTR加密的WebSocket通道,让操作者悄悄控制受害者会话。

先说结论:Talos有中等把握认为JWR是”The Outsider”钓鱼即服务平台的变体。两者客户端脚本和功能高度相似。The Outsider由中文使用者”Outsider Enterprise”运营,外部安全公司Tapetum Labs之前做过详细分析。2026年6月,FBI还通过”Ghost Hook”联合行动对Outsider平台实施了技术打击,但这个PhaaS产品在Telegram渠道里本来就是自助售卖模式。也就是说,别的中文威胁行为者拿到代码改一改,JWR这样的变体出现几乎是必然的。

技术上,JWR客户端引擎分成两大块:Host Bridge模块负责把指令转发给钓鱼iframe,Vue.js应用负责渲染44个钓鱼页面、实时回传受害者击键、执行40多条C2指令。数据外泄结构是个cvvform对象,字段涵盖卡号、CVV、PIN、有效期、社保号、护照或身份证图片、2FA验证码、网站登录凭证、PayPal凭据,外加设备指纹。

客户端架构与执行流程

执行从父钓鱼页面加载客户端引擎开始。引擎检查一个全局标志window.__HOST_MODE,由父页面设置,然后二选一进不同模式。标志被设置,进入Host Mode,控制权交给Host Bridge模块——一个IIFE(立即执行函数表达式),运行在父页面里。父页面通常是合法结账或登录页的高仿版,这个模块把接收到的数据转发给包含实际钓鱼表单的子iframe,同时建立到C2服务器的持久WebSocket连接。

标志没设置,就进Content Mode,控制权交给Vue.js应用。这是一个交互式前端,渲染钓鱼页面、收集受害者输入、管理44个HTML文件之间的流转、处理来自C2的操作者指令,数据发给C2后最终重定向到一个自定义错误页。Content Mode有三种通信方式:standalone、pluginIframe、hostIframe。

▲ 图1:JWR钓鱼框架客户端引擎架构与执行流程

  • standalone模式:应用完全拥有自己的WebSocket连接。
  • pluginIframe模式:没有直接网络连接,所有数据向上传给嵌入的插件框架。
  • hostIframe模式:完全依赖父页面,父页面已经扮演了中继桥的角色。

三种模式殊途同归。数据要么在DEV_MODE标志开启时以JSON明文发给C2,要么先经过JwrCrypto模块用新生成的密钥加密再发送。

引擎里有个后台Worker模块负责维持C2连接,页面导航也不断线,整个会话期间都活着。实时会话里,脚本持续把受害者的击键流转发到C2,操作者则不断下发下一条指令。每条进来的指令先查一个短历史记录,确保已经执行过的东西不会跑两遍,然后由指令处理模块路由到两个去向:把受害者重定向到另一个钓鱼页面,或者更新当前页面状态和显示内容,等操作者下一步指令。这个循环一直重复,直到操作者决定结束会话。到时候累积数据最后一次发给C2,受害者被重定向走。

Host Bridge模式的技术细节

Host Bridge模式下,IIFE建立到攻击者服务器的持久WebSocket连接,管理受害者会话身份,去重重复指令,并在服务器和钓鱼子iframe之间代理所有通信。

每个受害者在Bridge初始化的瞬间就被分配一个唯一会话令牌。模块先查持久存储里有没有现成的JWRCID值——如果受害者以前来过这个页面,旧令牌直接复用,操作者就能关联同一台设备的多次访问。没有的话生成新令牌,格式是JWRCVV-{Date.now()}-{random1}-{random2},两段随机数都是13位base-36字符串。这个令牌成为整个C2通信期间受害者的永久标识。

然后模块从static/js/ws-worker.js加载一个Web Worker,把WebSocket从主JavaScript上下文里隔离出来,保证钓鱼流程中页面导航不断连。WebSocket连接路径长得像这样:webSocket/QT/{sessionId}/khkjsahfjkwhakjlsdwdddddd88,尾部那串字母数字大概率是服务端认证令牌,用来确认连接来自一个已部署的套件实例。

▲ 图2:JWR客户端Host Bridge模式初始化去混淆视图

Host Bridge里还塞了个反分析检查,本质是一次性执行守卫,用自引用的.toString().search()调用配合回溯正则。这个检查能探测调试器是否被附加、函数源码是否被篡改。代码里还散布了一个诱饵变量,专门误导静态分析工具。

另外,模块在sessionStorage里维护一个名为JwrExecutedInstructions的JSON数组,防止同一条操作者指令执行两次。转发任何指令进钓鱼iframe之前,先拿指令ID跟列表比对。匹配到了就丢弃。新指令则向C2服务器回确认,格式是{type:”instructionAck”, instruction_id:, cvv_id:}。列表上限50条,满了裁到最近30条。

▲ 图3:Host Bridge模式指令处理与确认函数去混淆视图

Vue.js应用:实时捕获的核心

JWR开发者写的Vue 2.x应用是单个实例,window.vm = new Vue({el:’#app’, …}),挂载在id为”#app”的DOM元素上。这个应用就是受害者看到并交互的钓鱼页面。它负责渲染结账表单、收集并流式传输输入到C2、执行操作者指令、完成数据外泄。

Vue实例构造时先跑created函数,处理从假网页传来的数据但不挂载页面。生成会话ID,清掉上次访问残留的敏感字段——如果受害者之前来过同一个假页面。再从sessionStorage恢复之前保存的会话状态。然后,任何没有会话ID的页面(index/login/home除外)都被重定向到a_index.html,确保受害者进入钓鱼流程。最后Vue拿到渲染输出,挂到页面的#app元素上。

DOM就绪后,Vue异步执行mounted函数,这时候受害者开始对操作者可见。先判断引擎执行模式,再跑两个函数:getIPInfo()定位受害者IP、getSyncSettings()从C2拉取操作者配置。然后初始化通信通道、捕获受害者动作、创建包含设备指纹的CVV表单。指纹数据涵盖设备类型、浏览器、语言、时区、地理位置,加密后发给C2。

▲ 图4:Vue应用初始化与挂载函数去混淆视图

JWR最狠的能力之一是近实时输入流。钓鱼表单里每个输入框的内容都被传到操作者控制台,操作者能看到受害者的部分卡号、部分密码、部分验证码——边打字边看,完全不用等受害者点提交按钮。这意味着操作者在受害者提交表单之前就能决定下一步发什么指令。

Vue实例创建之前,客户端引擎就建好了一张指令映射表,把40多个操作者命令名跟具体的HTML页面文件名对应起来,赋予操作者对受害者浏览器会话的远程控制权。

▲ 图5:JWR客户端指令映射表示例

指令系统:操作者的远程遥控器

客户端脚本里有个C2命令调度器。操作者发指令过来,客户端接收、解密、转给调度函数,按指令类型路由到对应处理器。下面这张表是全量的操作者指令。

| 指令 | 用途 | | — | — | | to_index | 送受害者去落地/入口页 | | to_login | 送受害者去站点登录页 | | to_password | 提示输入账户密码 | | to_info | 收集个人身份信息 | | to_card | 送受害者去银行卡输入页 | | to_qr | 显示二维码供扫码验证 | | to_sms | 请求短信OTP | | to_sms_login | 登录步骤的短信OTP请求 | | to_sms_bank | 银行验证的短信OTP请求 | | to_2fa | 请求2FA码 | | to_text_verify | 请求自定义文本/验证码验证 | | to_email | 请求邮箱OTP | | to_pin | 请求银行卡PIN | | to_app | 请求银行App推送批准 | | to_login_app | 请求基于App的登录批准 | | to_bank_login1 | 多阶段银行登录第一步 | | to_bank_login2 | 多阶段银行登录第二步 | | to_bank_login3 | 多阶段银行登录第三步 | | to_custompage | 路由到自定义/模板定义页面 | | to_shop | 显示假店铺页面 | | to_paypal_login | 收集PayPal登录凭据 | | to_paypal_card | 通过PayPal品牌流程收集卡数据 | | to_paypal_card_verify | 请求卡验证文本(PayPal流程) | | to_paypal_sms | 请求PayPal绑定手机OTP | | to_paypal_email | 请求PayPal绑定邮箱OTP | | to_paypal_pin | 请求PayPal PIN | | to_paypal_app | 请求PayPal App批准验证 | | to_apple_login | 收集Apple ID登录信息 | | to_apple_sms | 请求Apple绑定短信OTP | | to_apple_email | 请求Apple绑定邮箱OTP | | to_apple_card | 通过Apple品牌流程收集卡数据 | | to_apple_verify | 请求通用Apple验证步骤 | | to_klarna_login | 收集Klarna登录凭据 | | to_klarna_sms | 请求Klarna绑定短信OTP | | to_klarna_email | 请求Klarna绑定邮箱OTP | | to_klarna_pay | 收集Klarna支付详情 | | to_klarna_pin | 请求Klarna PIN | | to_success | 完整数据发给C2后重定向到真实网站 | | to_redirect | 重定向受害者到操作者提供的URL | | tip_fail | 显示通用拒绝/无效错误,强制重新输入 | | tip_custom_fail | 显示操作者撰写的自定义错误消息 | | to_page_custom_fail | 路由到模板定义的自定义失败页 | | tip_change_card | 伪造”卡被拒绝”提示以套取第二张/另一张卡 | | updata_img | 推送新图片(可能是刷新后的二维码),不导航 | | updata_2fa | 静默注入/显示操作者提供的OTP码 | | text_updata_verify | 推送自定义验证文本,不导航 | | submitResult | 操作者把修正或丰富后的表单数据推回会话 |

这套指令集的设计相当务实。操作者可以引导受害者走完一整套认证流程,从登录到2FA到银行卡验证,每一步都在监控之下。遇到”卡被拒”还能循环试探,把受害者钱包里的卡一张张套出来。

数据外泄schema:远超支付数据

JWR的外泄schema范围广得离谱。完整的身份信息:姓名、性别、出生日期、社保号、护照、驾照、病历号。地址、邮箱、邮箱密码、最多三组网站登录凭证、PayPal登录信息、完整的卡数据(卡号、有效期、CVV、PIN、发卡品牌、发卡行、发卡国家)、卡片正反面照片、身份证件照片,外加自动捕获的浏览器指纹——IP、设备、语言、时区、User Agent、cookies、地理位置。

提交时客户端把提交类型归一化,触发一个全屏不可交互遮罩层盖住页面。信用卡提交时会播放Lottie动画,对应从BIN前两位识别出来的卡品牌。外泄完成后,操作者关闭WebSocket,终止Worker,把整个cvvform对象POST到C2端点api/open/the_final_interface。操作者确认后,受害者被重定向到真实网站。

整个体验行云流水,受害者大概率不会察觉哪里不对。这比那些一提交就报错的粗糙钓鱼页高明太多。

C2通信:WebSocket为主,REST兜底

JWR套件的主要C2通信走二进制WebSocket。路径格式如图6所示,JWRCID和JWRCVV段嵌入受害者的唯一会话令牌,尾部字母数字串是服务端认证令牌。

▲ 图6:JWR客户端C2连接初始化函数样例

WebSocket之外,客户端还注册了五个REST端点作为备用通道。会话以api/open/addClick打开,在钓鱼页面对受害者可见后从mounted函数里执行一次。操作者还没发任何指令,一条”新访客”记录就已经出现在控制台上了——受害者的IP、国家、具体落地的钓鱼页面、引荐或店铺URL,外加一捆设备和操作系统元数据。同期运行的api/open/getSyncSettings从攻击者服务器拉取配置,让操作者可以实时改错误消息、默认联系占位符、货币显示,不用重新部署客户端。遇到WebSocket被阻断的环境,api/open/pollInstruction提供HTTP长轮询兜底,功能一样。会话收尾靠api/open/the_final_interface——客户端引擎的终末外泄调用。操作者发出释放指令后,WebSocket和后台Worker关闭,整个cvvform对象通过HTTP POST送到C2端点。

| 端点 | 用途 | | — | — | | api/open/addclick | 受害者到达信标,附带指纹数据发到C2 | | api/open/getSyncSettings | 从C2获取操作者控制的设置 | | api/open/the_final_interface | POST完整cvvform——外泄端点 | | api/open/pollInstruction | 从C2获取操作者指令 | | api/open/addCvv | 外泄端点 |

JWR客户端还内置了Shopify和WooCommerce的定制集成。Shopify部署场景下,客户端读取cart_data URL参数——这是Shopify在结账步骤间传递的签名JSON blob——提取结账域名作为WebSocket基URL。这样WebSocket连接看起来像从合法Shopify域名发起的。initShopifyProductInfo()和initWordPressProductInfo()函数从购物车数据重建受害者的购物车,把准确的产品名、数量、单价、订单总额填进钓鱼页,假结账页跟真的一模一样。

▲ 图7:JWR客户端的Shopify平台集成函数

有个细节值得提:JWR框架操作者面向的状态消息全部是简体中文,读起来像专业管理后台的通知流。”正在填写PayPal登录账号”、”进入2FA验证页, 请发送验证, 等待用户提交”、”均失败”。中文使用者运营这套钓鱼活动的判断,有直接证据。

▲ 图8:JWR客户端程序中硬编码的简体中文状态消息

一次典型的盗卡过程

还原一下攻击者的操作流程。受害者点开钓鱼链接,浏览器发出到达信标,操作者控制台上跳出一条新访客提示。操作者接手,先发to_info指令,把受害者引到个人信息页。受害者打字的时候,操作者不发新指令,就盯着实时数据流看。

看完个人信息,操作者评估这个目标值不值得继续。有戏就发to_card指令,受害者跳到卡输入页。同样静默地看卡号一位一位被打出来。

如果操作者对这张卡不满意,发tip_fail或者tip_change_card,给受害者显示一个假的”您的卡被拒绝”提示,把他赶回卡页面去试另一张。这个循环想跑几轮都行,每次都能从同一个受害者身上多榨一张卡。卡被接受了,操作者就发to_sms、to_2fa、to_pin或者to_app,引受害者去验证页输入一次性验证码。验证码被拒就再发tip_fail逼他重输,验证通过就到to_success,受害者在不知情中被重定向到真实网站,操作者拿走了全部数据。

▲ 图9:JWR客户端引擎盗卡场景示意图

这种操作模式之所以危险,是因为它把静态钓鱼变成了动态博弈。传统钓鱼页就是收集表单提交,受害者输完走人。JWR的受害者以为自己在跟网站交互,实际上每一步都在攻击者的注视和操控之下。

正在进行的钓鱼活动

Talos观察到的真实活动用短信钓鱼传播,冒充收费站、邮局、快递公司,诱饵是通行费未缴、包裹被扣件、货到付款费。目标集中在东南亚和中东的几个国家。

受害者点开短信里的恶意链接,打开一个假网页,内嵌的JavaScript渲染并加载JWR客户端引擎。新加坡的诱饵主要冒充国家陆路交通管理局的车辆服务或道路收费支付门户,配合”未缴通行费或道路罚款”的套路。第二类URL冒充国家邮政服务,用”包裹被海关扣押需补缴费用”的诱饵。还有一小撮冒充阿联酋电子通行费系统。第三类冒充一个在东南亚多国运营的区域快递品牌,主题还是包裹未投递或货到付款费。

▲ 图10:短信钓鱼消息样例

▲ 图11:渲染并加载JWR客户端的钓鱼页面(开启HOST模式)

这个受害者分布说明这不是一次精准打击,而是一场广泛撒网的多国短信钓鱼活动。选择收费公路和快递当诱饵很有讲究——这两类场景下受害者对”小额补缴”的心理抵抗低,而且短信里附链接的接受度高。

跟其他中文PhaaS平台对比

有人会问:中文钓鱼套件这么多,JWR跟它们什么关系?Talos拿JWR跟Lucid、Darcula、Lighthouse做了对比评估。

▲ 图12:几款中文PhaaS套件特征对比

结论是:JWR跟上述三者在代码实现层面没有任何共享。C2通信协议、加密模块、消息封装全是独立开发的。但行为层面高度一致。四个套件共用同一套操作签名:操作者实时操控、盗卡与OTP/2FA拦截配合、规模化多品牌模板。这些特征把JWR归入同一个家族,说明中文网络犯罪生态里的钓鱼套件开发者之间存在一致的技战术风格。

说实话,这种”各自造轮子但拧成一个方向”的现象挺值得琢磨。一方面说明技术门槛没有想象的高——独立开发也能做出功能完整的套件。另一方面,操作模式的高度趋同意味着防御方可以针对这些行为特征做检测,不用死磕代码层面的特征。

检测与防护

以下是Talos发布的检测签名:

ClamAV签名:

  • Js.Phishing.JwrFramework-10060456-0

Snort2和Snort3规则(SID):

  • 66924
  • 66925
  • 66926
  • 66927
  • 66928

IOC完整列表在Cisco Tal


参考资料

[1] https://blog.talosintelligence.com/dissecting-the-jwr-phishing-framework/


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:幻泉之洲 《拆解JWR钓鱼框架——一个实时操控受害者浏览器的钓鱼武器》

评论:0   参与:  0