diff options
Diffstat (limited to 'examples/src/main/java')
20 files changed, 804 insertions, 0 deletions
diff --git a/examples/src/main/java/com/example/ExampleFuzzer.java b/examples/src/main/java/com/example/ExampleFuzzer.java new file mode 100644 index 00000000..073d924a --- /dev/null +++ b/examples/src/main/java/com/example/ExampleFuzzer.java @@ -0,0 +1,40 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import java.security.SecureRandom; + +public class ExampleFuzzer { + public static void fuzzerInitialize() { + // Optional initialization to be run before the first call to fuzzerTestOneInput. + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + // Without the hook in ExampleFuzzerHooks.java, the value of random would change on every + // invocation, making it almost impossible to guess for the fuzzer. + long random = new SecureRandom().nextLong(); + if (input.startsWith("magicstring" + random) && input.length() > 30 + && input.charAt(25) == 'C') { + mustNeverBeCalled(); + } + } + + private static void mustNeverBeCalled() { + throw new FuzzerSecurityIssueMedium("mustNeverBeCalled has been called"); + } +} diff --git a/examples/src/main/java/com/example/ExampleFuzzerHooks.java b/examples/src/main/java/com/example/ExampleFuzzerHooks.java new file mode 100644 index 00000000..41f16635 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleFuzzerHooks.java @@ -0,0 +1,30 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.HookType; +import com.code_intelligence.jazzer.api.MethodHook; +import java.lang.invoke.MethodHandle; + +public class ExampleFuzzerHooks { + @MethodHook(type = HookType.REPLACE, targetClassName = "java.security.SecureRandom", + targetMethod = "nextLong", targetMethodDescriptor = "()J") + public static long + getRandomNumber(MethodHandle handle, Object thisObject, Object[] args, int hookId) { + return 4; // chosen by fair dice roll. + // guaranteed to be random. + // https://xkcd.com/221/ + } +} diff --git a/examples/src/main/java/com/example/ExampleFuzzerWithNative.java b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java new file mode 100644 index 00000000..b9a13e24 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java @@ -0,0 +1,36 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.github.fmeum.rules_jni.RulesJni; + +public class ExampleFuzzerWithNative { + static { + String native_lib = System.getProperty("jazzer.native_lib"); + RulesJni.loadLibrary(native_lib, ExampleFuzzerWithNative.class); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + int val = data.consumeInt(); + String stringData = data.consumeRemainingAsString(); + if (val == 17759716 && stringData.length() > 10 && stringData.contains("jazzer")) { + // call native function which contains a crash + new ExampleFuzzerWithNative().parse(stringData); + } + } + + private native boolean parse(String bytes); +}
\ No newline at end of file diff --git a/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java b/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java new file mode 100644 index 00000000..d704da39 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java @@ -0,0 +1,28 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import java.util.ArrayList; + +public class ExampleOutOfMemoryFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + ArrayList<Byte> bytes = new ArrayList<>(); + int pos = 0; + while (pos >= 0 && pos < input.length) { + bytes.add(input[pos]); + pos += input[pos] + 1; + } + } +} diff --git a/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java b/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java new file mode 100644 index 00000000..f53ccb3c --- /dev/null +++ b/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java @@ -0,0 +1,43 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.io.File; +import java.io.IOException; + +public class ExamplePathTraversalFuzzer { + /** + * The root path for all files that this application is allowed to upload. + */ + public static final String publicFilesRootPath = "/app/upload/"; + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String relativePath = data.consumeRemainingAsAsciiString(); + // Upload the file and try very hard to ignore errors thrown during the upload. + try { + uploadFile(relativePath); + } catch (Throwable ignored) { + } + } + + private static void uploadFile(String relativePathToFile) throws IOException { + File fileToUpload = new File(publicFilesRootPath + relativePathToFile); + if (!fileToUpload.exists()) { + throw new IOException("File not found"); + } + // In a real application, the file would be uploaded to a public server here. + } +} diff --git a/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java new file mode 100644 index 00000000..b027de5b --- /dev/null +++ b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java @@ -0,0 +1,46 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh; +import com.code_intelligence.jazzer.api.HookType; +import com.code_intelligence.jazzer.api.Jazzer; +import com.code_intelligence.jazzer.api.MethodHook; +import java.lang.invoke.MethodHandle; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ExamplePathTraversalFuzzerHooks { + @MethodHook(type = HookType.BEFORE, targetClassName = "java.io.File", targetMethod = "<init>", + targetMethodDescriptor = "(Ljava/lang/String;)V") + public static void + fileConstructorHook(MethodHandle handle, Object thisObject, Object[] args, int hookId) { + String path = (String) args[0]; + Path normalizedPath; + try { + normalizedPath = Paths.get(path).normalize(); + } catch (InvalidPathException e) { + // Invalid paths are correctly rejected by the application. + return; + } + if (!normalizedPath.startsWith(ExamplePathTraversalFuzzer.publicFilesRootPath)) { + // Simply throwing an exception from here would not work as the calling code catches and + // ignores all Throwables. Instead, use the Jazzer API to report a finding from a hook. + Jazzer.reportFindingFromHook(new FuzzerSecurityIssueHigh( + "Path traversal discovered: '" + path + "' --> '" + normalizedPath + "'")); + } + } +} diff --git a/examples/src/main/java/com/example/ExampleStackOverflowFuzzer.java b/examples/src/main/java/com/example/ExampleStackOverflowFuzzer.java new file mode 100644 index 00000000..47166af4 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleStackOverflowFuzzer.java @@ -0,0 +1,33 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import java.math.BigDecimal; + +public class ExampleStackOverflowFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + step1(); + } + + private static void step1() { + BigDecimal unused = BigDecimal.valueOf(10, 100); + step2(); + } + + private static void step2() { + boolean unused = "foobar".contains("bar"); + step1(); + } +} diff --git a/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java new file mode 100644 index 00000000..acc023a2 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java @@ -0,0 +1,53 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; +import java.util.Base64; + +public class ExampleValueProfileFuzzer { + private static String base64(byte[] input) { + return Base64.getEncoder().encodeToString(input); + } + + private static long insecureEncrypt(long input) { + long key = 0xefe4eb93215cb6b0L; + return input ^ key; + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + // Without -use_value_profile=1, the fuzzer gets stuck here as there is no direct correspondence + // between the input bytes and the compared string. With value profile, the fuzzer can guess the + // expected input byte by byte, which takes linear rather than exponential time. + if (base64(data.consumeBytes(6)).equals("SmF6emVy")) { + long[] plaintextBlocks = data.consumeLongs(2); + if (plaintextBlocks.length != 2) + return; + if (insecureEncrypt(plaintextBlocks[0]) == 0x9fc48ee64d3dc090L) { + // Without --fake_pcs (enabled by default with -use_value_profile=1), the fuzzer would get + // stuck here as the value profile information for long comparisons would not be able to + // distinguish between this comparison and the one above. + if (insecureEncrypt(plaintextBlocks[1]) == 0x888a82ff483ad9c2L) { + mustNeverBeCalled(); + } + } + } + } + + private static void mustNeverBeCalled() { + throw new FuzzerSecurityIssueLow("mustNeverBeCalled has been called"); + } +} diff --git a/examples/src/main/java/com/example/FastJsonFuzzer.java b/examples/src/main/java/com/example/FastJsonFuzzer.java new file mode 100644 index 00000000..2e5d4797 --- /dev/null +++ b/examples/src/main/java/com/example/FastJsonFuzzer.java @@ -0,0 +1,30 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +// Found the issues described in +// https://github.com/alibaba/fastjson/issues/3631 +public class FastJsonFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + try { + JSON.parse(data.consumeRemainingAsString()); + } catch (JSONException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/GifImageParserFuzzer.java b/examples/src/main/java/com/example/GifImageParserFuzzer.java new file mode 100644 index 00000000..ab7de907 --- /dev/null +++ b/examples/src/main/java/com/example/GifImageParserFuzzer.java @@ -0,0 +1,32 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import java.io.IOException; +import java.util.HashMap; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.common.bytesource.ByteSourceArray; +import org.apache.commons.imaging.formats.gif.GifImageParser; + +// Found https://issues.apache.org/jira/browse/IMAGING-277 and +// https://issues.apache.org/jira/browse/IMAGING-278. +public class GifImageParserFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + try { + new GifImageParser().getBufferedImage(new ByteSourceArray(input), new HashMap<>()); + } catch (IOException | ImageReadException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/JacksonCborFuzzer.java b/examples/src/main/java/com/example/JacksonCborFuzzer.java new file mode 100644 index 00000000..902c1d96 --- /dev/null +++ b/examples/src/main/java/com/example/JacksonCborFuzzer.java @@ -0,0 +1,34 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.cbor.CBORFactory; +import java.io.IOException; + +// Reproduces https://github.com/FasterXML/jackson-dataformats-binary/issues/236 and +// https://github.com/FasterXML/jackson-databind/pull/3032 if executed with +// `--keep_going=3 -seed=2735196724`. +public class JacksonCborFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + CBORFactory factory = new CBORFactory(); + ObjectMapper mapper = new ObjectMapper(factory); + mapper.enableDefaultTyping(); + try { + mapper.readTree(input); + } catch (IOException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/JpegImageParserFuzzer.java b/examples/src/main/java/com/example/JpegImageParserFuzzer.java new file mode 100644 index 00000000..a6898bf0 --- /dev/null +++ b/examples/src/main/java/com/example/JpegImageParserFuzzer.java @@ -0,0 +1,45 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import java.io.IOException; +import java.util.HashMap; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.common.bytesource.ByteSourceArray; +import org.apache.commons.imaging.formats.jpeg.JpegImageParser; + +// Found https://issues.apache.org/jira/browse/IMAGING-275. +public class JpegImageParserFuzzer { + public static void fuzzerInitialize() { + String foo = System.getProperty("foo"); + String bar = System.getProperty("bar"); + String baz = System.getProperty("baz"); + // Only used to verify that arguments are correctly passed down to child processes. + if (foo == null || bar == null || baz == null || !foo.equals("foo") + || !(bar.equals("b;ar") || bar.equals("b:ar")) || !baz.equals("baz")) { + // Exit the process with an exit code different from that for a finding. + System.err.println("ERROR: Did not correctly pass all jvm_args to child process."); + System.err.printf("foo: %s%nbar: %s%nbaz: %s%n", foo, bar, baz); + System.exit(3); + } + } + + public static void fuzzerTestOneInput(byte[] input) { + try { + new JpegImageParser().getBufferedImage(new ByteSourceArray(input), new HashMap<>()); + } catch (IOException | ImageReadException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerCrashFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerCrashFuzzer.java new file mode 100644 index 00000000..05ac4611 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerCrashFuzzer.java @@ -0,0 +1,30 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerCrashFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + try { + JsonSanitizer.sanitize(input, 10); + } catch (ArrayIndexOutOfBoundsException ignored) { + // ArrayIndexOutOfBoundsException is expected if nesting depth is + // exceeded. + } + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerDenylistFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerDenylistFuzzer.java new file mode 100644 index 00000000..e715b1d9 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerDenylistFuzzer.java @@ -0,0 +1,45 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerDenylistFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + String validJson; + try { + validJson = JsonSanitizer.sanitize(input, 10); + } catch (Exception e) { + return; + } + + // Check for forbidden substrings. As these would enable Cross-Site Scripting, treat every + // finding as a high severity vulnerability. + assert !validJson.contains("</script") + : new FuzzerSecurityIssueHigh("Output contains </script"); + assert !validJson.contains("]]>") : new FuzzerSecurityIssueHigh("Output contains ]]>"); + + // Check for more forbidden substrings. As these would not directly enable Cross-Site Scripting + // in general, but may impact script execution on the embedding page, treat each finding as a + // medium severity vulnerability. + assert !validJson.contains("<script") + : new FuzzerSecurityIssueMedium("Output contains <script"); + assert !validJson.contains("<!--") : new FuzzerSecurityIssueMedium("Output contains <!--"); + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java new file mode 100644 index 00000000..111d3de2 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java @@ -0,0 +1,34 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerIdempotenceFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + String validJson; + try { + validJson = JsonSanitizer.sanitize(input, 10); + } catch (Exception e) { + return; + } + + // Ensure that sanitizing twice does not give different output (idempotence). Since failure to + // be idempotent is not a security issue in itself, fail with a regular AssertionError. + assert JsonSanitizer.sanitize(validJson).equals(validJson) : "Not idempotent"; + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java new file mode 100644 index 00000000..2d270966 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java @@ -0,0 +1,42 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerValidJsonFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + String validJson; + try { + validJson = JsonSanitizer.sanitize(input, 10); + } catch (Exception e) { + return; + } + + // Check that the output is valid JSON. Invalid JSON may crash other parts of the application + // that trust the output of the sanitizer. + try { + Gson gson = new Gson(); + gson.fromJson(validJson, JsonElement.class); + } catch (Exception e) { + throw new FuzzerSecurityIssueLow("Output is invalid JSON", e); + } + } +} diff --git a/examples/src/main/java/com/example/KlaxonFuzzer.kt b/examples/src/main/java/com/example/KlaxonFuzzer.kt new file mode 100644 index 00000000..39216eea --- /dev/null +++ b/examples/src/main/java/com/example/KlaxonFuzzer.kt @@ -0,0 +1,31 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example + +import com.beust.klaxon.KlaxonException +import com.beust.klaxon.Parser +import com.code_intelligence.jazzer.api.FuzzedDataProvider + +// Reproduces https://github.com/cbeust/klaxon/pull/330 +object KlaxonFuzzer { + + @JvmStatic + fun fuzzerTestOneInput(data: FuzzedDataProvider) { + try { + Parser.default().parse(StringBuilder(data.consumeRemainingAsString())) + } catch (_: KlaxonException) { + } + } +} diff --git a/examples/src/main/java/com/example/Log4jFuzzer.java b/examples/src/main/java/com/example/Log4jFuzzer.java new file mode 100644 index 00000000..41870c9c --- /dev/null +++ b/examples/src/main/java/com/example/Log4jFuzzer.java @@ -0,0 +1,82 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; +import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.status.StatusLogger; + +// This fuzzer reproduces the log4j RCE vulnerability CVE-2021-44228. +public class Log4jFuzzer { + private final static Logger log = LogManager.getLogger(Log4jFuzzer.class.getName()); + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + log.error(data.consumeRemainingAsString()); + } + + public static void fuzzerInitialize() { + // Install a logger that constructs the log message, but never prints it. + // This noticeably increases the fuzzing performance + DefaultConfigurationBuilder configBuilder = new DefaultConfigurationBuilder(); + configBuilder.setPackages(FuzzingAppender.class.getPackage().getName()); + AppenderComponentBuilder fuzzingAppender = + configBuilder.newAppender("nullAppender", "FuzzingAppender"); + configBuilder.add(fuzzingAppender); + RootLoggerComponentBuilder rootLogger = configBuilder.newRootLogger(); + rootLogger.add(configBuilder.newAppenderRef("nullAppender")); + configBuilder.add(rootLogger); + Configurator.reconfigure(configBuilder.build()); + + // Disable logging of exceptions caught in log4j itself. + StatusLogger.getLogger().reset(); + StatusLogger.getLogger().setLevel(Level.OFF); + } + + @Plugin( + name = "FuzzingAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) + public static class FuzzingAppender extends AbstractAppender { + protected FuzzingAppender(String name) { + super(name, null, PatternLayout.createDefaultLayout(), true); + } + + @PluginFactory + public static FuzzingAppender createAppender(@PluginAttribute("name") String name) { + return new FuzzingAppender(name); + } + + @Override + public void append(LogEvent event) { + try { + getLayout().toByteArray(event); + } catch (Exception ignored) { + // Prevent exceptions from being logged to stderr. + } + } + } +} diff --git a/examples/src/main/java/com/example/TiffImageParserFuzzer.java b/examples/src/main/java/com/example/TiffImageParserFuzzer.java new file mode 100644 index 00000000..5fa1af2d --- /dev/null +++ b/examples/src/main/java/com/example/TiffImageParserFuzzer.java @@ -0,0 +1,31 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import java.io.IOException; +import java.util.HashMap; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.common.bytesource.ByteSourceArray; +import org.apache.commons.imaging.formats.tiff.TiffImageParser; + +// Found https://issues.apache.org/jira/browse/IMAGING-276. +public class TiffImageParserFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + try { + new TiffImageParser().getBufferedImage(new ByteSourceArray(input), new HashMap<>()); + } catch (IOException | ImageReadException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/TurboJpegFuzzer.java b/examples/src/main/java/com/example/TurboJpegFuzzer.java new file mode 100644 index 00000000..b9ea715b --- /dev/null +++ b/examples/src/main/java/com/example/TurboJpegFuzzer.java @@ -0,0 +1,59 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import org.libjpegturbo.turbojpeg.TJ; +import org.libjpegturbo.turbojpeg.TJDecompressor; +import org.libjpegturbo.turbojpeg.TJException; +import org.libjpegturbo.turbojpeg.TJTransform; +import org.libjpegturbo.turbojpeg.TJTransformer; + +public class TurboJpegFuzzer { + static byte[] buffer = new byte[128 * 128 * 4]; + + public static void fuzzerInitialize() throws TJException { + // Trigger an early load of the native library to show the coverage counters stats in libFuzzer. + new TJDecompressor(); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + try { + int flagsDecompress = data.consumeInt(); + int flagsTransform = data.consumeInt(); + int pixelFormat = data.consumeInt(TJ.PF_RGB, TJ.PF_CMYK); + // Specify explicit small target width/height so that we can reuse a + // fixed-size buffer. + int desiredWidth = data.consumeInt(1, 128); + int desiredHeight = data.consumeInt(1, 128); + int transformOp = data.consumeInt(TJTransform.OP_NONE, TJTransform.OP_ROT270); + int transformOptions = data.consumeInt(); + int transformWidth = data.consumeBoolean() ? 128 : 64; + int transformHeight = data.consumeBoolean() ? 128 : 64; + TJDecompressor tjd; + if (data.consumeBoolean()) { + TJTransformer tjt = new TJTransformer(data.consumeRemainingAsBytes()); + TJTransform tjf = new TJTransform( + 0, 0, transformWidth, transformHeight, transformOp, transformOptions, null); + tjd = tjt.transform(new TJTransform[] {tjf}, flagsTransform)[0]; + } else { + tjd = new TJDecompressor(data.consumeRemainingAsBytes()); + } + tjd.decompress(buffer, 0, 0, desiredWidth, 0, desiredHeight, pixelFormat, flagsDecompress); + } catch (Exception ignored) { + // We are not looking for Java exceptions, but segfaults and ASan reports. + } + } +} |