Skip to content

Commit c365a0b

Browse files
committed
feat: add hooks for additional EL evaluation methods
1 parent 6da8018 commit c365a0b

File tree

7 files changed

+100
-14
lines changed

7 files changed

+100
-14
lines changed

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ TEST_MAVEN_ARTIFACTS = [
8181
"com.google.truth.extensions:truth-proto-extension:1.4.5",
8282
"com.google.truth:truth:1.4.5",
8383
"jakarta.el:jakarta.el-api:6.0.1",
84+
"jakarta.validation:jakarta.validation-api:3.0.2",
8485
"javax.persistence:javax.persistence-api:2.2",
8586
"junit:junit:4.13.2",
8687
"org.assertj:assertj-core:3.27.6",

maven_install.json

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
3-
"__INPUT_ARTIFACTS_HASH": 547805092,
4-
"__RESOLVED_ARTIFACTS_HASH": 467856921,
3+
"__INPUT_ARTIFACTS_HASH": 109726797,
4+
"__RESOLVED_ARTIFACTS_HASH": -368514076,
55
"conflict_resolution": {
66
"com.google.code.gson:gson:2.8.6": "com.google.code.gson:gson:2.8.9",
77
"com.google.j2objc:j2objc-annotations:2.8": "com.google.j2objc:j2objc-annotations:3.1",
@@ -188,6 +188,12 @@
188188
},
189189
"version": "6.0.1"
190190
},
191+
"jakarta.validation:jakarta.validation-api": {
192+
"shasums": {
193+
"jar": "291c25e6910cc6a7ebd96d4c6baebf6d7c37676c5482c2d96146e901b62c1fc9"
194+
},
195+
"version": "3.0.2"
196+
},
191197
"javax.activation:javax.activation-api": {
192198
"shasums": {
193199
"jar": "43fdef0b5b6ceb31b0424b208b930c74ab58fac2ceeb7b3f6fd3aeb8b5ca4393"
@@ -1295,6 +1301,17 @@
12951301
"jakarta.el:jakarta.el-api": [
12961302
"jakarta.el"
12971303
],
1304+
"jakarta.validation:jakarta.validation-api": [
1305+
"jakarta.validation",
1306+
"jakarta.validation.bootstrap",
1307+
"jakarta.validation.constraints",
1308+
"jakarta.validation.constraintvalidation",
1309+
"jakarta.validation.executable",
1310+
"jakarta.validation.groups",
1311+
"jakarta.validation.metadata",
1312+
"jakarta.validation.spi",
1313+
"jakarta.validation.valueextraction"
1314+
],
12981315
"javax.activation:javax.activation-api": [
12991316
"javax.activation"
13001317
],
@@ -2692,6 +2709,7 @@
26922709
"io.github.classgraph:classgraph",
26932710
"io.projectreactor:reactor-core",
26942711
"jakarta.el:jakarta.el-api",
2712+
"jakarta.validation:jakarta.validation-api",
26952713
"javax.activation:javax.activation-api",
26962714
"javax.annotation:javax.annotation-api",
26972715
"javax.el:javax.el-api",

sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ object ExpressionLanguageInjection {
3333
private const val EXPRESSION_LANGUAGE_ATTACK =
3434
"\${Byte.class.forName(\"$HONEYPOT_CLASS_NAME\").getMethod(\"el\").invoke(null)}"
3535
private const val SPRING_EXPRESSION_LANGUAGE_ATTACK = "T($HONEYPOT_CLASS_NAME).el()"
36+
private const val ELPROCESSOR_LANGUAGE_ATTACK =
37+
"\"\".getClass().forName(\"$HONEYPOT_CLASS_NAME\").getMethod(\"el\").invoke(null)"
3638

3739
init {
3840
require(EXPRESSION_LANGUAGE_ATTACK.length <= 64) {
@@ -41,6 +43,9 @@ object ExpressionLanguageInjection {
4143
require(SPRING_EXPRESSION_LANGUAGE_ATTACK.length <= 64) {
4244
"Expression language exploit must fit in a table of recent compares entry (64 bytes)"
4345
}
46+
require(ELPROCESSOR_LANGUAGE_ATTACK.length <= 64) {
47+
"Expression language exploit must fit in a table of recent compares entry (64 bytes)"
48+
}
4449
}
4550

4651
@MethodHooks(
@@ -80,17 +85,50 @@ object ExpressionLanguageInjection {
8085
Jazzer.guideTowardsContainment(expression, EXPRESSION_LANGUAGE_ATTACK, hookId)
8186
}
8287

88+
@MethodHooks(
89+
MethodHook(
90+
type = HookType.BEFORE,
91+
targetClassName = "javax.el.ELProcessor",
92+
targetMethod = "eval",
93+
),
94+
MethodHook(
95+
type = HookType.BEFORE,
96+
targetClassName = "jakarta.el.ELProcessor",
97+
targetMethod = "eval",
98+
),
99+
)
100+
@JvmStatic
101+
fun hookElProcessor(
102+
method: MethodHandle?,
103+
thisObject: Any?,
104+
arguments: Array<Any>,
105+
hookId: Int,
106+
) {
107+
if (arguments.size != 1) {
108+
return
109+
}
110+
val message = arguments[0] as String
111+
Jazzer.guideTowardsContainment(message, ELPROCESSOR_LANGUAGE_ATTACK, hookId)
112+
}
113+
83114
// With default configurations the argument to
84115
// ConstraintValidatorContext.buildConstraintViolationWithTemplate() will be evaluated by an
85116
// Expression Language interpreter which allows arbitrary code execution if the attacker has
86117
// control of the method argument.
87118
//
88119
// References: CVE-2018-16621
89120
// https://securitylab.github.com/research/bean-validation-RCE/
90-
@MethodHook(
91-
type = HookType.BEFORE,
92-
targetClassName = "javax.validation.ConstraintValidatorContext",
93-
targetMethod = "buildConstraintViolationWithTemplate",
121+
@MethodHooks(
122+
MethodHook(
123+
type = HookType.BEFORE,
124+
targetClassName = "javax.validation.ConstraintValidatorContext",
125+
targetMethod = "buildConstraintViolationWithTemplate",
126+
),
127+
MethodHook(
128+
type = HookType.BEFORE,
129+
targetClassName = "jakarta.validation.ConstraintValidatorContext",
130+
targetMethod = "buildConstraintViolationWithTemplate",
131+
),
94132
)
95133
@JvmStatic
96134
fun hookBuildConstraintViolationWithTemplate(

sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ java_junit5_test(
2020
"//src/main/java/com/code_intelligence/jazzer/api:hooks",
2121
"@clojure_jar//jar",
2222
"@maven//:jakarta_el_jakarta_el_api",
23+
"@maven//:jakarta_validation_jakarta_validation_api",
2324
"@maven//:javax_el_javax_el_api",
2425
"@maven//:javax_persistence_javax_persistence_api",
2526
"@maven//:javax_validation_validation_api",

sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/HookBindingSanityTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public boolean equals(Object o) {
114114
new MethodRef(
115115
"org.springframework.expression.spel.standard.SpelExpressionParser"),
116116
new MethodRef("jakarta.el.ExpressionFactory"),
117+
new MethodRef("jakarta.el.ELProcessor"),
117118
new MethodRef("java.util.regex.Pattern$CharPredicate"),
118119
new MethodRef("javax.xml.xpath.XPath", "evaluateExpression", null),
119120
new MethodRef(

sanitizers/src/test/java/com/example/BUILD.bazel

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,31 @@ java_fuzz_target_test(
4545
verify_crash_reproducer = False,
4646
)
4747

48-
java_fuzz_target_test(
49-
name = "ExpressionLanguageInjection",
48+
[java_fuzz_target_test(
49+
name = "ExpressionLanguageInjection_" + method,
5050
srcs = [
5151
"ExpressionLanguageInjection.java",
5252
],
5353
allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh"],
54-
expected_warning_or_error = "WARN: Some hooks could not be applied to class files built for Java 7 or lower.",
5554
tags = ["dangerous"],
5655
target_class = "com.example.ExpressionLanguageInjection",
56+
target_method = method,
5757
# The reproducer can't find jaz.Zer and thus doesn't crash.
5858
verify_crash_reproducer = False,
59+
runtime_deps = [
60+
"@maven//:org_junit_jupiter_junit_jupiter_engine",
61+
],
5962
deps = [
63+
"//deploy:jazzer-junit",
6064
"//sanitizers/src/test/java/com/example/el:ExpressionLanguageExample",
65+
"@maven//:javax_el_javax_el_api",
6166
"@maven//:javax_validation_validation_api",
67+
"@maven//:org_junit_jupiter_junit_jupiter_api",
6268
],
63-
)
69+
) for method in [
70+
"fuzzValidator",
71+
"fuzzEval",
72+
]]
6473

6574
java_fuzz_target_test(
6675
name = "AbsoluteFilePathTraversal",

sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,41 @@
1616

1717
package com.example;
1818

19-
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
19+
import com.code_intelligence.jazzer.junit.FuzzTest;
20+
import com.code_intelligence.jazzer.mutation.annotation.NotNull;
2021
import com.example.el.UserData;
2122
import java.util.logging.Level;
2223
import java.util.logging.LogManager;
24+
import javax.el.ELException;
25+
import javax.el.ELProcessor;
2326
import javax.validation.Validation;
2427
import javax.validation.Validator;
28+
import org.junit.jupiter.api.BeforeEach;
2529

2630
public class ExpressionLanguageInjection {
2731
private static final Validator validator =
2832
Validation.buildDefaultValidatorFactory().getValidator();
2933

30-
public static void fuzzerInitialize() {
34+
@BeforeEach
35+
public void setUp() {
3136
LogManager.getLogManager().getLogger("").setLevel(Level.SEVERE);
3237
}
3338

34-
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
35-
UserData uncheckedUserData = new UserData(data.consumeRemainingAsString());
39+
@FuzzTest
40+
void fuzzValidator(@NotNull String data) {
41+
UserData uncheckedUserData = new UserData(data);
3642
validator.validate(uncheckedUserData);
3743
}
44+
45+
@FuzzTest
46+
void fuzzEval(@NotNull String data) {
47+
ELProcessor elp = new ELProcessor();
48+
try {
49+
elp.eval(data);
50+
} catch (ELException
51+
| IllegalStateException
52+
| IllegalArgumentException
53+
| ArithmeticException ignored) {
54+
}
55+
}
3856
}

0 commit comments

Comments
 (0)