log4j2漏洞解决方案:log4j2转换为logback

log4j2漏洞解决方案:log4j2转换为logback,第1张

log4j2漏洞解决方案:log4j2转换为logback 背景

Apache Log4j2是项目中常用的Java日志框架。在2021年11月,阿里云安全团队向Apache官方报告了Apache Log4j2远程代码执行漏洞。在Log4j 2.15.0-rc2 版本之后,问题解决。但是部分项目由于历史代码原因,直接使用的Log4j2依赖包的类,在升级log4j2版本时,代码出现冲突,在这种情况下,我们可以log4j2转换为logback,在log4j2依赖不变更的情况下,解决安全问题。

解决方案 将log4j2转换为logback

可以通过log4j-to-slf4j工具,将log4j2转换为logback。log4j-to-slf4j 允许将编码到Log4j2 API的应用程序路由到SLF4J。使用这个适配器可能会导致一些性能损失,因为在将Log4j2消息传递给SLF4J之前,必须对它们进行格式化。

适用情况(同时满足以下场景):
    无法进行log4j2升级依赖log4j2的模块为子模块,或者是第三方依赖

maven依赖

    
        
            com.vhicool.test
            common
            1.0-SNAPSHOT
            
                
                    org.apache.logging.log4j
                    log4j-core
                
                
                    org.apache.logging.log4j
                    log4j-api
                
            
        
        
            org.apache.logging.log4j
            log4j-to-slf4j
            2.11.2
        
        
            ch.qos.logback
            logback-classic
            1.2.3
        
        
            ch.qos.logback
            logback-core
            1.2.3
        
    

测试类

public class Main {
	public static void main(String[] args) {
       //LoggerUtil为com.vhicool.test:test-common包中的工具类
		LoggerUtil.info("${jndi:ldap://127.0.0.1:8080/Log4jRCE}");
	}
}

LoggerUtil.java

public class LoggerUtil {
	private static final Logger logger = LogManager.getLogger(LoggerUtil.class);

	public static void info(String msg){
        System.out.println(logger.getClass());
		logger.info(msg);
	}
}

输出

Logger : class org.apache.logging.slf4j.SLF4JLogger
[main] INFO com.vhicoo.test.common.LoggerUtil - ${jndi:ldap://127.0.0.1:8080/Log4jRCE}

结论
从结果可以看出,经转换后,日志最终使用org.apache.logging.slf4j.SLF4JLogger输出。同时也没有触发日志注入的问题。 问题复现

当前模块:com.vhicool.test:test-common:1.0-SNAPSHOT

maven依赖
log4f版本为2.0-2.15

    
        2.8.2
    
    
        
            org.apache.logging.log4j
            log4j-core
            ${log4j-version}
        
        
            org.apache.logging.log4j
            log4j-api
            ${log4j-version}
        
    

log4j配置文件



	
		
			
		
	
	
		
			
		
	

测试方法

日志输出时,实际会调用远程地址ldap://127.0.0.1:8080/Log4jRCE,本身这个地址端口不存在,因此会出现网络异常。

public class Main {
	private static final Logger logger = LogManager.getLogger(Main.class);
	public static void main(String[] args) {
        System.out.println("Logger : "+logger.getClass());
		logger.info("${jndi:ldap://127.0.0.1:8080/Log4jRCE}");
	}
}

输出

Logger : class org.apache.logging.log4j.core.Logger

main WARN Error looking up JNDI resource [ldap://127.0.0.1:8080/Log4jRCE]. javax.naming.CommunicationException: 127.0.0.1:8080 [Root exception is java.net.ConnectException: Connection refused (Connection refused)]
	at com.sun.jndi.ldap.Connection.(Connection.java:238)
	at com.sun.jndi.ldap.LdapClient.(LdapClient.java:137)
	at com.sun.jndi.ldap.LdapClient.getInstance(LdapClient.java:1609)
	at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2749)
	at com.sun.jndi.ldap.LdapCtx.(LdapCtx.java:319)
...

结论
通过log4j2输出的日志,会解析${jndi:}中的变量为网络连接,并调用。其中打印日志类为:org.apache.logging.log4j.core.Logger 分析 为什么会导致日志jndi注入的问题

使用log4j2输出日志,如果日志输出串中,包含${}时,log4j会解析变量值的内部方法,内部方法的格式为$prefix:$key。默认支持的内部方法$prefix有:date, ctx, main, env, sys, sd, java, marker, jndi, jvmrunargs, bundle, map, log4j。

调用链:调用入口 org.apache.logging.log4j.core.Logger#logMessage

当$prefix是jndi时,JndiLookup解析字符串变量$key为JNDI执行语义。例如$key=ldap://127.0.0.1:8080/Log4jRCE的JNDI解析流程如下:

InitialContext#look($key)首先根据$key对应的创建对象的工厂ObjectFactory,查找逻辑:
(1). 解析 k e y 获 取 key获取 key获取schema:ldap
(2). 生成ObjectFactory类全限定名:com.sun.jndi.url.ldap.ldapURLContextFactory,生成格式如下:

    "com.sun.jndi.url."+$schema+"."+$schema+"URLContextFactory"

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5706203.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存