Skip to content
Fabian Meumertzheim 4 min read

Finding the log4j RCE With Fuzzing

On December 9th, 2021, the Remote Code Execution (RCE) CVE-2021-44228 in Apache log4j 2 was published and started seeing active exploitation soon after.  Since then, development teams have been working hard and tirelessly, trying to fix the issue to prevent (further) damage.

Can Fuzz Testing Help? 

Yes – CI Fuzz version 2.28.0 and up can find this vulnerability if log4j is included in the list of libraries to instrument for fuzzing. Due to the high interest in this class of vulnerabilities, we are making the relevant bug detector available to the open-source community. With Jazzer, our open-source fuzzing engine that is part of Google’s OSS fuzz, you can now find the log4j vulnerability as well as similar RCE issues in the same way as with our core product CI Fuzz

Protecting Open-Source Projects With Fuzzing

Since Jazzer is part of OSS-Fuzz, all integrated open-source projects written in Java and other JVM-based languages, are now continuously searched for similar vulnerabilities. A fuzz target for log4j that reproduces the vulnerability can be found here

#16150 NEW cov: 3945 ft: 6197 corp: 465/20Kb lim: 86 exec/s: 2691 rss: 491Mb L: 86/86 MS: 3 ShuffleBytes-ChangeByte-ChangeASCIIInt-
#16217 REDUCE cov: 3945 ft: 6197 corp: 405/20Kb lim: 86 exec/s: 2782 rss: 491Mb L: 26/86 MS: 2 CrossOver-EraseBytes~

= Java Exception: com.code_intelligence. jazzer.api.FuzzerSecurityIssueCritical: Remote JNDI Lookup
JINDI lookups with attacker-controlled remote URLs can, depending on the JDK
version, lead to remote code execution or the exfiltration of information
    at com.code_intelligence.jazzer.sanitizers.NamingContextLookup.LookupHook(NamingContextLookup.kt:83)
    at org.apache.logging.log4j.core.lookup.IndiLookup.Lookup(
    at org.apache.logging.log4j.core.lookup.Interpolator.Lookup(
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.resolveVariable(
  at org.apache.logging.log4j.core.lookup.Strsubstitutor.substitute(
  at org.apache.logging.log4j.core.lookup.Strsubstitutor.substitute(
  at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(
  at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.replace(Strsubstitutor,java:467)
    at org.apache.logging.log4j.core.pattern.MessagePatternConverter.format(
    at org.apache.logging.log4j.core.pattern.PatternFormatter.format(
    at org.apache.logging.log4j.core.layout.PatternLayout$PatternSerializer.toSerializable(
  at org.apache.logging.log4j.core.layout.PatternLayout$PatternSerializer.toSerializable(
    at org.apache.logging.log4j.core.layout.PatternLayout.toSerializable(
  at org.apache.logging.log4j.core.layout.PatternLayout.toSerjalizable(
    at org.apache.logging.log4j.core.layout.AbstractStringLayout.toBytearray(
    at com.example.Log4jFuzzer$FuzzingAppender.append(
    at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(
    at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl. java:129)
  at org.apache.logging.log4j.core.config.AppenderControl.calLAppenderPreventRecursion(
    at org.apache.logging.log4j.core.config.AppenderControl.callappender(
    at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(
  at org.apache.logging.log4j.core.config.LoggerConfig.processLogevent(
    at org.apache.logging.log4j.core.config.LoggerConfig.log(
  at org.apache.logging.log4j.core.config.LoggerConfig.log(
    at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(
    at org.apache.logging.log4j.core.Logger.log(
    at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(
    at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(
  at org-apache.logging.log4j.spi.AbstractLogger.logMessageSafely(
    at org.apache.logging.log4j.spi.AbstractLogger.logMessage(
  at org.apache.logging.log4j.spi.AbstractLogger.logiEnabled(
    at org.apache.logging.log4j.spi.AbstractLogger.error(
    at com.example.Log4jFfuzzer.fuzzerTestoneInput(
DEDUP_TOKEN: be2c0453Gedec39f
== libFuzzer crashing input ==
MS: 2 ChangeBit-PersAutoDict- DE: "rmi://"-; base unit: 968db9335a224bd9d9fe97123b385518533b74ed
0x23, 0x0, 0x89, 0xd5 ,0xb0, 0x1, 0x2c, 0x0, 0x24, 0XTb, 0x6a, 0xGe, 0X2C, 0X0, 0x24, 0x7b, 0x6a, 0xGe, 0x64 ,0x69, 0x39, 0x72,
0x6d, 0x69, 0x3a, 0x2f , 0x2f,ex67 ,0x2e, 0x63 , 0x6f , 0x2, 0x7d, 0x1, 0x3a, 0x5b, 0x7d, 0x1, 0x3a, 0xSb, 0x7d , 0x2c , 0x9 X24,
0x7b, 0x6a, 0x6e, 0x6c, 0x69, 0x67, 0x3a, 0x2b, 0x2b, 0x2b, 0x2b, 0x3b, 0x0, 0x70, 0x70, 0x7b, 0x3a, 0x7d,
#\x00\x80\xd5\ xb0\x01,\xees(jn,\xedS{jndizrmi://\x02:\xe1:{},\xeos{jnlig:;++++;\x00pp{:}

The logs of a Jazzer run that finds log4j CVE-2021-44228

About Fabian Meumertzheim

Fabian Meumertzheim (@fhenneke) is a Senior Software Engineer aCode Intelligence. A mathematician by education, he has always been passionate about IT security. He maintains and contributes to multiple open-source projects, such as Chromium, system, and Android Password Store, all with the aim of making security unobtrusive and ubiquitous.

See Jazzer on GitHub