aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdemarcsek <gyorgy.demarcsek@protonmail.ch>2022-11-21 22:43:23 +0100
committerNorbert Schneider <mail@bertschneider.de>2023-06-13 08:57:04 +0200
commit1bf61b4416bc96b0a0bb4c6c941d5b9b88d12c4e (patch)
tree1d1ef98ea758d171ae61d3aa8e5d91a44656fadb
parentd3d742bf8e1df8b8b60a86dc5834bbb2fe6ff5fe (diff)
downloadjazzer-api-1bf61b4416bc96b0a0bb4c6c941d5b9b88d12c4e.tar.gz
add script engine injection sanitizer
-rw-r--r--sanitizers/sanitizers.bzl1
-rw-r--r--sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel9
-rw-r--r--sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java112
-rw-r--r--sanitizers/src/test/java/com/example/ScriptEngineInjection.java118
4 files changed, 240 insertions, 0 deletions
diff --git a/sanitizers/sanitizers.bzl b/sanitizers/sanitizers.bzl
index 280ccfde..f4894f64 100644
--- a/sanitizers/sanitizers.bzl
+++ b/sanitizers/sanitizers.bzl
@@ -24,6 +24,7 @@ _sanitizer_class_names = [
"ReflectiveCall",
"RegexInjection",
"RegexRoadblocks",
+ "ScriptEngineInjection",
"ServerSideRequestForgery",
"SqlInjection",
"XPathInjection",
diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel
index 21217db0..7f416dfd 100644
--- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel
+++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel
@@ -28,6 +28,14 @@ java_library(
],
)
+java_library(
+ name = "script_engine_injection",
+ srcs = ["ScriptEngineInjection.java"],
+ deps = [
+ "//agent/src/main/java/com/code_intelligence/jazzer/api:hooks",
+ ],
+)
+
kt_jvm_library(
name = "sanitizers",
srcs = [
@@ -45,6 +53,7 @@ kt_jvm_library(
runtime_deps = [
":regex_roadblocks",
":server_side_request_forgery",
+ ":script_engine_injection",
":sql_injection",
],
deps = [
diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java
new file mode 100644
index 00000000..4236287c
--- /dev/null
+++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java
@@ -0,0 +1,112 @@
+// Copyright 2022 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.code_intelligence.jazzer.sanitizers;
+
+import static java.util.Collections.unmodifiableSet;
+import static java.util.stream.Collectors.toSet;
+
+import com.code_intelligence.jazzer.api.FuzzerSecurityIssueCritical;
+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 com.code_intelligence.jazzer.api.MethodHooks;
+import java.lang.invoke.MethodHandle;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Stream;
+import java.io.IOException;
+import java.io.Reader;
+
+import javax.script.ScriptEngineManager;
+
+/**
+ * Detects Script Engine injection
+ *
+ * <p>
+ * The hooks in this class attempt to detect user input flowing into
+ * {@link javax.script.ScriptEngine.eval} that might lead to
+ * remote code executions depending on the scripting engine's capabilities.
+ * Before JDK 15, the Nashorn Engine
+ * was registered by default with ScriptEngineManager under several aliases,
+ * including "js". Nashorn allows
+ * access to JVM classes, for exmaple {@link java.lang.Runtime} allowing the
+ * execution of arbitrary OS commands.
+ * Several other scripting engines can be embedded to the JVM (they must follow
+ * the <a href="https://www.jcp.org/en/jsr/detail?id=223">JSR-223</a>
+ * specification).
+ * </p>
+ **/
+public final class ScriptEngineInjection {
+ private static final String ENGINE = "js";
+ private static final String PAYLOAD = "1+1";
+
+ private static char[] guideMarkableReaderTowardsEquality(Reader reader, String target, int id) throws IOException {
+ final int size = target.length();
+ char[] current = new char[size];
+ int n = 0;
+
+ if (!reader.markSupported()) {
+ throw new IllegalStateException("Reader does not support mark - not possible to fuzz");
+ }
+
+ reader.mark(size);
+
+ while (n < size) {
+ int count = reader.read(current, n, size - n);
+ if (count < 0)
+ break;
+ n += count;
+ }
+ reader.reset();
+
+ Jazzer.guideTowardsEquality(new String(current), target, id);
+
+ return current;
+ }
+
+ @MethodHook(type = HookType.REPLACE, targetClassName = "javax.script.ScriptEngineManager", targetMethod = "registerEngineName")
+ public static Object ensureScriptEngine(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
+ throws Throwable {
+ return method.invokeWithArguments(Stream.concat(Stream.of(thisObject),
+ Stream.concat(Stream.of((Object) ENGINE), Arrays.stream(arguments, 1, arguments.length))).toArray());
+ }
+
+ @MethodHook(type = HookType.REPLACE, targetClassName = "javax.script.ScriptEngineManager", targetMethod = "getEngineByName")
+ public static Object hookEngineName(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
+ throws Throwable {
+ String engine = (String) arguments[0];
+ Jazzer.guideTowardsEquality(engine, ENGINE, hookId);
+ return method.invokeWithArguments(Stream.concat(Stream.of(thisObject), Arrays.stream(arguments)).toArray());
+ }
+
+ @MethodHook(type = HookType.BEFORE, targetClassName = "javax.script.ScriptEngine", targetMethod = "eval")
+ public static void checkScriptEngineExecute(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
+ throws Throwable {
+ String script = null;
+
+ if (arguments[0] instanceof String) {
+ script = (String) arguments[0];
+ Jazzer.guideTowardsEquality(script, PAYLOAD, hookId);
+ } else {
+ script = new String(guideMarkableReaderTowardsEquality((Reader) arguments[0], PAYLOAD, hookId));
+ }
+
+ if (script.equals(PAYLOAD)) {
+ Jazzer.reportFindingFromHook(
+ new FuzzerSecurityIssueCritical(String.format("Possible script execution")));
+ }
+ }
+}
diff --git a/sanitizers/src/test/java/com/example/ScriptEngineInjection.java b/sanitizers/src/test/java/com/example/ScriptEngineInjection.java
new file mode 100644
index 00000000..cf9bc4f6
--- /dev/null
+++ b/sanitizers/src/test/java/com/example/ScriptEngineInjection.java
@@ -0,0 +1,118 @@
+// Copyright 2022 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.FuzzerSecurityIssueCritical;
+
+import java.io.Reader;
+import java.io.StringReader;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+class DummyScriptEngine implements ScriptEngine {
+ @Override
+ public Bindings createBindings() {
+ return null;
+ }
+
+ @Override
+ public Object eval(String script) throws ScriptException {
+ return null;
+ }
+
+ @Override
+ public Object eval(Reader reader) throws ScriptException {
+ return null;
+ }
+
+ @Override
+ public Object eval(String script, ScriptContext context) throws ScriptException {
+ return null;
+ }
+
+ @Override
+ public Object eval(Reader reader, ScriptContext context) throws ScriptException {
+ return null;
+ }
+
+ @Override
+ public Object eval(String script, Bindings n) throws ScriptException {
+ return null;
+ }
+
+ @Override
+ public Object eval(Reader reader, Bindings n) throws ScriptException {
+ return null;
+ }
+
+ @Override
+ public Object get(String key) {
+ return null;
+ }
+
+ @Override
+ public Bindings getBindings(int scope) {
+ return null;
+ }
+
+ @Override
+ public ScriptContext getContext() {
+ return null;
+ }
+
+ @Override
+ public ScriptEngineFactory getFactory() {
+ return null;
+ }
+
+ @Override
+ public void put(String key, Object value) {
+ }
+
+ @Override
+ public void setBindings(Bindings bindings, int scope) {
+ }
+
+ @Override
+ public void setContext(ScriptContext context) {
+ }
+
+ public DummyScriptEngine() {
+ }
+}
+
+public class ScriptEngineInjection {
+ static ScriptEngine engine = new DummyScriptEngine();
+
+ static void insecureScriptEval(String input) throws Exception {
+ engine.eval(new StringReader(input));
+ }
+
+ public static void fuzzerTestOneInput(FuzzedDataProvider data) throws Exception {
+ try {
+ insecureScriptEval(data.consumeRemainingAsAsciiString());
+ } catch (Throwable t) {
+ if (t instanceof FuzzerSecurityIssueCritical) {
+ throw t;
+ }
+ }
+ }
+}