aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--maven.bzl5
-rw-r--r--maven_install.json104
-rw-r--r--sanitizers/sanitizers.bzl1
-rw-r--r--sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Deserialization.kt5
-rw-r--r--sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt83
-rw-r--r--sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ReflectiveCall.kt5
-rw-r--r--sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Utils.kt5
-rw-r--r--sanitizers/src/test/java/com/example/BUILD.bazel16
-rw-r--r--sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java49
-rw-r--r--sanitizers/src/test/java/com/example/InsecureEmailValidator.java36
10 files changed, 297 insertions, 12 deletions
diff --git a/maven.bzl b/maven.bzl
index 5da7c2d8..6469951f 100644
--- a/maven.bzl
+++ b/maven.bzl
@@ -25,4 +25,9 @@ MAVEN_ARTIFACTS = [
"com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.12.1",
"com.alibaba:fastjson:1.2.75",
"com.beust:klaxon:5.5",
+ "javax.validation:validation-api:2.0.1.Final",
+ "javax.xml.bind:jaxb-api:2.3.1",
+ "javax.el:javax.el-api:3.0.1-b06",
+ "org.hibernate:hibernate-validator:5.2.4.Final",
+ "org.glassfish:javax.el:3.0.1-b06",
]
diff --git a/maven_install.json b/maven_install.json
index 0cf7b858..e182bff6 100644
--- a/maven_install.json
+++ b/maven_install.json
@@ -1,8 +1,8 @@
{
"dependency_tree": {
"__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
- "__INPUT_ARTIFACTS_HASH": 1204355925,
- "__RESOLVED_ARTIFACTS_HASH": 897458754,
+ "__INPUT_ARTIFACTS_HASH": -1154277281,
+ "__RESOLVED_ARTIFACTS_HASH": -451611894,
"conflict_resolution": {},
"dependencies": [
{
@@ -93,6 +93,17 @@
"url": "https://repo1.maven.org/maven2/com/fasterxml/jackson/dataformat/jackson-dataformat-cbor/2.12.1/jackson-dataformat-cbor-2.12.1.jar"
},
{
+ "coord": "com.fasterxml:classmate:1.1.0",
+ "dependencies": [],
+ "directDependencies": [],
+ "file": "v1/https/repo1.maven.org/maven2/com/fasterxml/classmate/1.1.0/classmate-1.1.0.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/com/fasterxml/classmate/1.1.0/classmate-1.1.0.jar"
+ ],
+ "sha256": "610d23db8ece7268e93930562d89b91546c79fc80f3966baf433e5e93110b118",
+ "url": "https://repo1.maven.org/maven2/com/fasterxml/classmate/1.1.0/classmate-1.1.0.jar"
+ },
+ {
"coord": "com.google.code.gson:gson:2.8.6",
"dependencies": [],
"directDependencies": [],
@@ -115,6 +126,54 @@
"url": "https://repo1.maven.org/maven2/com/mikesamuel/json-sanitizer/1.2.1/json-sanitizer-1.2.1.jar"
},
{
+ "coord": "javax.activation:javax.activation-api:1.2.0",
+ "dependencies": [],
+ "directDependencies": [],
+ "file": "v1/https/repo1.maven.org/maven2/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar"
+ ],
+ "sha256": "43fdef0b5b6ceb31b0424b208b930c74ab58fac2ceeb7b3f6fd3aeb8b5ca4393",
+ "url": "https://repo1.maven.org/maven2/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar"
+ },
+ {
+ "coord": "javax.el:javax.el-api:3.0.1-b06",
+ "dependencies": [],
+ "directDependencies": [],
+ "file": "v1/https/repo1.maven.org/maven2/javax/el/javax.el-api/3.0.1-b06/javax.el-api-3.0.1-b06.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/javax/el/javax.el-api/3.0.1-b06/javax.el-api-3.0.1-b06.jar"
+ ],
+ "sha256": "0b46b36709ecbb9791ac4ba44d16125b9d65b576112afdaaa286052b6e498bc4",
+ "url": "https://repo1.maven.org/maven2/javax/el/javax.el-api/3.0.1-b06/javax.el-api-3.0.1-b06.jar"
+ },
+ {
+ "coord": "javax.validation:validation-api:2.0.1.Final",
+ "dependencies": [],
+ "directDependencies": [],
+ "file": "v1/https/repo1.maven.org/maven2/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar"
+ ],
+ "sha256": "9873b46df1833c9ee8f5bc1ff6853375115dadd8897bcb5a0dffb5848835ee6c",
+ "url": "https://repo1.maven.org/maven2/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar"
+ },
+ {
+ "coord": "javax.xml.bind:jaxb-api:2.3.1",
+ "dependencies": [
+ "javax.activation:javax.activation-api:1.2.0"
+ ],
+ "directDependencies": [
+ "javax.activation:javax.activation-api:1.2.0"
+ ],
+ "file": "v1/https/repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar"
+ ],
+ "sha256": "88b955a0df57880a26a74708bc34f74dcaf8ebf4e78843a28b50eae945732b06",
+ "url": "https://repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar"
+ },
+ {
"coord": "junit:junit:4.12",
"dependencies": [
"org.hamcrest:hamcrest-core:1.3"
@@ -141,6 +200,17 @@
"url": "https://repo1.maven.org/maven2/org/apache/commons/commons-imaging/1.0-alpha2/commons-imaging-1.0-alpha2.jar"
},
{
+ "coord": "org.glassfish:javax.el:3.0.1-b06",
+ "dependencies": [],
+ "directDependencies": [],
+ "file": "v1/https/repo1.maven.org/maven2/org/glassfish/javax.el/3.0.1-b06/javax.el-3.0.1-b06.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/org/glassfish/javax.el/3.0.1-b06/javax.el-3.0.1-b06.jar"
+ ],
+ "sha256": "c255fe3ff4d7e491caf92c10c497f3c77d19acc4832d9bd2e80180d168fcedd2",
+ "url": "https://repo1.maven.org/maven2/org/glassfish/javax.el/3.0.1-b06/javax.el-3.0.1-b06.jar"
+ },
+ {
"coord": "org.hamcrest:hamcrest-core:1.3",
"dependencies": [],
"directDependencies": [],
@@ -152,6 +222,36 @@
"url": "https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar"
},
{
+ "coord": "org.hibernate:hibernate-validator:5.2.4.Final",
+ "dependencies": [
+ "org.jboss.logging:jboss-logging:3.2.1.Final",
+ "com.fasterxml:classmate:1.1.0",
+ "javax.validation:validation-api:2.0.1.Final"
+ ],
+ "directDependencies": [
+ "com.fasterxml:classmate:1.1.0",
+ "javax.validation:validation-api:2.0.1.Final",
+ "org.jboss.logging:jboss-logging:3.2.1.Final"
+ ],
+ "file": "v1/https/repo1.maven.org/maven2/org/hibernate/hibernate-validator/5.2.4.Final/hibernate-validator-5.2.4.Final.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/org/hibernate/hibernate-validator/5.2.4.Final/hibernate-validator-5.2.4.Final.jar"
+ ],
+ "sha256": "fc7e2ed4079859f61390932a4f4cd5b2447e1ebc77d4915badb1a0655588697a",
+ "url": "https://repo1.maven.org/maven2/org/hibernate/hibernate-validator/5.2.4.Final/hibernate-validator-5.2.4.Final.jar"
+ },
+ {
+ "coord": "org.jboss.logging:jboss-logging:3.2.1.Final",
+ "dependencies": [],
+ "directDependencies": [],
+ "file": "v1/https/repo1.maven.org/maven2/org/jboss/logging/jboss-logging/3.2.1.Final/jboss-logging-3.2.1.Final.jar",
+ "mirror_urls": [
+ "https://repo1.maven.org/maven2/org/jboss/logging/jboss-logging/3.2.1.Final/jboss-logging-3.2.1.Final.jar"
+ ],
+ "sha256": "a3b0ffa8ae2b2f2387ebdfdce29086d3955d2a46ce7da802c2ba6ae47fa2f1bf",
+ "url": "https://repo1.maven.org/maven2/org/jboss/logging/jboss-logging/3.2.1.Final/jboss-logging-3.2.1.Final.jar"
+ },
+ {
"coord": "org.jetbrains.kotlin:kotlin-reflect:1.4.31",
"dependencies": [
"org.jetbrains.kotlin:kotlin-stdlib:1.4.31",
diff --git a/sanitizers/sanitizers.bzl b/sanitizers/sanitizers.bzl
index fc93ddb5..488d4bd8 100644
--- a/sanitizers/sanitizers.bzl
+++ b/sanitizers/sanitizers.bzl
@@ -17,6 +17,7 @@ _sanitizer_package_prefix = "com.code_intelligence.jazzer.sanitizers."
_sanitizer_class_names = [
"Deserialization",
"ReflectiveCall",
+ "ExpressionLanguageInjection",
]
SANITIZER_CLASSES = [_sanitizer_package_prefix + class_name for class_name in _sanitizer_class_names]
diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Deserialization.kt b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Deserialization.kt
index af46b573..f6401dfd 100644
--- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Deserialization.kt
+++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Deserialization.kt
@@ -32,11 +32,6 @@ import java.util.WeakHashMap
@Suppress("unused_parameter")
object Deserialization {
- /**
- * jaz.Zer is a honeypot class: All of its methods report a finding when called.
- */
- private const val HONEYPOT_CLASS_NAME = "jaz.Zer"
-
private val OBJECT_INPUT_STREAM_HEADER =
ObjectStreamConstants.STREAM_MAGIC.toBytes() + ObjectStreamConstants.STREAM_VERSION.toBytes()
diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt
new file mode 100644
index 00000000..9b1e8ca6
--- /dev/null
+++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt
@@ -0,0 +1,83 @@
+
+// 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.code_intelligence.jazzer.sanitizers
+
+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
+
+/**
+ * Detects injectable inputs to an expression language interpreter which may lead to remote code execution.
+ */
+@Suppress("unused_parameter")
+object ExpressionLanguageInjection {
+
+ /**
+ * Try to call the default constructor of the honeypot class.
+ */
+ private const val EXPRESSION_LANGUAGE_ATTACK =
+ "\${\"\".getClass().forName(\"$HONEYPOT_CLASS_NAME\").newInstance()}"
+
+ @MethodHooks(
+ MethodHook(
+ type = HookType.BEFORE,
+ targetClassName = "javax.el.ExpressionFactory",
+ targetMethod = "createValueExpression",
+ ),
+ MethodHook(
+ type = HookType.BEFORE,
+ targetClassName = "javax.el.ExpressionFactory",
+ targetMethod = "createMethodExpression",
+ ),
+ )
+ @JvmStatic
+ fun hookElExpressionFactory(
+ method: MethodHandle?,
+ thisObject: Any?,
+ arguments: Array<Any>,
+ hookId: Int
+ ) {
+ if (arguments[1] is String) {
+ val expression = arguments[1] as String
+ Jazzer.guideTowardsContainment(expression, EXPRESSION_LANGUAGE_ATTACK, hookId)
+ }
+ }
+
+ // With default configurations the argument to
+ // ConstraintValidatorContext.buildConstraintViolationWithTemplate() will be evaluated by an
+ // Expression Language interpreter which allows arbitrary code execution if the attacker has
+ // control of the method argument.
+ //
+ // References: CVE-2018-16621
+ // https://securitylab.github.com/research/bean-validation-RCE/
+ @MethodHook(
+ type = HookType.BEFORE,
+ targetClassName = "javax.validation.ConstraintValidatorContext",
+ targetMethod = "buildConstraintViolationWithTemplate"
+ )
+ @JvmStatic
+ fun hookBuildConstraintViolationWithTemplate(
+ method: MethodHandle?,
+ thisObject: Any?,
+ arguments: Array<Any>,
+ hookId: Int
+ ) {
+ val message = arguments[0] as String
+ Jazzer.guideTowardsContainment(message, EXPRESSION_LANGUAGE_ATTACK, hookId)
+ }
+}
diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ReflectiveCall.kt b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ReflectiveCall.kt
index d58f73f3..7842d879 100644
--- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ReflectiveCall.kt
+++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ReflectiveCall.kt
@@ -25,11 +25,6 @@ import java.lang.invoke.MethodHandle
@Suppress("unused_parameter")
object ReflectiveCall {
- /**
- * jaz.Zer is a honeypot class: All of its methods report a finding when called.
- */
- private const val HONEYPOT_CLASS_NAME = "jaz.Zer"
-
@MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Class", targetMethod = "forName")
@JvmStatic
fun classForNameHook(method: MethodHandle?, alwaysNull: Any?, args: Array<Any?>, hookId: Int) {
diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Utils.kt b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Utils.kt
index 57ba014e..3166773b 100644
--- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Utils.kt
+++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/Utils.kt
@@ -17,6 +17,11 @@ package com.code_intelligence.jazzer.sanitizers
import com.code_intelligence.jazzer.api.Jazzer
import java.io.InputStream
+/**
+ * jaz.Zer is a honeypot class: All of its methods report a finding when called.
+ */
+const val HONEYPOT_CLASS_NAME = "jaz.Zer"
+
internal fun Short.toBytes(): ByteArray {
return byteArrayOf(
((toInt() shr 8) and 0xFF).toByte(),
diff --git a/sanitizers/src/test/java/com/example/BUILD.bazel b/sanitizers/src/test/java/com/example/BUILD.bazel
index f86b6922..b2cb6543 100644
--- a/sanitizers/src/test/java/com/example/BUILD.bazel
+++ b/sanitizers/src/test/java/com/example/BUILD.bazel
@@ -15,3 +15,19 @@ java_fuzz_target_test(
],
target_class = "com.example.ReflectiveCall",
)
+
+java_fuzz_target_test(
+ name = "ExpressionLanguageInjection",
+ srcs = [
+ "ExpressionLanguageInjection.java",
+ "InsecureEmailValidator.java",
+ ],
+ target_class = "com.example.ExpressionLanguageInjection",
+ deps = [
+ "@maven//:javax_el_javax_el_api",
+ "@maven//:javax_validation_validation_api",
+ "@maven//:javax_xml_bind_jaxb_api",
+ "@maven//:org_glassfish_javax_el",
+ "@maven//:org_hibernate_hibernate_validator",
+ ],
+)
diff --git a/sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java b/sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java
new file mode 100644
index 00000000..e26a9117
--- /dev/null
+++ b/sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java
@@ -0,0 +1,49 @@
+// 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.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.validation.*;
+
+class UserData {
+ public UserData(String email) {
+ this.email = email;
+ }
+
+ @ValidEmailConstraint private String email;
+}
+
+@Constraint(validatedBy = InsecureEmailValidator.class)
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@interface ValidEmailConstraint {
+ String message() default "Invalid email address";
+ Class<?>[] groups() default {};
+ Class<? extends Payload>[] payload() default {};
+}
+
+public class ExpressionLanguageInjection {
+ final private static Validator validator =
+ Validation.buildDefaultValidatorFactory().getValidator();
+
+ public static void fuzzerTestOneInput(FuzzedDataProvider data) {
+ UserData uncheckedUserData = new UserData(data.consumeRemainingAsString());
+ validator.validate(uncheckedUserData);
+ }
+}
diff --git a/sanitizers/src/test/java/com/example/InsecureEmailValidator.java b/sanitizers/src/test/java/com/example/InsecureEmailValidator.java
new file mode 100644
index 00000000..d61e888d
--- /dev/null
+++ b/sanitizers/src/test/java/com/example/InsecureEmailValidator.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 static java.lang.String.format;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class InsecureEmailValidator implements ConstraintValidator<ValidEmailConstraint, String> {
+ @Override
+ public void initialize(ValidEmailConstraint email) {}
+
+ @Override
+ public boolean isValid(String email, ConstraintValidatorContext cxt) {
+ if (email == null || !email.matches(".+@.+\\..+")) {
+ // Insecure: do not call buildConstraintViolationWithTemplate with untrusted data!
+ cxt.buildConstraintViolationWithTemplate(format("Invalid email address: %s", email))
+ .addConstraintViolation();
+ return false;
+ }
+ return true;
+ }
+}