[Security] 无鉴权 WebSocket 服务可窃取自动化浏览器的全部会话 Cookie(端口 8084)
1. 摘要
EasySpider 的 Electron 主进程在 8084 端口运行一个无鉴权、不校验来源(Origin)、且默认绑定所有网卡的 WebSocket 服务(ElectronJS/main.js:1270)。任意攻击者只要能够连接该端口,即可通过两步消息协议:
- 发送
{"type":0,"message":{"id":2}}将自己的连接注册为socket_flowchart; - 发送
{"type":7}触发服务端读取正在自动化的浏览器当前会话的全部 Cookie 并回传。
从而窃取受害者在第三方网站上已登录的会话 Cookie,实现身份冒用。该问题已在本地环境用真实 Chrome 完成动态验证(见 §6)。
2. 受影响组件与版本
| 项目 | 值 |
|---|---|
| 仓库 | https://github.com/NaiboWang/EasySpider |
| 组件 | ElectronJS/main.js(ElectronJS/package.json 的 main 字段,生产入口) |
| 版本 | 0.6.3 |
| 测试 commit | 191bd6d7547bb397e4c579dd2c70ae835be3f512(master,2026-07-03) |
| 漏洞端口 | 8084(main.js:57 硬编码) |
| CWE | CWE-306(关键功能缺少认证)、CWE-200(敏感信息暴露给未授权方)、CWE-346(来源校验错误) |
| 严重度 | High(利用前提见 §5) |
3. 根因
ElectronJS/main.js:1270 创建 WebSocket 服务时没有认证握手、没有校验 Origin、也没有指定监听 host:
// main.js:1270
let wss = new WebSocket.Server({port: websocket_port}); // websocket_port = 8084 (main.js:57)
wss.on("connection", function (ws) {
ws.on("message", async function (message, isBinary) {
let msg = JSON.parse(message.toString());
...
});
});- 未指定 host → 默认绑定
0.0.0.0(实测监听*:8084),局域网内其他设备可访问; - 无
verifyClient/ 无Origin校验 → 任何连接都被接受; - 消息分发对"连接者身份"不加区分:任意连接者按合法流程图页面的协议(
ElectronJS/src/taskGrid/logic.js:16-24)发送注册消息,即可被服务端视为可信页面。
完整调用链(源码级)
| 步骤 | 位置 | 行为 |
|---|---|---|
| 1 | main.js:1270 |
无鉴权 WebSocket 服务监听 8084 |
| 2 | main.js:1276-1288 |
if (msg.type == 0) → msg.message.id == 2 → socket_flowchart = ws:任意连接可注册自己 |
| 3 | main.js:1123-1126 |
msg.type == 7 → driver.manage().getCookies() 读取当前浏览器全部 Cookie |
| 4 | main.js:1128-1133 |
拼装 name=value 文本,socket_flowchart.send(...) 回传给注册的连接者 |
合法客户端同款协议:src/taskGrid/logic.js:16-24(注册 {type:0,message:{id:2}})、src/taskGrid/FlowChart.js:286-293(触发 {type:7} 抓取 Cookie)。这是"在流程图画布上获取当前页面 Cookie"的正常功能,但服务端把该功能开放给了任意未授权连接。
4. 威胁模型与前提条件
- 前提 1:EasySpider 正在运行(Electron 主进程已启动,8084 服务在线)。
- 前提 2:正在自动化的浏览器当前持有某些站点的 Cookie(用户通常用 EasySpider 采集"已登录"网站,此前提在典型使用中成立)。
- 前提 3:攻击者能连到 8084。三种现实通道:
- 同局域网设备:端口绑定所有网卡,扫描到主机即可连接(无需经过互联网);
- 本机任意进程:本地程序直接连
ws://127.0.0.1:8084; - 本机恶意网页:服务端不校验 Origin,用户浏览器中任意页面可发起
ws://127.0.0.1:8084(浏览器 Private Network Access 仅对部分环境拦截)。
- 攻击者不需要任何应用内认证,EasySpider 无用户登录系统,无身份门槛。
5. 影响
攻击者获得正在自动化浏览器中的全部第三方站点会话 Cookie。由于用户常用 EasySpider 采集已登录站点,这些 Cookie 通常包含有效登录态。后果:
- 身份冒用:攻击者把 Cookie 导入自己的浏览器,即以受害者身份登录其账号,可读取/修改其数据、执行其权限内操作;
- 泄露范围取决于受害者浏览器中已登录站点的敏感性(购物、邮箱、工作系统等);
- 该未鉴权通道不限于
type==7:同一服务还承载任务执行、窗口控制等消息类型,存在更大的潜在攻击面(本次仅报告已证实的 Cookie 泄露)。
6. 动态验证(本机真实执行)
环境:macOS、真实 Google Chrome、selenium-webdriver + ws。服务器端为从 main.js:1270-1293、1123-1136 逐行原样搬移的处理逻辑;浏览器通过 Selenium 驱动真实 Chrome,并在 example.com 上设置两个合成演示 Cookie(sessionid=SIMULATED-SESSION-0a1b2c3d、auth_token=SIMULATED-TOKEN-7f6e5d4c),模拟受害者已登录的第三方会话。
正向验证(攻击成功):
[server] browser ready, synthetic cookies set on example.com
[server] WebSocket server listening on ws://0.0.0.0:8084 (unauthenticated)
[attacker] connected with no auth
[attacker] RECEIVED FROM SERVER:
{"type":"GetCookies","message":"auth_token=SIMULATED-TOKEN-7f6e5d4c\nsessionid=SIMULATED-SESSION-0a1b2c3d"}服务端日志同时确认其通过 driver.manage().getCookies() 读到了浏览器 Cookie 并回传。
负向对照(不注册则拿不到数据):
[neg] connected (no auth, NO registration)
[neg] result: no data returned (expected)说明:socket_flowchart 为 null 时 null.send() 抛错,被 try/catch 吞掉,攻击者收不到任何数据。这证明"能否拿到 Cookie"的唯一门槛就是注册步骤,而注册步骤本身不要求任何凭据。
监听地址实测:
node TCP *:8084 (LISTEN) # 所有网卡,非 127.0.0.1 → 局域网可达复现步骤(对运行中的 EasySpider 同样适用):
- 启动 EasySpider,等待其自动打开浏览器(无需任何配置)。
- 从同网段另一台设备(或本机进程/脚本)连接
ws://<EasySpider主机IP>:8084。 - 发送
{"type":0,"message":{"id":2}}。 - 发送
{"type":7}。 - 接收服务端回传的
GetCookies消息,其中包含当前浏览器所有站点的name=valueCookie。
验证范围说明:复现的是
main.js中漏洞处理逻辑的逐字等价实现 + 真实 Chrome,未运行完整 Electron GUI;Cookie 使用合成值,未触碰任何真实用户数据,未对任何线上系统发起探测。
7. 与已公开问题(#466)的关系
作者曾在 #466(端口 8074 任意文件读取,Closed as not planned)回复:
"默认就是本地运行的软件不经过互联网……大型组织和公司……肯定会写或者集成到自己的带有鉴权的完善的认证系统,所以这个问题对于此项目来说可以忍受。"
本报告不是 #466 的重复,二者为不同漏洞、不同端口、不同攻击面(#466:HTTP 服务路径遍历读文件;本报告:无鉴权 WebSocket 窃取浏览器 Cookie)。且作者此前被告知的辩护理由对本案不成立:
- "默认本地运行、不经互联网" —— 实测 8084 绑定所有网卡,同局域网设备可直接访问,无需经过互联网;
- 服务端不校验 Origin,本机浏览器中的恶意网页也可连接(用户自己成了攻击通道);
- 无 SECURITY.md、无私有报告流程指引之外的任何安全边界,海量普通桌面用户按默认配置运行,同样暴露。
8. 修复建议
- 握手鉴权:连接建立时要求携带主进程生成的一次性 token,仅可信的本地官方页面(流程图/扩展)能获取该 token。
- 校验 Origin:在
wss握手阶段校验Origin,只放行官方页面来源。 - 收敛监听地址:默认显式绑定
127.0.0.1;如确需局域网访问,必须叠加鉴权。 - 敏感操作权限化:对
type==7(Cookie 导出)、任务执行、窗口控制等敏感消息类型增加二次鉴权/会话绑定校验。 - 回归测试:新增"未携带 token / 非本地 Origin 的连接无法触发 Cookie 导出"的自动化用例。
9. 披露说明
- 本报告基于 commit
191bd6d7(v0.6.3)源码分析与本机动态验证,遵循负责任披露流程; - 已核查仓库历史:无公开 issue 报告过本问题(#466/#504 为端口 8074 文件读取,#972 已由报告者撤回),去重结果 clear;
- 已确认仓库启用 GitHub 私有漏洞报告(Security → "Report a vulnerability"),建议经此通道提交;
- 本报告不含真实密钥、真实用户数据或可利用的完整攻击载荷。
Source: NaiboWang/EasySpider