
إثبات مفهوم مخبري يوضح حقن الأوامر في إرسال سير عمل GitHub Actions (CVE-2026-39866). يشغّل الإصدارات الضعيفة والمصححة جنبًا إلى جنب للتحليل التعليمي.
مختبر إثبات مفهوم لحقن الأوامر في GitHub Actions workflow dispatch
CVE-2026-39866 هو ثغرة حقن أوامر في سير عمل إصدارات Lawnchair على GitHub Actions. يمرر ملف release_update.yml إدخال المستخدم مباشرة إلى bash دون اقتباس، لذا يمكن للمهاجم حقن أوامر. يستضيف هذا المستودع النسختين الضعيفة والمصححة جنبًا إلى جنب.
| الحقل | القيمة |
|---|---|
| CVE ID | CVE-2026-39866 |
| GHSA | GHSA-9prc-pp2c-3427 |
| CWE | CWE-78 (تحييد غير صحيح للعناصر الخاصة المستخدمة في أمر نظام التشغيل) |
| المنتج | LawnchairLauncher/lawnchair |
| المتأثر | جميع الإصدارات الأقل من fcba413 |
| المُصحح | fcba413 |
| الاكتشاف | Abhay Kumar (@abhayclasher) |
توجد الثغرة في ملف سير العمل release_update.yml. يأخذ الإجراء معامل artifactName ويمرره مباشرة إلى أمر bash دون اقتباس.
- name: Rename artifact
run: mv ${{ inputs.artifactName }}.zip lawnchair.zip
يستبدل GitHub Actions قيمة المتغير في السلسلة قبل أن تشغلها الصدفة. بدون علامات اقتباس حول ${{ inputs.artifactName }}، تفسر الصدفة المسافات والفواصل المنقوطة كفواصل أوامر.
إذا قدم المهاجم file.zip; touch pwned.txt # كـ artifactName:
كيف يوزعها GitHub Actions:
${{ inputs.artifactName }} → file.zip; touch pwned.txt #
كيف تنفذها الصدفة:
mv file.zip; touch pwned.txt #.zip lawnchair.zip
تنفذ الصدفة mv file.zip، ثم touch pwned.txt، ثم تتجاهل الباقي كتعليق.
GITHUB_TOKEN أو مفاتيح توقيع الإصدارات).git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866
docker compose up -d
بعد بدء الخادم، قم بزيارة هذه العناوين:
| URL | الوصف |
|---|---|
http://localhost:3000/workflow/vulnerable | سير العمل الضعيف — حقن الأوامر يُنفذ عند الإرسال |
http://localhost:3001/workflow/patched | نفس سير العمل مع تطبيق تصحيح fcba413 |
ستنفذ النسخة الضعيفة أمرك المحقون على المشغل. تعامل النسخة المصححة الإدخال كسلسلة حرفية.
يمكنك أيضًا تشغيل PoC المستقل:
cd poc
node exploit.js
يُشغل هذا خادمًا بسيطًا على المنفذ 3000 يوضح نفس الثغرة دون سياق التطبيق الكامل.
1. يتحكم المهاجم في معامل الإدخال artifactName
2. يوسع GitHub Actions ${{ inputs.artifactName }} في YAML
3. ترسل خطوة التشغيل هذا إلى bash
4. ترى bash أحرفًا خاصة غير مقتبسة وتتعامل معها كصيغة أوامر
5. تُنفذ الأوامر بصلاحيات بيئة المشغل
┌────────────────────────────────────────────────────┐
│ 1. إدخال خبيث: │
│ │
│ artifactName = "file.zip; id #" │
│ │
│ 2. يستبدل GitHub Actions في YAML: │
│ │
│ run: mv file.zip; id #.zip lawnchair.zip │
│ │
│ 3. ترى bash: │
│ - mv file.zip │
│ - id <-- أمر عشوائي │
│ - #.zip lawnchair.zip (مُتجاهل) │
│ │
│ 4. تنفذ الصدفة الأمر العشوائي │
└────────────────────────────────────────────────────┘
يقدم الإصدار fcba413 اقتباسًا حول المتغير المُدرج:
- name: Rename artifact
run: mv "${{ inputs.artifactName }}.zip" lawnchair.zip
تمنع علامات الاقتباس الصدفة من تحليل الأحرف الخاصة. يبقى الحمولة معاملة كسلسلة اسم ملف حرفية.
أفضل ممارسة هي استخدام متغيرات البيئة لعزل إدخال المستخدم تمامًا:
- name: Rename artifact
env:
ARTIFACT_NAME: ${{ inputs.artifactName }}
run: mv "$ARTIFACT_NAME.zip" lawnchair.zip
عرض الالتزام الكامل: fcba413
CVE-2026-39866/
├── README.md # هذا الملف
├── docker-compose.yml # إعداد مختبر Docker
├── app/
│ ├── Dockerfile # إعداد بناء الحاوية
│ ├── package.json # تبعيات Node.js
│ └── server.js # محاكاة مشغل سير العمل الضعيف + المصحح
└── poc/
└── exploit.js # إثبات مفهوم مستقل مبسط
أُبلغ بواسطة Abhay Kumar. هذا المستودع مخصص للاستخدام التعليمي المحلي فقط. لا تنشر الخادم الضعيف على شبكة عامة.
لأغراض تعليمية فقط — استخدم في بيئات معزولة