
إثبات مفهوم لـ Gitea restore-repo file:// تضمين ملف محلي (CWE-73) ينسخ أهداف الروابط الرمزية خارج تفريغ غير موثوق إلى مرفقات الإصدار.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-restore-file-open
Gitea 1.27.3 - Gitea
CVE-2026-59765 وضع عميل HTTP من نوع hostmatcher على OpenWithClient. يتجاهل فرع file:// ذلك العميل ويستدعي os.Open. لا يحتوي استعادة dump على DownloadFunc. بعد ذلك يفتح gitea_uploader.go عنوان URL المعاد كتابته file://. يتبع os.Open رابطًا رمزيًا من جهة الـ dump. يبقى Join تحت الـ dump. أما الرابط الرمزي فلا.
استعادة المشغّل لـ dump غير موثوق تنسخ هدف الرابط الرمزي إلى مرفق إصدار. بصفته uid الخاص بـ Gitea، هذا أي ملف يمكن لحساب الخدمة قراءته.
| ID | لا يوجد CVE بعد |
| CWE | CWE-73 |
| CVSS | متوسط: 4.2 CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N |
| المنتج | Gitea |
| المتأثر | حتى v1.27.3 (146cc3e)؛ مشغّل محلي gitea restore-repo |
| المصادقة | استعادة مشغّل محلي لـ dump غير موثوق |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
سلّم المسؤول dump غير موثوق (دليل "نسخة احتياطية"، استعادة من remote غير موثوق) يحتوي على رابط رمزي تحت المسار المضموم تحت الـ dump. تنسخ الاستعادة الملف الهدف إلى مرفق إصدار على المستودع المستعاد. من يستطيع قراءة ذلك المستودع يقوم بتنزيله.
إذا سمّى مؤلف الـ dump الملف app.ini، فهذه سرقة بيانات اعتماد (INTERNAL_TOKEN، SECRET_KEY، JWT، كلمة مرور قاعدة البيانات). كان oracle المختبر ملف شاهد مزروع، وليس app.ini. ليس LPE على مستوى نظام التشغيل. ليس غير مصادق. ليس هروب join من جذر الـ dump. الترحيل الحي من GitHub/GitLab/Gitea يضبط DownloadFunc ولا يستدعي os.Open على file:// من المهاجم.
كانت خريطتا استعادة موجودتين على الجدول المتبقي. اختبرت الواضحة أولاً.
الاكتشاف 8 كان هروب join من جذر الـ dump: ضع DownloadURL مطلقًا في release.yml، وتمنَّ أن يُسقط FilePathJoinAbs بادئة الـ dump بالطريقة التي يفعلها filepath.Join(base, "/etc/passwd") الساذج. في 1.27.3 لا يفعل ذلك. كل sub هو filepath.Clean("/"+s) ثم filepath.Join(base, cleaned). بقي join النسخة المتماثلة لـ /tmp/dump + /tmp/GITEA-JOIN-OUTSIDE عند /tmp/dump/tmp/GITEA-JOIN-OUTSIDE. أعادت الاستعادة كتابة file:// تحت الـ dump وفشلت بـ open ...: no such file or directory. هذا ليس هروب join على هذا الإصدار.
الاكتشاف 9 هو ما نجح. زرعت ملف شاهد خارج الـ dump، ووضعت رابطًا رمزيًا عند المسار المضموم تحت الـ dump، وشغّلت gitea restore-repo. الاستعادة rc=0. احتوى مرفق الإصدار المستعاد على GITEA-FILE-OPEN-WITNESS.
المنعطفات الخاطئة المسجلة مسبقًا: هروب join من جذر الـ dump عبر DownloadURL مطلق؛ LFI عبر file:// في الترحيل الحي؛ X-Gitea-Internal-Auth (الاستعادة CLI بصفة uid الخاص بـ Gitea، وليس /api/internal)؛ معاملة استعادة HTTP كغير مصادق عن بعد (CVSS محلي، صلاحية عالية، تفاعل مستخدم)؛ reverse shell. مسرحية.
cd lab
./run.sh
الهدف فقط http://127.0.0.1:18137. يثبّت Compose lab/dump للقراءة فقط. يزرع run.sh الشاهد داخل الحاوية، ويبني الـ dump، ويضع الرابط الرمزي، ثم gitea restore-repo.
restore-rc=0 Restore repo labadmin/restored successfully
download status=200 snippet=GITEA-FILE-OPEN-WITNESS
SUCCESS GITEA-RESTORE-FILE-OPEN
أزل file:// من OpenWithClient، أو ارفض اتباع روابط الـ dump الرمزية، ولا تستعد dumps غير موثوقة. يجب ألا يحتوي المرفق المستعاد على الشاهد خارج الـ dump.
uri.go · gitea_uploader.go · FilePathJoinAbsGNU Affero GPL v3.0. انظر LICENSE. مختبر loopback فقط. لا ضمان.