1 // Copyright 2021 Code Intelligence GmbH 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.example; 16 17 import com.code_intelligence.jazzer.api.FuzzedDataProvider; 18 import org.apache.logging.log4j.Level; 19 import org.apache.logging.log4j.LogManager; 20 import org.apache.logging.log4j.Logger; 21 import org.apache.logging.log4j.core.Appender; 22 import org.apache.logging.log4j.core.Core; 23 import org.apache.logging.log4j.core.LogEvent; 24 import org.apache.logging.log4j.core.appender.AbstractAppender; 25 import org.apache.logging.log4j.core.config.Configurator; 26 import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; 27 import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; 28 import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder; 29 import org.apache.logging.log4j.core.config.plugins.Plugin; 30 import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 31 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 32 import org.apache.logging.log4j.core.layout.PatternLayout; 33 import org.apache.logging.log4j.status.StatusLogger; 34 35 // This fuzzer reproduces the log4j RCE vulnerability CVE-2021-44228. 36 public class Log4jFuzzer { 37 private final static Logger log = LogManager.getLogger(Log4jFuzzer.class.getName()); 38 fuzzerTestOneInput(FuzzedDataProvider data)39 public static void fuzzerTestOneInput(FuzzedDataProvider data) { 40 log.error(data.consumeRemainingAsString()); 41 } 42 fuzzerInitialize()43 public static void fuzzerInitialize() { 44 // Install a logger that constructs the log message, but never prints it. 45 // This noticeably increases the fuzzing performance 46 DefaultConfigurationBuilder configBuilder = new DefaultConfigurationBuilder(); 47 configBuilder.setPackages(FuzzingAppender.class.getPackage().getName()); 48 AppenderComponentBuilder fuzzingAppender = 49 configBuilder.newAppender("nullAppender", "FuzzingAppender"); 50 configBuilder.add(fuzzingAppender); 51 RootLoggerComponentBuilder rootLogger = configBuilder.newRootLogger(); 52 rootLogger.add(configBuilder.newAppenderRef("nullAppender")); 53 configBuilder.add(rootLogger); 54 Configurator.reconfigure(configBuilder.build()); 55 56 // Disable logging of exceptions caught in log4j itself. 57 StatusLogger.getLogger().reset(); 58 StatusLogger.getLogger().setLevel(Level.OFF); 59 } 60 61 @Plugin( 62 name = "FuzzingAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) 63 public static class FuzzingAppender extends AbstractAppender { FuzzingAppender(String name)64 protected FuzzingAppender(String name) { 65 super(name, null, PatternLayout.createDefaultLayout(), true); 66 } 67 68 @PluginFactory createAppender(@luginAttribute"name") String name)69 public static FuzzingAppender createAppender(@PluginAttribute("name") String name) { 70 return new FuzzingAppender(name); 71 } 72 73 @Override append(LogEvent event)74 public void append(LogEvent event) { 75 try { 76 getLayout().toByteArray(event); 77 } catch (Exception ignored) { 78 // Prevent exceptions from being logged to stderr. 79 } 80 } 81 } 82 } 83