
تقرير التحقيق عن ثغرات Struts2 S2-045, S2-055 وثغرات Jackson CVE-2017-7525, CVE-2017-15095
تم نشر مقال ملخص يلخص النقاط الرئيسية ويجعله سهل القراءة. يُوصى به لمن يرغب في معرفة نظرة عامة أولاً أو لمن ليس لديه وقت كافٍ.
في 1 ديسمبر 2017، تم إصدار تحديث أمني لـ Struts2. قبل الإصدار، كانت هناك مناقشات في القائمة البريدية تفيد بأن ثغرات Jackson (مكتبة JSON الشائعة في Java) مرتبطة بها، وكان الكاتب، الذي يستخدم Jackson في الأنظمة والأدوات الداخلية للشركة، مهتمًا بمعرفة التفاصيل الدقيقة.
في الواقع، تم إصلاح مشكلتين أمنيتين في المحتوى المنشور. الثغرة في jackson-databind، وهي مكون من Jackson، تؤثر فقط على S2-055.
في REST plugin، يبدو أنه تم تضمين معالجات تستخدم JSON-lib ومعالجات تستخدم Jackson منذ البداية، ويمكن للمستخدم الاختيار بينهما. في S2-054، تم تبديل المعالج الافتراضي إلى Jackson، ثم في S2-055 تم تحديث إصدار Jackson القديم إلى الأحدث، وهذا هو جوهر التصحيح الحالي.
إذن، ما هي ثغرة CVE-2017-7525 بالضبط؟ بما أن الكاتب يستخدم Jackson غالبًا عند معالجة JSON في Java، فقد قضى عطلة نهاية الأسبوع في 2 و 3 ديسمبر للتحقيق في هذه المسألة، وهذا هو محتوى هذه المقالة.
بيئة الكاتب المستخدمة للتحقق من نموذج الكود:
تم نشر شرح لـ CVE-2017-7525 في مدونة Adam Caudill.
باختصار بكلمات الكاتب نفسه، يوفر jackson-databind وظيفة لتعيين JSON إلى كائنات Java (فئة ObjectMapper).
عن طريق استدعاء ObjectMapper.enableDefaultTyping()، يصبح من الممكن التعيين باستخدام اسم فئة مضمن بشكل مخصص في JSON.
ربما شعر بعض القراء بإحساس سيء بمجرد "إمكانية تحديد اسم الفئة من إدخال JSON"، وهذا الإحساس السيئ تحقق تمامًا في CVE-2017-7525.
قبل الدخول في شرح الثغرة، دعنا نشرح لماذا تم تنفيذ هذه الوظيفة في المقام الأول.
يرجى مراجعة نموذج الكود التالي للاستخدام الأساسي لإلغاء التسلسل (deserialize) بواسطة jackson-databind. (في هذه المقالة، نستخدم Groovy في نموذج كود Jackson. من المريح أنه يمكن تبديل إصدار jackson-databind بسهولة باستخدام @Grab.)
في نموذج الكود أعلاه، يمكن ببساطة تعيين مفتاح "animal" إلى فئة Animal. وماذا عن الحالة التالية؟```java class Zoo { Animal animal; }
abstract class Animal { String name; protected Animal() { } }
class Dog extends Animal { double barkVolume; Dog() { } }
class Cat extends Animal { boolean likesCream; int lives; Cat() { } }
في هذا التكوين، يظهر نوعان: عندما يشير مفتاح "animal" إلى فئة Dog، وعندما يشير إلى فئة Cat. وبالتالي، نحتاج إلى معلومات إضافية لتحديد أي من الفئتين سيتم استخدامها للتعيين.
لحل هذه المشكلة، أضافت jackson-databind معالجة خاصة تسمح بدمج اسم الفئة المستخدمة في التعيين داخل JSON.
على سبيل المثال، كما هو موضح أدناه، يتم تحويل محتوى مفتاح "animal" إلى مصفوفة، ويتم تحديد اسم الفئة في العنصر الأول.```
{"animal":["Dog",{"name":"dog1","barkVolume":1.2}]}
وبالتالي فإن ObjectMapper.readValue() يتعرف على أن محتوى المفتاح "animal" هو من فئة Dog ويقوم بالتخطيط.
بالطبع، لا يمكن التمييز مباشرةً ما إذا كان محتوى المفتاح "animal" كان في الأصل مصفوفة أم كان يحتوي على معلومات اسم الفئة الخاصة بـ jackson-databind.
الطريقة التي تسمح بالتبديل بين هذين النمطين هي ObjectMapper.enableDefaultTyping().
هناك أيضًا طريقة لتعريف التعليق التوضيحي @JsonTypeInfo في الفئة. لمزيد من التفاصيل، راجع وثائق Jackson التالية.
فيما يلي نموذج لاستخدام طريقة ObjectMapper.enableDefaultTyping() فعليًا.
كما رأينا أعلاه، من خلال إعطاء اسم الفئة ثم خصائصها بتنسيق JSON، يمكن إنشاء أي فئة بأي خصائص، على الرغم من وجود بعض القيود. واستغلال هذه الثغرة هو ثغرة CVE-2017-7525، ويُعتقد أن الدافع وراء ذلك هو التقرير التالي.
بالنسبة لمكتبات التسلسل وإلغاء التسلسل شائعة الاستخدام في Java مثل Jackson، تم الإبلاغ عن خطر يؤدي إلى تنفيذ رمز عشوائي عن طريق التلاعب بأسماء الفئات، وتم إدراج أسماء الفئات المحددة التي تشكل خطرًا. لا أعرف ما إذا كان ذلك ردًا على ذلك، لكن من حيث التاريخ، بعد أول commit للمستودع المذكور أعلاه مباشرة، تم إنشاء Issue التالي في jackson-databind وبدأ العمل على معالجته.
كيف يبدو بالفعل بيانات JSON ورمز Java الذي يستغل هذه الثغرة؟ هناك تلميح في كود الاختبار الخاص بـ jackson-databind 2.8.9 الذي تم معالجته في هذه المشكلة:
فيما يلي نموذج كود تم تعديله بناءً على كود الاختبار هذا للتحقق من العمل.
عند تشغيله مع تحديد @Grab للإصدار 2.8.9، سيظهر your jackson version IS SAFE to CVE-2017-7525. ويرجع ذلك إلى أنه في الإصدار 2.8.9 تمت إضافة فحص القائمة السوداء لأسماء الفئات التي سيتم إنشاء مثيل لها.
إذا تم تحديد @Grab للإصدار 2.8.8 وتشغيله، فسيكون الإخراج كالتالي.```
your jackson version MAY NOT BE SAFE to CVE-2017-7525
com.fasterxml.jackson.databind.JsonMappingException: N/A
at [Source:
{
"id" : 124,
"obj" : [
"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
{
"transletBytecodes" : [ "AAIAZQ==" ],
"transletName" : "a.b",
"outputProperties" : { }
}
]
}
; line: 9, column: 28] (through reference chain: Bean1599["obj"]->com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl["outputProperties"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:277)
(...)
at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:128)
Caused by: java.lang.NullPointerException
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.defineTransletClasses(TemplatesImpl.java:399)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:451)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties(TemplatesImpl.java:507)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet(SetterlessProperty.java:116)
... 30 more
null
يوجد في نتائج الإخراج سطر `at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401)`.
في هذا الكود النموذجي، يتم طرح NullPointerException، ولكن من اسم الطريقة، يمكن استنتاج أن هناك عملية تتضمن بعض الآثار الجانبية قيد التنفيذ.
لبناء JSON يمكنه تنفيذ الكود بنجاح، يبدو أن هناك حاجة لمزيد من البحث.
في هذه المقالة، سنكتفي بهذا القدر من المقدمة في الوقت الحالي، ولكن إذا تم نشر مقالات بحثية أخرى في مواقع أخرى، فنود إضافة المزيد هنا أيضًا.
بالمناسبة، أين يتم تنفيذ القائمة السوداء الحقيقية؟ إنها في الفئة التالية:
* https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.8.9/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java#L51
في الواقع، يبدو أن هناك ثغرة في هذه القائمة السوداء في الإصدار 2.8.9. هذه المشكلة هي CVE-2017-15095.
### التعامل مع CVE-2017-15095 الذي يحسن القائمة السوداء
فيما يتعلق بتحسين الثغرة في القائمة السوداء، تم أولاً إضافة `s.add("com.sun.rowset.JdbcRowSetImpl");` في https://github.com/FasterXML/jackson-databind/issues/1680.
بعد ذلك، تم إصدار 2.9.0، ثم تمت إضافة فحص القائمة السوداء التالي في https://github.com/FasterXML/jackson-databind/issues/1737.```java
// [databind#1737]; JDK provided
s.add("java.util.logging.FileHandler");
s.add("java.rmi.server.UnicastRemoteObject");
// [databind#1737]; 3rd party
s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor");
s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean");
s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource");
s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource");
مع هذا، تم إصدار 2.8.10 / 2.9.1، واكتمل الرد على CVE-2017-15095.
رمز اختبار للتحقق من تشغيل القائمة السوداء في 2.8.10:
بناءً على رمز الاختبار هذا، يظهر أدناه نموذج رمز تم تعديله للتحقق من التشغيل.
عند تشغيل 2.8.10 الذي يُفترض أن الرد عليه قد اكتمل باستخدام @Grab، تظهر الرسالة your jackson version IS SAFE to CVE-2017-15095.
ثم عند تشغيل 2.8.9 قبل تحسين القائمة السوداء باستخدام @Grab، يتم إخراج ما يلي:```
your jackson version MAY NOT BE SAFE to CVE-2017-15095
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.util.logging.FileHandler, problem: \tmp\foobar.txt.lck
at [Source:
{
"v" : [
"java.util.logging.FileHandler",
"/tmp/foobar.txt"
]
}
; line: 5, column: 5] (through reference chain: PolyWrapper["v"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:277)
(...)
Caused by: java.nio.file.NoSuchFileException: \tmp\foobar.txt.lck
(...)
at java.util.logging.FileHandler.openFiles(FileHandler.java:459)
at java.util.logging.FileHandler.(FileHandler.java:292)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call1(AnnotatedConstructor.java:129)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:318)
... 31 more
null
في الإصدارات السابقة للإصلاح، كان من الممكن أن يتجاوز اسم الفئة `java.util.logging.FileHandler` فحص القائمة السوداء، ويتم إنشاء مثيل له لمحاولة فتح ملف فعليًا.
أما في الإصدارات بعد الإصلاح، فيتم اكتشافه بواسطة فحص القائمة السوداء، ويتم رمي استثناء `JsonMappingException`.
علاوة على ذلك، أصبحت القائمة السوداء في الإصدار 2.8.10 كما يلي. الجزء الذي يبدأ بتعليق `[databind#1737]` هو القائمة السوداء الإضافية التي تمت إضافتها لمعالجة CVE-2017-15095.
* https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.8.10/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java#L52
### حول شروط التأثر بالثغرة، وإمكانية تحقيق الهجوم، والإجراءات على جانب التطبيق
بتلخيص النتائج أعلاه، يتطلب التأثر بثغرة jackson-databind الشروط التالية:
1. استخدام jackson-databind 2.8.9 / 2.9.0 أو أقل.
2. معالجة JSON تم الحصول عليه من مصدر غير موثوق بأحد الطرق التالية:
* استدعاء `ObjectMapper.enableDefaultTyping()` ثم القيام بعملية إلغاء التسلسل (deserialize).
* عدم استدعاء `ObjectMapper.enableDefaultTyping()`، ولكن استخدام التعليق التوضيحي `@JsonTypeInfo` في تعريف الفئة للسماح بالتعيين، ثم القيام بعملية إلغاء التسلسل.
* حتى لو لم يتم استخدامه في كود التطبيق، فقد يقوم الإطار (framework) بإلغاء التسلسل تلقائيًا بناءً على رأس الطلب `Accept` أو امتداد URL.
3. احتواء مسار الفئات (classpath) على فئة (Gadget) يحتمل استغلالها في ثغرة Java serialize/deserialize.
4. استخدام نوع في حقل عضو لفئة Java الهدف يمكنه قبول فئة Gadget، مثل النوع Object.
* إذا كان النوع المحدد هو فئة Bean خاصة بالتطبيق غير متوافقة مع الفئات المستخدمة في Gadget، فيمكن رفضها قبل إنشاء المثيل الفعلي بسبب خطأ في التحقق من النوع. (باستثناء الحالات التي تحتوي فيها فئة Bean الخاصة بالتطبيق نفسها على ثغرة في إلغاء التسلسل).
يبدو أن التأثر الفعلي يعتمد بشكل كبير على كيفية استخدام/حالة تكوين `ObjectMapper` ودمج التعليق التوضيحي `@JsonTypeInfo`، بالإضافة إلى الحقول الأعضاء في الفئة الهدف، وما إلى ذلك من كود جانب التطبيق.
أيضًا، إذا كان اسم المفتاح في JSON غير موجود في فئة Java الهدف لإلغاء التسلسل، فسيتم تجاهله ببساطة بواسطة Jackson.
لذلك، لنجاح الهجوم، يلزم تخصيص الهجوم ليتناسب مع JSON الخاص بكل تطبيق على حدة، مما يجعل من الصعب جدًا إنشاء كود هجوم يمكن إعادة استخدامه عبر تطبيقات متعددة.
علاوة على ذلك، فيما يتعلق بالشرط رقم 4، في أسلوب الكتابة العام، أعتقد أنه لا أحد سيجعل حقل فئة Java من النوع Object عن قصد.
كما أن الفئات التي تسمح بتنفيذ كود عشوائي تكون في الأغلب غير متوافقة مع فئات Bean التي يتم إنشاؤها في التطبيقات.
بناءً على ما سبق، أعتقد أن احتمال حدوث هجوم واسع النطاق أو حدوث أضرار فعلية عن طريق استغلال هذه الثغرة (أي نجاح الهجوم) هو احتمال منخفض.
أما بالنسبة للإجراءات على جانب التطبيق، فيما يتعلق بالشرط رقم 3، نظرًا لإمكانية استغلال الفئات المضمنة في JDK أيضًا، فمن المستحيل عمليًا اتخاذ إجراء ضده.
لذلك، فإن الإجراء الأساسي هو تحديث jackson-databind إلى الإصدار الأحدث.
إذا تعذر تحديث jackson-databind إلى الإصدار الأحدث، فسيكون الحل هو إزالة استدعاء `ObjectMapper.enableDefaultTyping()` أو التعليق التوضيحي `@JsonTypeInfo`، وإعادة تصميم النظام بحيث لا يعتمد عليهما. على سبيل المثال، يمكن إنشاء مسلسل (serializer) مخصص.
ومع ذلك، إذا تم بالفعل استخدامه كـ API يمكن استدعاؤه عن بعد، فليس من السهل تغيير تنسيق JSON.
أما بالنسبة للتعليق التوضيحي `@JsonTypeInfo`، فيبدو أنه يمكن تكوينه لقبول أسماء فئات فرعية محددة فقط، وذلك حسب الإعدادات، مما يسمح بقبول أسماء الفئات التي يتوقعها المبرمج فقط.
يرجى مراجعة المستندات التالية للحصول على التفاصيل.
* JacksonPolymorphicDeserialization
* https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization
#### حول جدوى إجراء القائمة السوداء وإنشاء مسلسل مخصص
قام jackson-databind 2.8.10 / 2.9.1 بمعالجة CVE-2017-7525 و CVE-2017-15095 من خلال إجراء القائمة السوداء.
ومع ذلك، كما هو واضح من مشاكل OGNL في Struts2، فإن إجراء القائمة السوداء ليس مضمونًا تمامًا.
(على المستوى الشخصي للكاتب، أعتقد أن هذا الإجراء فعال إلى حد ما ضد ما يسمى بـ "script kiddies" الذين يكتفون باستخدام أدوات المسح الضوئي).
لذلك، يعتقد الكاتب أنه لاتخاذ إجراء جذري حقًا، من المهم تعطيل/عدم استخدام الميزات التي تقوم بتضمين معلومات الفئة في JSON، مثل `ObjectMapper.enableDefaultTyping()`.
فما هو الحل للمشكلة التي كان `ObjectMapper.enableDefaultTyping()` يحاول حلها في المقام الأول؟
بخصوص هذا الأمر، لم يتمكن الكاتب نفسه من إعداد بديل يمكن اعتباره الحل الصحيح.
أعتقد أن جوهر المشكلة هو "القدرة على التعيين عندما تكون فئة Java الهدف غامضة، دون الاعتماد على JSON غير الموثوق به".
كأسلوب لتحقيق ذلك، يعتقد الكاتب أنه ربما يكون هناك أسلوب يتمثل في إنشاء مسلسل مخصص (custom deserializer).
يسمح المسلسل المخصص لجانب كود البرنامج باستقبال JSON أثناء عملية إلغاء التسلسل، والتحكم في الكائن الذي سيتم إنشاؤه بنفسه.
على سبيل المثال، إذا وصل `{"animal":{"name":"dog1","barkVolume":1.2}}`، يمكن للبرنامج أن يقرر "بما أن هناك مفتاح `barkVolume`، فهذا يعني أنه سيتم إنشاء مثيل له كفئة Dog".
وإذا وصل `{"animal":{"name":"cat1","likesCream":true,"lives":10}}`، يمكن للبرنامج أن يقرر "بما أن هناك مفتاح `likesCream` ومفتاح `lives`، فهذا يعني أنه سيتم إنشاء مثيل له كفئة Cat".
باستخدام هذا، لا داعي لتضمين اسم الفئة في JSON عن قصد.
شخصيًا، شعرت أن تنسيق JSON الذي يتضمن اسم الفئة يعيق قابلية التشغيل البيني مع اللغات/المكتبات الأخرى (إذا كان هناك امتداد مماثل في لغات أو مكتبات أخرى، فيرجى إبلاغي بذلك).
إذا كان الهدف هو التشغيل البيني مع أنظمة أخرى، أعتقد أنه من الأفضل إنشاء مسلسل مخصص في Jackson بدلاً من مطالبتهم بتضمين اسم الفئة بشكل مخصص وفقًا لراحة Jackson.
أعتقد أن هناك عدة حلول أخرى، وسأكون ممتنًا إذا كان لدى القراء أي رأي حول "هل هناك حل مثل هذا أيضًا؟".
(كمثال متطرف، أعتقد أن هناك أيضًا طريقة لإلغاء التسلسل إلى تنسيق `Map<String, Object>` أو `List<String, Object>` دون التعيين إلى فئة فردية).
لقد وجدت بعض المقالات المرجعية لإنشاء مسلسل مخصص، لذا سأضع روابط لها بالرغم من أنها باللغة الإنجليزية.
* Jackson: create a custom JSON deserializer with StdDeserializer and JsonToken classes | Dede Blog
* http://www.davismol.net/2015/05/20/jackson-create-a-custom-json-deserializer-with-stddeserializer-and-jsontoken-classes/
* Getting Started with Deserialization in Jackson | Baeldung
* http://www.baeldung.com/jackson-deserialization
* Custom JSON Deserialization with Jackson - DZone Integration
* https://dzone.com/articles/custom-json-deserialization-with-jackson
* Building a Custom Jackson Deserializer - The Boy Wonders
* http://www.robinhowlett.com/blog/2015/01/01/building-a-custom-jackson-deserializer/
JavaDoc الخاص بـ jackson-databind (الإصدارات 2.8، 2.9):
* https://fasterxml.github.io/jackson-databind/javadoc/2.8/
* https://fasterxml.github.io/jackson-databind/javadoc/2.9/
لقد قمت أيضًا بإنشاء نموذج كود لمسلسل مخصص، وآمل أن يكون مفيدًا كتلميح.
* [custom-deserializer-demo.groovy](https://github.com/SecureSkyTechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095/blob/master/custom-deserializer-demo.groovy)
## حول S2-055
حتى الآن، نظرنا في ثغرات Jackson نفسها. الآن، كيف تؤثر هذه الثغرات فعليًا على REST plugin الخاص بـ Struts2؟ لقد قمنا بالتحقق باستخدام struts2-rest-showcase المرفق مع Struts2.
تم الرجوع إلى الطريقة التالية لاستخدام Struts REST plugin:
* http://struts.apache.org/plugins/rest/
### REST plugin الخاص بـ Struts2 لا يستدعي ObjectMapper.enableDefaultTyping()
بالمناسبة، كانت شروط التعرض الفعلي للثغرة CVE-2017-7525 تتضمن الشرط التالي:
* معالجة JSON تم الحصول عليه من مصدر غير موثوق بأحد الطرق التالية:
* استدعاء `ObjectMapper.enableDefaultTyping()` ثم القيام بعملية إلغاء التسلسل.
* عدم استدعاء `ObjectMapper.enableDefaultTyping()`، ولكن استخدام التعليق التوضيحي `@JsonTypeInfo` في تعريف الفئة للسماح بالتعيين، ثم القيام بعملية إلغاء التسلسل.
عند التحقق مما إذا كان REST plugin الخاص بـ Struts2 يحتوي على كود يطابق هذه الشروط، تم تأكيد عدم تضمين أي منهما.
في الواقع، في الإصدار 2.5.14 قبل الإصلاح، لم يتم استخدام أي من `ObjectMapper.enableDefaultTyping()` أو `@JsonTypeInfo` سواء في REST plugin نفسه أو حتى عند البحث (grep) في شجرة المصادر الكاملة لـ Struts2.
* https://github.com/apache/struts/tree/STRUTS_2_5_14
الفئة الوحيدة في شجرة المصادر الكاملة لـ Struts2 التي تستخدم ObjectMapper الخاص بـ Jackson هي فئة `org.apache.struts2.rest.handler.JacksonLibHandler`. عند مراجعة كود المصدر، تأكدنا أنه في الإصدار 2.5.14، لم يتم استخدام `ObjectMapper.enableDefaultTyping()` بالفعل.
* https://github.com/apache/struts/blob/STRUTS_2_5_14/plugins/rest/src/main/java/org/apache/struts2/rest/handler/JacksonLibHandler.java
* محتوى ملف Java هذا لم يتغير في الإصدار 2.5.14.1.
لهذا السبب، يُعتقد أن REST plugin نفسه لم يكن به مشكلة حتى الإصدار 2.5.14 فيما يتعلق بـ CVE-2017-7525.
يكون التعرض للخطر فقط في حالة قيام التطبيق بتعيين `@JsonTypeInfo` في حقل الفئة التي يتم التعيين إليها في JSON.
لذلك، في التحقق التالي باستخدام struts2-rest-showcase، تم إعداد `@JsonTypeInfo` في حقل التعيين الإضافي على جانب التطبيق للتحقق.
بالمناسبة، يبدو أن Struts2 يحتوي أيضًا على JSON plugin.
* http://struts.apache.org/plugins/json/
* عند النظر إلى كود مصدر JSON plugin، يبدو أن pom.xml لا يضع أي مكتبات JSON أخرى كمكتبات تابعة.
* يبدو أنه يقوم بمعالجة JSON بشكل مستقل.
* https://github.com/apache/struts/tree/STRUTS_2_5_14/plugins/json
* لذلك، يُعتقد أن ثغرة jackson-databind لا تؤثر على JSON plugin.
### جعل struts2-rest-showcase متوافقًا مع Jackson
struts2-rest-showcase هو نموذج يستخدم REST plugin لتنفيذ عمليات CRUD لفئة Order. سنقوم هنا بإضافة فئات Zoo / Animal / Cat / Dog المستخدمة في نموذج كود ثغرة Jackson، بالإضافة إلى ZooController لمعالجة CRUD الخاص بها بتنسيق JSON.
يرجى مراجعة كود المصدر الكامل أدناه. (تم تأكيد البناء والتنفيذ باستخدام JDK8 في هذه المقالة)
* [rest-showcase](https://github.com/SecureSkyTechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095/tree/master/rest-showcase)
النقاط الرئيسية للتعديل:
* تم تغيير منفذ الاستماع الخاص بـ jetty-maven-plugin إلى 18088. (باستخدام `mvn jetty:run`)
* تمت إضافة jackson-core و jackson-databind كتبعيات.
* تمت إضافة فئات Zoo و Animal (abstract) و Dog و Cat. وتمت إضافة فئة ZooService كطبقة خدمة.
* تمت إضافة ZooController مع حد أدنى من عمليات CRUD. (تم حذف JSP الخاص بالعرض)
* تم تغيير معالج تنسيق json في struts.xml إلى JacksonLibHandler.
* تم تضمين maven-wrapper، بحيث يمكن البناء والتنفيذ مباشرة باستخدام `mvnw` / `mvnw.bat` طالما أن JDK مثبت.
البناء والتنفيذ:
1. بعد استنساخ المستودع، انتقل إلى دليل rest-showcase وقم بتنفيذ `mvnw jetty:run`. (يرجى ملاحظة أنه في المرة الأولى للتنفيذ، سيتم تنزيل maven، مما قد يستغرق بضع دقائق أو حتى أكثر من 10 دقائق في بعض الحالات)
1. قم بزيارة http://localhost:18088/struts2-rest-showcase/، إذا تم عرض قائمة Orders، فهذا يعني النجاح.
1. يمكنك إنهاء التنفيذ بالضغط على Ctrl-C.
1. إذا قمت بتعديل ملف Java، فقم بإنهاء التنفيذ بالضغط على Ctrl-C ثم قم بتنفيذ `mvnw jetty:run` مرة أخرى.
التحقق من التشغيل باستخدام أمر curl: (بافتراض المرور عبر proxy محلي http على localhost:8080)```
一覧取得:
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo"
ID指定:
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo/1"
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo/2"
削除:
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo/2" -X DELETE
في ملف Zoo.java في المستودع، حقل animal هو على النحو التالي: @JsonTypeInfo تم التعليق عليها، والنوع هو Animal من abstract class.```java
//@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
public Animal animal;
//public Object animal;
هنا، سنرسل طلب JSON باستخدام طريقة POST، ونستدعي طريقة ZooController.create() .```
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":{"name":"dog2","barkVolume":2.3}}'
ثم حدث استثناء يحتوي على رسالة الخطأ التالية. فئة Animal مجردة، لذلك تعذر إنشاء مثيل لها.``` Can not construct instance of org.demo.rest.example.Animal: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
لذلك، قم بإلغاء تعليق `@JsonTypeInfo` الخاص بحقل `animal` وتفعيله. أوقف تطبيق الويب باستخدام Ctrl-C ثم أعد تشغيل `mvnw jetty:run`.```java
// 次のimportを忘れずに追加
import com.fasterxml.jackson.annotation.JsonTypeInfo;
//...
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
public Animal animal;
//public Object animal;
بهذا يمكننا تضمين أسماء الفئات داخل JSON. جرّب إرسال طلب POST باستخدام الأمر curl التالي مع JSON يحتوي على أسماء الفئات.``` curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":["org.demo.rest.example.Dog",{"name":"dog2","barkVolume":2.3}]}'
→ يتم إرجاع `HTTP/1.1 201 Created`. عند محاولة جلب القائمة، يمكن التأكد من أنه قد تمت إضافته بالفعل.
### التحقق من CVE-2017-7525 في Struts2 REST plugin 2.5.14
في ملف pom.xml للمستودع، نظرًا لأن artifact الخاص بـ struts في `<parent>` يحدد الإصدار 2.5.14، فإنه يكون عرضة لـ CVE-2017-7525 بشكل افتراضي.
لتأكيد ذلك، قم بتشغيل أمر curl التالي. اسم الفئة ومحتوياتها مستندان إلى cve-2017-7525-check.groovy، لذا إذا تم إرجاع نفس الاستجابة كما في jackson-databind 2.8.8، فإنه يعتبر عرضة للثغرة.```
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":["com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",{"transletBytecodes":["AAIAZQ=="],"transletName":"a.b","outputProperties":{}}]}'
→ تم إلقاء استثناء يحتوي على رسالة الخطأ التالية.``` java.lang.IllegalArgumentException: Class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl not subtype of [simple type, class org.demo.rest.example.Animal]
يبدو أن الحقل `animal` من النوع `Animal` وليس من النوع الفرعي للفئة `com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl`، مما تسبب في ظهور استثناء `IllegalArgumentException`.
الآن، سنقوم بتعديل الحقل `animal` في `Zoo.java` ليكون من النوع `Object`، ثم إعادة التشغيل باستخدام `mvnw jetty:run`.```java
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
//public Animal animal;
public Object animal;
وبهذا، عند تنفيذ أمر curl السابق مرة أخرى، حدث استثناء يتضمن تتبع المكدس التالي.``` Caused by: java.lang.NullPointerException at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401) ~[?:1.8.0_92]
هذا هو نفس الاستثناء في حالة الثغرة الأمنية كما تم التحقق منه في cve-2017-7525-check.groovy.
بناءً على ما سبق، تم تأكيد وجود ثغرة jackson-databind CVE-2017-7525 في Struts2 REST plugin 2.5.14.
كما تبين أنه بالإضافة إلى `@JsonTypeInfo`، هناك حاجة لاستخدام النوع Object.
ما يلي هو رأي شخصي للكاتب: لا يبدو من الشائع عند إنشاء فصول البيانات لواجهة برمجة تطبيقات REST أن يتم تحديد نوع الحقل عمدًا على أنه فئة Object أو فئة متوافقة مع الفئات التي يمكن استخدامها كأدوات (Gadgets) في ثغرات إلغاء التسلسل في Java. لذلك، أشعر أنه قد يكون من الصعب نجاح الهجوم فعليًا.
### التحقق من المعالجة في Struts2 REST plugin 2.5.14.1
الآن دعونا نتحقق مما إذا كانت الثغرة الأمنية قد تم إصلاحها في الإصدار 2.5.14.1.
قم بتعديل إصدار artifact `<parent>` في pom.xml إلى 2.5.14.1، وأعد التشغيل باستخدام `mvnw jetty:run`، ثم قم بتنفيذ نفس أمر curl السابق.```
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":["com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",{"transletBytecodes":["AAIAZQ=="],"transletName":"a.b","outputProperties":{}}]}'
→ حدث استثناء يحتوي على رسالة الخطأ التالية.```
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl: Illegal type (com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl) to deserialize: prevented for security reasons
هذا هو نفس الاستثناء بعد إصلاح الثغرة كما تم التحقق منه في `cve-2017-7525-check.groovy`.
عند فتح المشروع في IDE يدعم Maven مثل Eclipse، وبعد حل التبعيات، يمكنك رؤية إصدار jackson-databind بالفعل 2.9.2. إذا لم يكن لديك IDE، يمكنك استخدام `mvnw help:effective-pom` لإخراج ملف pom النهائي، وبالبحث عن jackson-databind ستجد أنه يستخدم الإصدار 2.9.2.
أما بخصوص CVE-2017-15095 فسأختصر الحديث، لكن مما سبق تأكدنا من أن الإصدار 2.5.14.1 يعالج ثغرات jackson-databind.
### PoC لـ S2-055
※ إضافة بتاريخ 2017-12-08
تم نشر مقال بحثي وتقرير PoC حول S2-055.
* بيئة ثغرة S2-055 وإعدادها وتحليلها | مدونة NSFOCUS
* http://blog.nsfocus.net/s2-055/
عند قراءتها بمساعدة ترجمة Google، يبدو أن هناك نفس الرأي حول شروط الحدوث ومدى تحقيق الهجوم.
تم أيضًا تعديل rest-showcase فعليًا، وتم عرض PoC لاتصال HTTP بدأ تشغيل الآلة الحاسبة.
لقد استعرت جزء JSON فقط وجربته أولاً مع Jackson بمفرده، وهذا هو نموذج الكود التالي.
* [cve-2017-7525-poc.groovy](https://github.com/SecureSkyTechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095/blob/master/cve-2017-7525-poc.groovy)
عند تشغيله باستخدام إصدار 2.8.8، كان الناتج في بيئة الكاتب كما يلي:```
your jackson version MAY NOT BE SAFE to CVE-2017-7525
(...)
Caused by: java.lang.NullPointerException
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401)
(...)
كما في حالة cve-2017-7525-check.groovy، تم تشغيل طريقة run() مما أدى إلى NullPointerException. لكن الآلة الحاسبة لم تعمل.
على سبيل التخمين، TemplatesImpl الخاصة بـ xalan هي فئة مضمنة في Java، لذلك من المحتمل أن Java قد قامت ببعض التعديلات. أو ربما يتطلب نجاح الهجوم الفعلي شروطًا أكثر بداهة.
في مقال الـ PoC، لم يتم ذكر إصدار Java المستخدم في التحقق، ولم نتمكن من الوصول إلى تشغيل الآلة الحاسبة. إذا ظهرت معلومات إضافية، سأود التحقق مرة أخرى.
حتى الآن، قدمنا بشكل أساسي الثغرات في Jackson CVE-2017-7525 و CVE-2017-15095 بدءًا من S2-055. الآن، سألخص نتائج التحقيق السريع حول الحالة الأخرى S2-054.
باختصار، في وقت كتابة هذا المقال (2017-12-03)، لم أعثر على معلومات محددة. كما لم أعثر على PoC لاختبار وجود الثغرة.
في صفحة الإفصاح عن S2-054، تم شرح أن ملحق REST يستخدم مكتبة JSON-lib قديمة، مما يسمح بهجوم DoS عبر طلب ضار بحمولة JSON مصممة خصيصًا.
The REST Plugin is using an outdated JSON-lib library which is vulnerable and allow perform a DoS attack using malicious request with specially crafted JSON payload.
في صفحة الإصدار 2.5.14.1 الذي تم إصداره كاستجابة لهذه المشكلة، تم ربط تذكرة JIRA المقابلة WW-4892.
قمت بفحص WW-4892، لكن لم يذكر أي شيء عن DoS أو ثغرات JSON-lib. حتى عند قراءة "الوصف"، يبدو أنه يذكر فقط أن JSON-lib قديمة وغير صيانة، وأنه تم تغيير المعالج الافتراضي إلى Jackson.
طلب السحب على GitHub كما يلي، لكنه أيضًا لا يذكر مشكلة محددة في JSON-lib.
لذا قررت إلقاء نظرة على جانب JSON-lib. الموقع الرسمي هو التالي:
وأيضًا اعتبارًا من عام 2017، يبدو أنه يتم إدارته على GitHub.
أيهما الأحدث؟ في وقت كتابة هذا المقال، لا توجد إصدارات على جانب GitHub. لذا أتحقق من حالة التسجيل في مستودع Maven Central. بالبحث عن "json-lib"، تظهر عدة groupId.
لتحديد أي groupId صحيح، أتحقق من ملف pom.xml الخاص بـ REST plugin في Struts2 2.5.14.1.
بالنظر إلى إصدارات groupId = net.sf.json-lib, artifactId = json-lib، نجد أن الإصدار الأخير هو الإصدار 2.4 من ديسمبر 2010.
بالتحقق من صفحة sourceforge، نجد أن الإصدار الأخير هو أيضًا الإصدار 2.4 من ديسمبر 2012.
في الواقع، على الرغم من عدم وجود علامات إصدار على GitHub، إلا أنه بتتبع سجل الـ commits، نجد commitًا يشير إلى إصدار الإصدار 2.4 في ديسمبر 2010. ومنذ ذلك الحين، تم دمج طلبات السحب ولكن لم تكن هناك أنشطة إصدار.
بالاطلاع على القضايا (مغلقة ومفتوحة) على GitHub، لا أرى عناوين قد تؤدي إلى DoS.
بالنظر إلى التذاكر على sourceforge، أخيرًا صادفت تذاكر حول مشكلة تسرب الذاكرة. يبدو أن أيًا منها لم يتم إصلاحه بعد.
تمكنت أخيرًا من الوصول إلى أن مشكلة تسرب الذاكرة لا تزال موجودة على الأرجح، لكن بسبب قدراتي وضيق الوقت، لم أتمكن من التحقيق أكثر من ذلك. إذا كان لديك معلومات محددة عن حدوث تسرب ذاكرة أو DoS بسبب هذا النمط من JSON، فسأكون ممتنًا جدًا إذا أبلغتني بها.
نظرًا لوجود العديد من البرامج مفتوحة المصدر التي تستخدم Jackson، هناك مكتبات وأطر أخرى تأثرت بهذه الثغرة. على سبيل المثال، تأثرت منتجات Pivotal بما في ذلك Spring Security، وتم الإفصاح عنها في يونيو 2017.
أما بالنسبة للأطر الأخرى مثل Spring Framework نفسه، فعلى الأقل على https://pivotal.io/security/ لم يتم نشر معلومات تحديث ناتجة عن Jackson. ولكن لا يمكننا أن نكون مطمئنين. ففي الأصل، Jackson في Spring Framework مصمم ليكون قابلًا للتخصيص بسهولة، ويمكن إنشاء ObjectMapper خاص بالتطبيق. يجب عليك التحقق مما إذا تم تخصيص إعدادات/ميزات Jackson من جانب التطبيق؟ هل تم استخدام @JsonTypeInfo؟ من الأفضل التحقق فقط للاطمئنان.
قمت بترتيب المعلومات من مشكلات GitHub وإصدارات jackson-databind، بالإضافة إلى bugzilla من RedHat، بترتيب زمني. إذا كان هناك خطأ، فلا تتردد في إبلاغي أو الاتصال بي.
في ذلك الوقت، كانت قائمة الحظر كما يلي:```java s.add("org.apache.commons.collections.functors.InvokerTransformer"); s.add("org.apache.commons.collections.functors.InstantiateTransformer"); s.add("org.apache.commons.collections4.functors.InvokerTransformer"); s.add("org.apache.commons.collections4.functors.InstantiateTransformer"); s.add("org.codehaus.groovy.runtime.ConvertedClosure"); s.add("org.codehaus.groovy.runtime.MethodClosure"); s.add("org.springframework.beans.factory.ObjectFactory"); s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); s.add("org.apache.xalan.xsltc.trax.TemplatesImpl");
هنا، تم إصدار 2.9.0 كتعديل إضافي لمواجهة black-list خلال نفس الشهر.
* https://github.com/FasterXML/jackson-databind/issues/1680
تمت إضافة ما يلي:```java
s.add("com.sun.rowset.JdbcRowSetImpl");
خلال نفس الشهر، يتم فتح المشكلة التالية.
→ تتم إضافة ما يلي إلى القائمة السوداء، ويتم تضمين ذلك في 2.8.10 / 2.9.1.```java // [databind#1737]; JDK provided s.add("java.util.logging.FileHandler"); s.add("java.rmi.server.UnicastRemoteObject"); // [databind#1737]; 3rd party s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor"); s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean"); s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource"); s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource");
### 2017-08
* تم إصدار الإصدار 2.8.10 الذي يعالج #1680 و #1737.
* أيضًا في أغسطس، تم فتح القضية التالية وتمت مناقشة المعالجة الشاملة لـ CVE-2017-7525.
* https://github.com/FasterXML/jackson-databind/issues/1723
* في مدونة Adam Caudill، تم نشر شرح لاستغلال CVE-2017-7525.
* https://adamcaudill.com/2017/10/04/exploiting-jackson-rce-cve-2017-7525/
### 2017-09
* تم إصدار الإصدار 2.9.1 الذي يعالج #1737.
### 2017-10
في bugzilla التالي، تبين أن الإصدارين 2.8.9 / 2.9.0 وقت إصلاح CVE-2017-7525 لم يكونا كافيين، وبدأ العمل على تطبيق القائمة السوداء (black-list) للإصدارين الأحدث 2.8.10 / 2.9.1 ضمن CVE-2017-15095 الجديدة.
* https://bugzilla.redhat.com/show_bug.cgi?id=1506612
### 2017-11
* نشرت RedHat معلومات عن CVE-2017-15095.
* https://access.redhat.com/security/cve/cve-2017-15095
* في Issue الخاص بـ jackson-databind، تم تبادل الأسئلة والأجوبة حول حالة التعامل مع CVE-2017-15095.
* https://github.com/FasterXML/jackson-databind/issues/1847
* وجاء الرد بأنه تمت معالجته في الإصدارين 2.8.10 / 2.9.1.
### 2017-12
* تم نشر مقال توضيحي في مدونة مطوري WAF Scutum السحابي.
* https://www.scutum.jp/information/waf_tech_blog/2017/12/waf-blog-052.html
* في هذا المقال، ذُكر أن القائمة السوداء للإصدار 2.9.3 بها نقص في فئة Spring، ورغم أنه ليس طارئًا، إلا أن هناك احتمالًا لتحديث آخر.
* كما نوقش ما إذا كانت مسؤولية هذه الثغرة تقع على المكتبة نفسها أم على التطبيق.
## حول اتجاهات الثغرات المتعلقة بـ serialize/deserialize في Java في المستقبل
في العامين الماضيين تقريبًا بدأت تظهر معلومات عن ثغرات متعلقة بـ serialize/deserialize في Java بشكل متزايد. على سبيل المثال، يتم نشر معلومات ثغرات منتجات Pivotal بما في ذلك Spring على https://pivotal.io/security/، وبالفعل في عام 2017، بالإضافة إلى CVE-2017-4995، تم نشر الثغرات التالية:
* https://pivotal.io/security/cve-2017-8045
* RCE بسبب مشكلة deserialize في `org.springframework.amqp.core.Message` في Spring AMQP
* https://pivotal.io/security/cve-2017-8046
* RCE بسبب خلل في معالجة JSON باستخدام طريقة PATCH في Spring Data REST
نظرًا لأن هذه الثغرات تؤدي بسهولة إلى RCE، فإن كلًا من المهاجمين والباحثين يركزون حاليًا على serialize/deserialize في Java. من المتوقع أن تستمر التقارير عن الثغرات المتعلقة بعمليات serialize/deserialize خلال السنوات القليلة القادمة. ومع ذلك، في التطوير الحديث حيث أصبح تبادل البيانات عبر واجهات برمجة التطبيقات عن بُعد أمرًا شائعًا، يبدو أنه من غير العملي التخلي تمامًا عن serialize/deserialize أو بنائها من الصفر في معظم بيئات العمل. المهم هو بناء ثقافة تطوير سريعة الحركة تسمح بتحديث المكتبات في أقرب وقت ممكن عند الكشف عن ثغرة.
أشعر شخصيًا أنه من المهم مواجهة ثغرات الوسائط والمكتبات والأطر التي نعتمد عليها، وفكرت في الجانب العقلي لهذا الأمر، لذلك كتبت رأيي أدناه.
## رأي شخصي
عندما رأيت معلومات عن إصدار S2-054 و S2-055 وتأثرها بثغرة Jackson، صدمت كثيرًا. وذلك لأنني قبل أيام قليلة سألني أحد الزملاء: "هل هناك مكتبة موصى بها لتحليل JSON في Java؟" وأجبت بثقة: "Jackson واسع الاستخدام في المشاريع مفتوحة المصدر وله سجل جيد، وتجد الكثير من المقالات والأسئلة والأجوبة عنه على Google، لذا أوصي به."
كنت شخصيًا أستخدم Jackson في تطوير الأدوات الداخلية للشركة وشعرت بفائدته. ومع ذلك، في ذلك الوقت لم أكن على علم بـ CVE-2017-7525، وكنت مطمئنًا لأن Jackson مستخدم على نطاق واسع في المصادر المفتوحة. لاحقًا، وجد زميل آخر مدونة Adam Caudill وأخبرني بوجود CVE-2017-7525. وبعد أيام قليلة من توصيتي باستخدام Jackson بثقة، تم إصدار تحديث لـ Struts2 بسبب ثغرة Jackson، خاصة أن ثغرة Jackson نفسها كانت قد عولجت منذ أشهر. كمهندس في مجال الأمن، لا يمكنني إنكار أنني كنت مقصرًا في جمع معلومات الثغرات عن المكتبات التي أستخدمها (لا يمكنني قول غير ذلك).
لذلك كانت حالتي النفسية سيئة للغاية (ارتفاع ضغط الدم والنبض، ارتعاش اليدين، دموع بدون حزن، خفقان لا يتوقف) لأيام. لمحاولة التعامل مع ذلك، بدأت بفهم ما هي CVE-2017-7525 وما وضع Jackson بالضبط، وقضيت عطلة نهاية الأسبوع في البحث وكتابة هذه المقالة.
بالنظر إلى هذه النقطة، أدركت أنه من الصعب حقًا الانتباه إلى معلومات تحديث المكتبات التابعة عند الانغماس في التطوير. في موجة مهام التطوير المتتالية، من غير العملي فحص جميع وظائف المكتبات وجودتها مثل الثغرات بشكل كامل. من ناحية أخرى، تزداد الوظائف المطلوبة للتطوير، ومن غير العملي أيضًا بناء كل شيء من الصفر. نحن بحاجة إلى "الثقة" في بعض المكتبات لتبسيط التطوير. ومع ذلك، تتبع جميع تحديثات الأدوات والمكتبات المستخدمة في العمل اليومي ومراقبة تضمينها لإصلاحات أمنية أمر صعب جدًا. بالطبع، أعلم أن هناك خدمات تقدم معلومات التحديث عند تسجيل الأدوات والمكتبات المستخدمة.
ما شعرت به من تدهور حالتي النفسية هو أن "شعور اللوم والذنب لعدم القيام بما يجب" كان قويًا جدًا لدي. كنت ألصق بنفسي تسمية سلبية: "كيف لمهندس أمن لا يعرف معلومات ثغرات المكتبة التي يستخدمها؟" حتى المطورين العاديين غير المرتبطين بمجال الأمن قد يشعرون بالندم أو القلق: "لو لم أقترح Struts2 في ذلك الوقت..." أو "يجب أن أهتم أكثر بإدارة دورة حياة المكتبات/الأطر (= أنا/الوضع الحالي غير قادر على ذلك ويقلقني)."
إذا حاولنا حل هذه المشكلة "كموضوع" مباشرة، فسنحتاج إلى جمع معلومات الثغرات واحدة تلو الأخرى، وفحص كود المصدر ووظائف المكتبات والأطر المخطط استخدامها بدقة، والتحقق من كونها المعيار الفعلي (de facto standard)، وإدارة دورة الحياة بعد بدء التشغيل بدقة. ولكن هل هذا الأسلوب "الاحتراسي" (敲石橋を叩いて渡る - الضرب على الجسر الحجري قبل عبوره) ممكن في بيئات التطوير المتنوعة الحالية؟
ما فكرت فيه بعد كتابة هذه المقالة هو أن عصر اعتبار "ما لم تفعله/ما لم تستطع فعله/ما لم تلاحظه" كسبب أو مذنب، وحذفه = "جعله ممكنًا" هو "حل المشكلة" قد انتهى. في مثل هذه الثقافة، ما لم تكن إنسانًا كاملاً، سيواجه المطورون باستمرار ما لم يفعلوه، وما لم يستطيعوا فعله، وما لم يلاحظوه. نظرًا لعدم وجود إنسان كامل، أعتقد أنه من الصعب تحمل ذلك إلا لمن لديه قوة نفسية كبيرة.
في مشاكل أمن البرمجيات، المجرم الحقيقي الذي يسبب الضرر هو المهاجم. من يقلل الوضع إلى السلبية هو المهاجم الذي يستغل الثغرات. غالبية المطورين يعملون بنية حسنة وبجد. هذا في حد ذاته وضع إيجابي. ما "لم يتم إدارة دورة حياة المكتبات/الأطر" و"لم يتم جمع ومراقبة معلومات ثغرات المكتبات المستخدمة" هو مجرد عدم فعل، وهو ليس إيجابيًا ولا سلبيًا. ولكن بوجود المهاجم، يصبح "ما لم تفعله" سلبيًا كـ "ما لم تستطع فعله/ما لم تلاحظه". أليس هذا موقفًا حزينًا؟
لا يمكن إنكار أن قيم "الاحتراس" متأثرة بالمجتمع الياباني الحديث وثقافة الشركات. بالفعل، تحدث حوادث إنشاء ثغرات مثل SQL injection، وتحدث أضرار من المهاجمين، وحتى قضايا في المحاكم تطالب الشركات المطورة بالمسؤولية. هناك أيضًا حالات تؤدي فيها الإهمال الوظيفي إلى حوادث كبيرة. لكن إذا تم اعتبار كل هذه الأمور "مشكلة شركة التطوير/المطورين"، فسوف ينكمش تطوير تكنولوجيا المعلومات بشكل عام. السيئ الحقيقي هو المهاجم الذي يستغل الثغرات.
بالنظر إلى ذلك، هل يمكن اعتبار المطورين أو شركات التطوير الذين "لم يفعلوا/لم يستطيعوا/لم يلاحظوا" ضحايا بدلاً من جناة؟ إذا كان الأمر كذلك، فبدلاً من توجيه اللوم لهم بأنهم "سيئون لعدم فعلهم/عدم قدرتهم/عدم ملاحظتهم"، ينبغي تقديم يد دافئة بهدف السير معًا: "هكذا يمكنك أن تكون أكثر أمانًا، يمكننا التحسن، فلنعمل معًا". ثم التعاون والمنافسة في مجالات تخصصنا والاستفادة من بعضنا. بهذه الطريقة، يمكن لشركات التطوير والمطورين التعامل مع الثغرات بثقة، ومن ثم مواصلة التطوير النشط بناءً على الثقة. عندما يزداد التطوير الإيجابي والآمن والمشرق، قد تزداد النتائج الابتكارية، مما يثري المجتمع الياباني.
أشعر بقوة أنني أتمنى أن تنتشر الأفكار التالية مستقبلًا:
* المطورون في الميدان
* "استخدام مكتبة بها ثغرة" ليس سلبيًا بحد ذاته.
* ما يجعلها سلبية هو المهاجم الذي يستغل الثغرة، والثقافة التي تقيمها كسلبية.
* العمل بجد يوميًا في التطوير هو بحد ذاته إيجابي بما يكفي.
* التعامل مع الثغرات ليس عملية إعادة الصفر السلبي إلى الصفر، بل هو عمل إيجابي لتحسين نتائج التطوير اليومية وجعلها أكثر أمانًا.
* المدراء والقادة الذين يديرون المطورين، والإدارة العليا
* لا تقيّم "ما لم تفعله/ما لم تستطع فعله/ما لم تلاحظه" كسلبي. هذه القيم ستتلاشى تدريجيًا.
* توقف عن معاملة الأعضاء الذين "لم يفعلوا/لم يستطيعوا/لم يلاحظوا" كجناة. إنهم، وكلنا جميعًا، ضحايا للمهاجمين الذين يستغلون الثغرات، ومن هذا المنطلق نحن في نفس الموقف.
* توقف عن محاولة الحل باستخدام "إجراءات كاملة".
على الرغم من أن هذا يتغير "كيف نرى ونفهم الأمور" ويشمل المطورين والمدراء والقادة والإدارة العليا، إلا أنه من الواضح أن هذا صعب للغاية. فكيف يمكننا التغيير؟ لا أستطيع تقديم إجابة صحيحة بسبب نقص قدراتي، لكن أعتقد أن هناك تلميحًا واحدًا. وهو أنه ربما يجب علينا التخلي عن البحث عن الإجابة الصحيحة نفسها. لكل تطوير برمجيات هدف ما. البحث عن "الإجابة الصحيحة" لتحقيقه بطريقة آمنة ومأمونة أصبح مستحيلًا تقريبًا اليوم بسبب تعقيد عالم البرمجيات. ربما ما يوجد هو نموذج تطوير يعتمد على "التغيير" حيث يجرب العديد من اللاعبين طرقهم الخاصة، ويحصلون على تغذية راجعة، ويتبادلون المعلومات، ثم يتفرعون ويتغيرون بحرية.
في تطوير الأنظمة، يتم استخدام الأصول البرمجية التي تم إنشاؤها مرة واحدة لعدة سنوات، وأحيانًا لعقود. لكن البرمجيات المتصلة بالإنترنت، مثل تطوير الويب، تتعرض لبيئة تتغير في غضون أشهر إلى سنوات. قد تصبح المكتبات والأطر المستخدمة منذ خمس سنوات غير صالحة للاستخدام. وبالتالي، فإن اعتبار المكتبات/الأطر التي تم إنشاؤها في البداية "صحيحة" وتثبيت الإصدارات (version pinning) يحمل مساوئ أكثر من الفوائد. العالم الخارجي يتغير باستمرار، لذلك من الطبيعي أن تكتشف ثغرات في المكتبات/الأطر المستخدمة. تثبيت الإصدارات يجعل من المستحيل مواكبة هذه التغييرات، مما يؤدي إلى تكاليف ذهنية ومادية هائلة للتعامل مع الثغرات. لذلك، أشعر بقوة أن التحول إلى نموذج تطوير يعتمد على "التغيير والقدرة على التكيف مع التغيير" سيؤدي إلى نتائج أفضل على المدى الطويل.
لقد طال المقال، ولكن هذا كل شيء.
----
الكاتب: ماساهيكو ساكاموتو (تابع لقسم البحث والتطوير، يعمل على تطوير أدوات فحص تطبيقات الويب الداخلية)
* البريد الإلكتروني: [email protected]
* تويتر: https://twitter.com/msakamoto_sf
* فيسبوك: https://www.facebook.com/masahiko.sakamoto.75
* جيت هاب: https://github.com/msakamoto-sf
للاستفسارات أو التعليقات حول هذه المقالة، يرجى التواصل مع ساكاموتو.