
استخدام Struts2 و PowerShell لإعادة إنشاء ثغرة CVE-2017-5638 OGNL Injection
هذا المستودع هو جزء من مشروع مساق SC3010 أمن الحاسوب.
يقوم بإعادة إنشاء CVE-2017-5638، وهو ثغرة حرجة لتنفيذ التعليمات البرمجية عن بُعد في Apache Struts 2 (الإصدارات 2.3.5–2.3.31 و2.5–2.5.10)، والتي تم استغلالها بشكل مشهور في خرق بيانات Equifax عام 2017 لسرقة السجلات الشخصية لحوالي 147 مليون شخص.
تنشأ الثغرة من عيب تصميمي واحد: عندما يفشل Apache Struts في تحليل طلب تحميل multipart/form-data، يقوم ببناء رسالة خطأ عن طريق تضمين رأس Content-Type الخام — دون تعقيمه — في سلسلة نصية يتم تمريرها بعد ذلك إلى مُقيِّم تعبيرات OGNL. لذلك، يحصل المهاجم الذي يضع تعبير OGNL داخل رأس Content-Type على تنفيذه من جانب الخادم كمستخدم عملية Tomcat.
يحتوي هذا المستودع على:
SC3010-Computer-Security/
├── simulation/
│ ├── backend/ # خادم Apache Struts2 2.3.28 ضعيف (Java/Maven)
│ └── attack-script/ # نص استغلال لـ CVE-2017-5638
│ └── exploit_cve_2017_5638.ps1 # PowerShell (متعدد المنصات)
├── struts-src-code/ # المصدر المرجعي لـ Apache Struts2 + إشعارات قانونية
│ ├── licenses/ # الترخيص، الإشعار، وتراخيص المكونات
│ └── src/
│ ├── struts2-core/ # فئات خط أنابيب الطلب + JakartaMultiPartRequest الضعيفة
│ ├── xwork2/ # ActionContext.java، OgnlUtil.java
│ └── ognl/ # OgnlContext.java
├── diagrams/ # مخططات آلة الحالة + مخططات التسلسل، حمولة OGNL مشروحة
└── _notes/ # قراءة خلفية
يظهر المخطط أدناه كل فرع قرار في خط أنابيب طلبات Struts2. المسار الأحمر هو المسار الذي يجبره المهاجم؛ → ... باللون الرمادي هي مسارات آمنة لا تنطبق أثناء الاستغلال.
flowchart TD
Start([يرسل المهاجم طلب HTTP POST /upload.action<br/>Content-Type: %{OGNL_PAYLOAD}.multipart/form-data])
Start --> PIPE
PIPE["<b>خط أنابيب Struts2: تصفية → تغليف → تحليل</b><br/>doFilter() → PrepareOperations.wrapRequest()<br/>→ Dispatcher.wrapRequest() → new MultiPartRequestWrapper()"]
PIPE --> D_CT{"هل يحتوي Content-Type<br/>على "multipart/form-data"؟"}
D_CT -- "لا → طلب عادي ..." --> OUT_NORM([مسار غير تحميل ...])
D_CT -- "نعم (المهاجم يلحق<br/>.multipart/form-data بالحمولة)" --> PARSE
PARSE[/"<b>JakartaMultiPartRequest.parse()</b><br/>→ Commons FileUpload.parseRequest()"/]
PARSE --> D_VALID{"هل يمكن لـ FileUpload تحليل<br/>Content-Type؟"}
D_VALID -- "نعم → تحميل ملف عادي ..." --> OUT_OK([تم استخراج الملفات، تنفيذ الإجراء ...])
D_VALID -- "لا — Content-Type غير صالح<br/>يطرح InvalidContentTypeException<br/>(الرسالة = سلسلة Content-Type الخام)" --> CATCH
CATCH["كتلة catch في parse()<br/>تستدعي buildErrorMessage(e, {})"]
CATCH --> FIND
FIND[/"<b>buildErrorMessage()</b><br/>→ LocalizedTextUtil.findText(class, errorKey, locale, e.getMessage(), args)"/]
FIND --> D_KEY{"هل تحتوي حزمة الموارد على مفتاح<br/>struts.messages.upload.error.*؟"}
D_KEY -- "نعم → رسالة مترجمة آمنة ..." --> OUT_SAFE([تم عرض الخطأ بأمان ...])
D_KEY -- "لا — يتراجع إلى<br/>e.getMessage() كقيمة افتراضية<br/>(Content-Type الخام للمهاجم)" --> TRANSLATE
TRANSLATE["TextParseUtil.translateVariables()<br/>يفحص السلسلة بحثًا عن تعبيرات %{...}"]
TRANSLATE --> D_OGNL{"هل تحتوي السلسلة على<br/>تعبير OGNL %{...}؟"}
D_OGNL -- "لا → سلسلة خطأ عادية ..." --> OUT_PLAIN([رسالة خطأ آمنة ...])
D_OGNL -- "نعم — يتم تقييم OGNL<br/>داخل محتوى المهاجم" --> SANDBOX
SANDBOX[/"<b>تجاوز صندوق الحماية OGNL</b><br/>#ognlUtil.getExcludedClasses().clear()<br/>#ognlUtil.getExcludedPackageNames().clear()"/]
SANDBOX --> ACCESS
ACCESS[/"<b>انعكاس غير مقيد</b><br/>#context.setMemberAccess(DEFAULT_MEMBER_ACCESS)"/]
ACCESS --> RCE
RCE[/"<b>RCE</b><br/>Runtime.getRuntime().exec(cmd)"/]
RCE --> Done([تم تنفيذ الأمر كمستخدم عملية Tomcat])
style Start fill:#d32f2f,color:#fff
style Done fill:#d32f2f,color:#fff
style OUT_NORM fill:#9e9e9e,color:#fff
style OUT_OK fill:#9e9e9e,color:#fff
style OUT_SAFE fill:#9e9e9e,color:#fff
style OUT_PLAIN fill:#9e9e9e,color:#fff
style SANDBOX fill:#b71c1c,color:#fff
style ACCESS fill:#b71c1c,color:#fff
style RCE fill:#b71c1c,color:#fff
style CATCH fill:#e65100,color:#fff
style TRANSLATE fill:#e65100,color:#fffلآلة الحالة الكاملة مع شروح الفروع، راجع diagrams/cve-2017-5638-state-machine.md.
لرسم تخطيطي للتسلسل وتحليل حمولة OGNL المشروحة، راجع diagrams/cve-2017-5638-attack-chain.md.
يستنسخ هذا المستودع أجزاءً من كود مصدر Apache Struts 2.3.28 لأغراض البحث الأكاديمي في مجال الأمن.
Apache Struts حقوق النشر © 2000–2016 مؤسسة برمجيات Apache.
مرخصة بموجب رخصة Apache، الإصدار 2.0.
نسخة من الترخيص متاحة فيstruts-src-code/licenses/LICENSE.txt.
إشعار الإسناد الكامل المطلوب بموجب رخصة Apache موجود فيstruts-src-code/licenses/NOTICE.txt.
يحتوي Apache Struts 2 على مكونات إضافية من طرف ثالث، يخضع كل منها لترخيصه الخاص:
| المكون | ملف الترخيص |
|---|---|
| OGNL (مكتبة التنقل بين الكائنات البيانية) | struts-src-code/licenses/OGNL-LICENSE.txt |
| XWork | struts-src-code/licenses/XWORK-LICENSE.txt |
| FreeMarker | struts-src-code/licenses/FREEMARKER-LICENSE.txt |