【Struts2-命令-代码执行漏洞分析系列】S2-057

栏目: Struts · 发布时间: 8年前

内容简介:https://cwiki.apache.org/confluence/display/WW/S2-057 问题: It is possible to perform a RCE attack when namespace value isn't set for a result defined in underlying xml configurations and in same time, its upper action(s) configurations have no or wildcard
漏洞公告

https://cwiki.apache.org/confluence/display/WW/S2-057 问题: It is possible to perform a RCE attack when namespace value isn't set for a result defined in underlying xml configurations and in same time, its upper action(s) configurations have no or wildcard namespace. Same possibility when using url tag which doesn’t have value and action set and in same time, its upper action(s) configurations have no or wildcard namespace.

漏洞发现者的博客: https://lgtm.com/blog/apache_struts_CVE-2018-11776 环境搭建 下载 https://archive.apache.org/dist/struts/2.5.16/struts-2.5.16-all.zip IDEA中打开,修改apps/showcase/src/main/resources/struts-actionchaining.xml 为:

register2

同时查看 org/apache/struts2/default.properties:201 ,其值为true

Whether to always select the namespace to be everything before the last slash or not

struts.mapper.alwaysSelectFullNamespace=true 访问: http://localhost:8081/${(111+111)}/actionChain1.action

url变为: http://localhost:8081/222/register2.action 111+111=222 即产生了OGNL注入。 漏洞分析 这次的漏洞可以有多种攻击向量,根据漏洞作者blog有:

Redirect action Action chaining Postback result

以上提及的三种都属于Struts2的跳转方式。在 struts-default.xml:190(截取部分)

为清楚起见,这里解释一下strut2中对默认result对象的处理过程。这些默认result type都要经过 com/opensymphony/xwork2/DefaultActionInvocation.java:367 处理 private void executeResult() throws Exception { result = createResult();

String timerKey = "executeResult: " + getResultCode();
try {
    UtilTimerStack.push(timerKey);
    if (result != null) {
        result.execute(this);
    } 
...

}

首先通过result = createResult()获取到相应的result对象。如果result不为null则执行result.execute(this);。这个execute方法则由具体result对象实现。具体的result对象会产生一个跳转地址location,并传入org/apache/struts2/result/StrutsResultSupport.java:194: /** * 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. * * @param invocation the execution state of the action. * @throws Exception if an error occurs while executing the result. */ public void execute(ActionInvocation invocation) throws Exception { lastFinalLocation = conditionalParse(location, invocation); doExecute(lastFinalLocation, invocation); }

而conditionalParse定义如下,将会执行OGNL表达式。 /** * Parses the parameter for OGNL expressions against the valuestack * * @param param The parameter value * @param invocation The action invocation instance * @return the resulting string */ protected String conditionalParse(String param, ActionInvocation invocation) { if (parse && param != null && invocation != null) { return TextParseUtil.translateVariables( param, invocation.getStack(), new EncodingParsedValueEvaluator()); } else { return param; } }

所以可以看到重点是StrutsResultSupport中conditionalParse(location, invocation)的location变量。接下来部分就关注三种result-type的具体实现。 攻击点一:Redirect action apps/showcase/src/main/resources/struts-actionchaining.xml 中注意 标签中 为redirectAction: register2

redirectAction对应的处理类为org.apache.struts2.result.ServletActionRedirectResult 在 com/opensymphony/xwork2/DefaultActionInvocation.java:368

跟入redirectAction的execute方法即 org/apache/struts2/result/ServletActionRedirectResult.java:160 public void execute(ActionInvocation invocation) throws Exception { actionName = conditionalParse(actionName, invocation); if (namespace == null) { namespace = invocation.getProxy().getNamespace(); ... }

由于在配置xml时没有指定naPmespace,所以这里的namespace为null,将会执行invocation.getProxy().getNamespace();

所以执行后对于result对象的namespace即为/${(111+111)}。 同一函数中继续执行 172行 public void execute(ActionInvocation invocation) throws Exception { ...

String tmpLocation = actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null));

setLocation(tmpLocation);

super.execute(invocation);

}

