欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

CVE-2018-11776:如何通过Semmle QL找到Apache Struts的远程执行代码漏洞

来源:本站整理 作者:佚名 时间:2018-09-07 TAG: 我要投稿

前言
2018年4月,一个新的Apache Struts远程代码执行漏洞被报告。在Struts特定配置下,访问特制的URL可以触发该漏洞。漏洞编号为CVE-2018-11776(S2-057)。本文将介绍发现漏洞的过程。
映射攻击面
许多漏洞涉及从不受信任的源(例如,用户输入)流向某个特定位置的数据,这种情况下数据会以危险的方式被利用(SQL查询,反序列化以及一些其他解释语言等等)。对于特定项目,开始着手研究此类问题的一种好方法是查看旧版本软件的已知漏洞。这可以让你深入了解所想要查找的各种源及接收点。
在本案例中,作者首先查看了RCE漏洞S2-032(CVE-2016-3081),S2-033(CVE-2016-3687)和S2-037(CVE-2016-4438)。与Struts中的许多其他RCE一样,这些RCE涉及被认为是OGNL表达式的非法输入,允许攻击者在服务器上运行任意代码。这三个漏洞特别有趣,不仅因为它们让我们对Struts的内部工作有了一些了解,而且还因为同样的问题需要三次才能修复!
所有这三个问题都是远程输入通过变量methodName作为参数传递给方法OgnlUtil::getValue()的结果。
String methodName = proxy.getMethod();    //source, but where from?
LOG.debug("Executing action method = {}", methodName);
String timerKey = "invokeAction: " + proxy.getActionName();
try {
    UtilTimerStack.push(timerKey);
    Object methodResult;
    try {
        methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action); //
这里的proxy字段是ActionProxy类型,它是一个接口。在查看它的定义时,作者发现除了方法getMethod()(在上面的代码中用于赋值的变量methodName)之外,还有各种其他的方法,如getActionName()和getNamespace()。这些方法貌似会从URL返回信息。所以作者开始假设所有这些方法都可能返回不受信任的输入。
现在可以使用QL开始对这些不受信任的来源进行建模:
class ActionProxyGetMethod extends Method {
  ActionProxyGetMethod() {
    getDeclaringType().getASupertype*().hasQualifiedName("com.opensymphony.xwork2", "ActionProxy") and
    (
      hasName("getMethod") or
      hasName("getNamespace") or
      hasName("getActionName")
    )
  }
}
predicate isActionProxySource(DataFlow::Node source) {
   source.asExpr().(MethodAccess).getMethod() instanceof ActionProxyGetMethod
}
识别OGNL接收点
现在已经识别并描述了一些不受信任的来源,下一步是为接收点做同样的事情。如前所述,许多Struts RCE涉及将远程输入解析为OGNL表达式。Struts中有许多函数最终将它们的参数作为OGNL表达式进行评估;对于在本文中开始的三个漏洞,都使用了OgnlUtil::getValue(),但是在漏洞S2-045(CVE-2017-5638)中,使用了TextParseUtil::translateVariables()。我们可以寻找用于执行OGNL表达式的函数,而不是将每一个方法描述为QL中的单独接收点。而OgnlUtil::compileAndExecute()和OgnlUtl::compileAndExecuteMethod()看起来像有希望的接收点。
作者在一个QL predicate中描述了它们,如下所示:
predicate isOgnlSink(DataFlow::Node sink) {
  exists(MethodAccess ma | ma.getMethod().hasName("compileAndExecute") or ma.getMethod().hasName("compileAndExecuteMethod") |
    ma.getMethod().getDeclaringType().getName().matches("OgnlUtil") and
    sink.asExpr() = ma.getArgument(0)
  )
}
第一次尝试污点跟踪
现在已经在QL中定义了源和接收点,所以可以在污点跟踪查询中使用这些定义。 这里作者使用DataFlow库来定义DataFlow配置:
class OgnlTaintTrackingCfg extends DataFlow::Configuration {
  OgnlTaintTrackingCfg() {
    this = "mapping"
  }
  override predicate isSource(DataFlow::Node source) {
    isActionProxySource(source)
  }
  override predicate isSink(DataFlow::Node sink) {
    isOgnlSink(sink)
  }
  override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
    TaintTracking::localTaintStep(node1, node2) or
    exists(Field f, RefType t | node1.asExpr() = f.getAnAssignedValue() and node2.asExpr() = f.getAnAccess() and
      node1.asExpr().getEnclosingCallable().getDeclaringType() = t and
      node2.asExpr().getEnclosingCallable().getDeclaringType() = t
    )
  }
}
from OgnlTaintTrackingCfg cfg, DataFlow::Node source, DataFlow::Node sink
where cfg.hasFlow(source, sink)
select source, sink
这里作者使用了之前定义的isActionProxySource和isOgnlSink predicates。
需要注意的是,作者还覆盖了一个名为isAdditionalFlowStep的predicate。这个predicate允许包含可以传播非法数据的额外步骤。例如,这允许将项目特定的信息合并到流配置中。再如,如果有通过某个网络层进行通信的组件,则可以在QL中描述那些各种网络端点的代码,允许DataFlow库跟踪非法数据。
对于此特定查询,作者添加了两个额外的流程步骤供DataFlow库使用。 第一个:
TaintTracking::localTaintStep(node1, node2)

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载