在趋势科技漏洞研究服务最近发布的漏洞报告中,趋势科技研究团队成员KcUdonsi和JohnSimpson详细介绍了近期的A代码执行漏洞暴露了。该安全漏洞最初是由Apple信息安全部门的MatthiasKaiser发现并报告的。以下是他们撰写的CVE-2019-0230报告的节选,并进行了一些修改。近日,ApacheStruts框架被曝出远程代码执行漏洞。该漏洞是由于输入验证不足导致的,该验证在计算原始用户输入时会强制进行两次对象图导航库(OGNL)计算。攻击者可以通过向目标服务器发送精心设计的请求来利用此漏洞。一旦攻击成功,他们就可以以服务器的权限执行任意代码。漏洞详情ApacheStruts是一个模型-视图-控制器(MVC)框架,用于构建基于Java的Web应用程序。MVC框架用于将信息的表示与用户与信息的交互隔离开来。其中,模型由负责与数据库通信等后端工作的业务和应用逻辑组成。视图是呈现给用户的数据的表示,而控制器负责协调模型和视图之间的通信。ApacheStruts将通过JavaServletAPI提供一个名为ActionServlet的控制器。客户端的请求以XML配置文件中定义的“Action”形式发送给控制器。这时控制器会调用模型代码中对应的Action类,这些类会使用内部逻辑来验证和执行这些操作,并以新视图的形式返回结果,控制器负责更新这些结果。ApacheStruts允许通过JavaServerPages(JSP)对视图进行编码,这些视图将被编译成HTML页面以便通过浏览器查看。我们可以使用这样的URI访问关联的操作:actionname>.action其中替换为操作名称。后缀“.action”只是一个约定。基于Struts的Web应用程序可以使用任何后缀,例如“.do”。HTTP是RFC7230-7237和其他RFC中描述的请求/响应协议。客户端向服务器发送请求,服务器对其进行相应处理后,将响应返回给客户端。通常,一个HTTP请求由一个请求行、各种标头、一个空行和一个可选的消息文本组成,如下所示:一个空格字符。参数可以作为请求URI或消息文字中的“名称-值”对从客户端传递到服务器,具体取决于所使用的方法和Content-Type标头。例如,当使用GET方法传输名为“param”且值为“1”的参数时,对应的HTTP请求如下:如果使用POST方法,则对应的HTTP请求如下:如果有多个参数/值对,它们将被编码为以&分隔的“名称=值”对,如下:var1=value1&var2=value2...另外,ApacheStruts还支持使用对象图导航语言(ObjectGraphNavigationalLanguage(OGNL)表达式通过模板.jsp文件动态生成网页内容。OGNL是一种动态表达式语言(EL),语法简洁,用于获取和设置Java对象的属性、lambda表达式等。OGNL表达式可以包含构成导航链的字符串。这些字符串可以是属性名称、方法调用、数组索引等。OGNL表达式根据作为OGNL上下文提供给评估器的初始或根上下文对象进行评估。ApacheStruts我们es一个名为ActionContext的线程本地容器对象,用于存储执行Actions所需的各种对象。这些对象通常会包括会话标识符、请求参数、语言环境等。此外,ActionContext还公开了一个ValueStack接口,用于推送和存储处理动态表达式语言(EL)所需的对象。当EL编译器需要解析表达式时,它会从最近推入堆栈的对象开始向下搜索堆栈。OGNL是Struts使用的主要EL,它具有以下特殊语法:--objectName:ValueStack中的对象(OGNL上下文中的默认/根对象),如Action属性。--#objectName:位于ActionContext内和ValueStack外的对象。例如,#parameters.objectName用于请求参数,#session.objectName用于会话范围的属性等。--%{ognlexp}:强制对通常为字符串的属性进行OGNL评估。--@full.class.name@Static_Field:用来表示一个类的静态字段(变量和方法)。与此报告相关的一项功能是使用语法%{ognlex}强制对通常为字符串的属性进行OGNL计算。此语法称为alt语法,默认情况下启用。如果ognlexp是从用户输入中获得的,没有经过任何清理,则可能会发生重复计算。如果您在标记属性中进行强制评估,则很可能会发生这种情况。例如(使用锚标记作为示例):如果用户提供了一个fileNam,使得原始OGNL表达式在没有进一步验证的情况下被传递,则当该标记作为请求的结果呈现时,将评估走私的OGNL表达式。也就是说,如果filename是一个OGNL表达式,比如%{2+2},那么渲染的label就是:在渲染response的时候,对于每一个要渲染的label,org.apache.struts2.views.jsp会是调用了.ComponentTagSupport类中定义的doStartTag()方法。然后,此方法将进一步调用populateParams()方法。但是,调用此方法的实际版本取决于正在处理的标记。对于锚标记,相应的方法定义为org.apache.struts2.views.jsp.ui.AnchorTag类中的populateParams()方法。实际上,id属性的第一次计算发生在populateParams()的基类实现中,它调用了org.apache.struts2.component.UIBean类中定义的setId()。调用populateParams()方法后,doStartTag()方法将开始计算标签并渲染模板。在org.apache.struts2.component.ClosingUIBean类中定义的start()方法中,函数evaluateParams()将被执行,该函数将使用populateComponentHtmlId()函数来填充标签的id属性。至于这个方法的调用版本,它取决于正在渲染的标签。对于锚标记,将调用org.apache.struts2.component.UIBean类中定义的populateComponentHtmlId()方法。此实现将再次将id属性作为findStringIfAltSyntax()函数中的OGNL表达式进行处理。ApacheStruts2框架中存在远程代码执行漏洞。该漏洞是由于OGNL表达式可用的类和包限制不足,特别是java.io.、java.nio.、java.net.、sun.misc。包裹。尽管ApacheStruts团队多次指出重复的OGNL计算是潜在的安全风险,并指出开发者应该只使用强制计算功能来处理可信数据,并且已经添加了相应的缓解控制以尽量减少被利用的可能性。这些缓解控制包括:--struts.excludedClasses:排除类的逗号分隔列表。--struts.excludedPackageNamePatterns:根据Regex排除包的模式。--struts.excludedPackageNames:排除包的逗号分隔列表。此外,Struts默认限制对类构造函数和静态方法的访问。SetOgnlUtil()方法修改了ValueStack的SecurityMemberAccess属性以包括上述列表中的排除项。但是,如果遇到不在排除范围内的类或包,它仍然可以执行不安全的操作,因为SecurityMemberAccess.isAccessible()方法允许评估访问或操作这些包中定义的对象的表达式。ApacheStruts使用的FreeMarkerJava模板引擎在包freemarker.ext.jsp中提供了一组实用类,以方便FreeMarker-JSP的双向集成。在这些类中,有一个类叫做TaglibFactory。该类提供与servlet上下文关联的哈希模型,并可以加载关联的JSP标记库。TaglibFactory类能够通过ObjectWrapper类的实例将Java对象映射到FreeMarker模板语言的类型系统。ObjectWrapper类确定可以从模板访问Java对象的哪些部分以及如何访问。此外,类TaglibFactory还公开了一个getter,用于获取支持ObjectWrapper的实例。同时,这个ObjectWrapper实例还提供了一种创建任意Java对象的方法,以便在需要时分别使用newInstance()和wrap()两个公共方法对其进行封装。因此,攻击者可以通过首先使用wrap()方法包装参数,然后调用newInstance()指定要实例化的类和包装的构造函数参数来创建具有公共构造函数的任意类实例。这个小工具有效地帮助Struts摆脱了对Class构造函数的默认限制。因此,TaglibFactory的实例可以在OGNLValueStack的OGNL表达式中使用。Guice轻量级依赖注入容器在com.openymphony.xwork2.inject包中提供了一组实用类,用于提供构造函数、方法、静态方法、字段和静态字段注入。其中,有一个名为ContainerBuilder的类。此类提供用于构建依赖注入容器的工厂方法。更重要的是,它还提供了一个公共方法,可用于创建Container实例。Container实例不仅有一个依赖映射,还提供了一个公共方法injection(),可以用来创建任意实例并将其注入到依赖映射中。容器实例有效地提供了一种方法来检索受静态工厂方法保护的实例,否则OGNL表达式将无法使用这些实例。通过调用inject()并指定所需的类,我们可以获得任何没有公共构造函数但返回单例实例的静态工厂方法的类的实例。我们可以使用前面提到的FreeMarkerTagLibFactory小部件来获取ContainerBuilder类的实例,因为它实现了一个公共构造函数。在这些小工具的帮助下,我们可以从java.net中实例化一个类。打包,打开到远程机器的连接并通过获取受限类sun.misc.Unsafe的实例从远程机器接收类字节,最后使用sun.misc.Unsafe.defineAnonymousClass()方法接收来自远程服务器定义任意类,从而绕过当前的安全限制。然后,我们可以使用sun.misc.Unsafe.allocateInstance()方法来初始化任何类。虽然allocateInstance()方法不能调用构造函数,但它可以在创建的实例上调用为该类定义的其他公共方法。或者,我们也可以使用java.io或java.nio包中的类实例,这些实例可用于创建任意文件(例如JSP文件)并将它们写入任意位置(例如Web应用程序根目录),因为文件路径引用可用于OGNLValueStack的OGNL表达式中。远程攻击者可以通过向受感染的服务器发送包含恶意参数的HTTP请求来利用此漏洞。一旦被成功利用,他们就可以使用服务器权限执行任意代码。摘要ApacheStruts团队已于2020年8月修复了此安全漏洞。据他们称,相关补丁通过确保正确检查和验证传入的每个值以用于标签属性来修复漏洞。此外,他们建议不要使用%{...}或${...}语法来强制计算值以外的属性,除非这是不可避免的。最后,需要补充的是,Struts2.5.22及更高版本不受此漏洞影响。特别感谢趋势科技研究团队的KcUdonsi和JohnSimpson提供了如此详细的漏洞分析。有关趋势科技研究服务的概述,请访问http://go.trendmicro.com/tis/。本文翻译自:https://www.thezdi.com/blog/2020/10/7/cve-2019-0230-apache-struts-ognl-remote-code-execution如有转载请注明出处。