ActionMapping生成如下,this.namespace值赋为/${(111+111)}:

跟入getUriFromActionMapping: public String getUriFromActionMapping(ActionMapping mapping) { StringBuilder uri = new StringBuilder();

handleNamespace(mapping, uri);
    handleName(mapping, uri);
    handleDynamicMethod(mapping, uri);
    handleExtension(mapping, uri);
    handleParams(mapping, uri);

    return uri.toString();
}

handleNamespace处理结果如下:

当函数返回,tmpLocation值为/${(111+111)}/register2.action,然后通过setLocation(tmpLocation)使得location变量值为/${(111+111)}/register2.action,从而最终造成OGNL注入。

攻击点二: Action chaining apps/showcase/src/main/resources/struts-actionchaining.xml 中注意 标签中 为chain: register2

同样会先经过result = createResult(),然后调用result.execute(this);。这会进入到 com/opensymphony/xwork2/ActionChainResult.java:203 public void execute(ActionInvocation invocation) throws Exception { // if the finalNamespace wasn't explicitly defined, assume the current one if (this.namespace == null) { this.namespace = invocation.getProxy().getNamespace(); }

ValueStack stack = ActionContext.getContext().getValueStack();
String finalNamespace = TextParseUtil.translateVariables(namespace, stack);
String finalActionName = TextParseUtil.translateVariables(actionName, stack);
...

}

由于没有设定namespace,所以通过invocation.getProxy().getNamespace()使得this.namespace值为/${(111+111)}。然后调用了String finalNamespace = TextParseUtil.translateVariables(namespace, stack);对namespace进行OGNL解析。如下

攻击点三:Postback result apps/showcase/src/main/resources/struts-actionchaining.xml 中注意 标签中 为postback: register2

定位到postback这个result对象的处理方法,在 org/apache/struts2/result/PostbackResult.java:113 @Override public void execute(ActionInvocation invocation) throws Exception { String postbackUri = makePostbackUri(invocation); setLocation(postbackUri); super.execute(invocation); }

跟入makePostbackUri1,在org/apache/struts2/result/PostbackResult.java:129 protected String makePostbackUri(ActionInvocation invocation) { ActionContext ctx = invocation.getInvocationContext(); HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); String postbackUri;

if (actionName != null) {
    actionName = conditionalParse(actionName, invocation);
    if (namespace == null) {
        namespace = invocation.getProxy().getNamespace();
    } else {
        namespace = conditionalParse(namespace, invocation);
    }
    ...
    postbackUri = request.getContextPath() + actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null));
}
...

return postbackUri;

}

获取到namespace值为/${(111+111)}。跟入actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null)),其具体执行过程如攻击点一[Redirect action]提到的那样,设置namespace等参数,然后从getUriFromActionMapping中返回uri。最后组装的postbackUri为/${(111+111)}/register2.action

回到前面的execute中通过setLocation(postbackUri)设置了location变量:

此后location变量传入,造成OGNL表达式注入

参考

https://struts.apache.org/core-developers/namespace-configuration.html


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

解密硅谷

解密硅谷

[美]米歇尔 E. 梅西纳(Michelle E. Messina)、乔纳森 C. 贝尔(Jonathan C. Baer) / 李俊、李雪 / 机械工业出版社 / 2018-12 / 50.00

《解密硅谷》由身处硅谷最中心的连续创业者米歇尔·梅西纳和资深的投资人乔纳森·贝尔联合撰写,二人如庖丁解牛一般为读者深入剖析硅谷成功的原因:从硅谷的创新机制、创业生态、投资领域的潜规则、秘而不宣的价值观等角度,让阅读本书的人能够在最短的时间内,拥有像硅谷人一样的商业头脑,从而快速发现机遇,顺利地躲过创业的坑,熬过创业生死挑战中的劫数,带领初创公司顺利地活下去,并实现快速增长。 如果初创公司能够......一起来看看 《解密硅谷》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具