xref: /aosp_15_r20/external/jazzer-api/src/main/java/jaz/Zer.java (revision 33edd6723662ea34453766bfdca85dbfdd5342b8)
1*33edd672SMark // Copyright 2021 Code Intelligence GmbH
2*33edd672SMark //
3*33edd672SMark // Licensed under the Apache License, Version 2.0 (the "License");
4*33edd672SMark // you may not use this file except in compliance with the License.
5*33edd672SMark // You may obtain a copy of the License at
6*33edd672SMark //
7*33edd672SMark //      http://www.apache.org/licenses/LICENSE-2.0
8*33edd672SMark //
9*33edd672SMark // Unless required by applicable law or agreed to in writing, software
10*33edd672SMark // distributed under the License is distributed on an "AS IS" BASIS,
11*33edd672SMark // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*33edd672SMark // See the License for the specific language governing permissions and
13*33edd672SMark // limitations under the License.
14*33edd672SMark 
15*33edd672SMark package jaz;
16*33edd672SMark 
17*33edd672SMark import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh;
18*33edd672SMark import com.code_intelligence.jazzer.api.Jazzer;
19*33edd672SMark import java.io.*;
20*33edd672SMark import java.util.*;
21*33edd672SMark import java.util.concurrent.Callable;
22*33edd672SMark import java.util.function.Function;
23*33edd672SMark 
24*33edd672SMark /**
25*33edd672SMark  * A honeypot class that reports a finding on initialization.
26*33edd672SMark  *
27*33edd672SMark  * Class loading based on externally controlled data could lead to RCE
28*33edd672SMark  * depending on available classes on the classpath. Even if no applicable
29*33edd672SMark  * gadget class is available, allowing input to control class loading is a bad
30*33edd672SMark  * idea and should be prevented. A finding is generated whenever the class
31*33edd672SMark  * is loaded and initialized, regardless of its further use.
32*33edd672SMark  * <p>
33*33edd672SMark  * This class needs to implement {@link Serializable} to be considered in
34*33edd672SMark  * deserialization scenarios. It also implements common constructors, getter
35*33edd672SMark  * and setter and common interfaces to increase chances of passing
36*33edd672SMark  * deserialization checks.
37*33edd672SMark  * <p>
38*33edd672SMark  * <b>Note</b>: Jackson provides a nice list of "nasty classes" at
39*33edd672SMark  * <a
40*33edd672SMark  * href=https://github.com/FasterXML/jackson-databind/blob/2.14/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator.java>SubTypeValidator</a>.
41*33edd672SMark  * <p>
42*33edd672SMark  * <b>Note</b>: This class must not be referenced in any way by the rest of the code, not even
43*33edd672SMark  * statically. When referring to it, always use its hardcoded class name {@code jaz.Zer}.
44*33edd672SMark  */
45*33edd672SMark @SuppressWarnings({"rawtypes", "unused"})
46*33edd672SMark public class Zer
47*33edd672SMark     implements Serializable, Cloneable, Comparable<Zer>, Comparator, Closeable, Flushable, Iterable,
48*33edd672SMark                Iterator, Runnable, Callable, Function, Collection, List {
49*33edd672SMark   static final long serialVersionUID = 42L;
50*33edd672SMark 
51*33edd672SMark   // serialized size is 41 bytes
52*33edd672SMark   private static final byte REFLECTIVE_CALL_SANITIZER_ID = 0;
53*33edd672SMark   private static final byte DESERIALIZATION_SANITIZER_ID = 1;
54*33edd672SMark   private static final byte EXPRESSION_LANGUAGE_SANITIZER_ID = 2;
55*33edd672SMark 
56*33edd672SMark   // A byte representing the relevant sanitizer for a given jaz.Zer instance. It is used to check
57*33edd672SMark   // whether the corresponding sanitizer is disabled and jaz.Zer will not report a finding in this
58*33edd672SMark   // case. Each sanitizer which relies on this class must set this byte accordingly. We choose a
59*33edd672SMark   // single byte to represent the sanitizer in order to keep the serialized version of jaz.Zer
60*33edd672SMark   // objects small (currently 41 bytes) so that it fits in the 64 byte limit of the words that can
61*33edd672SMark   // be used with Jazzer's methods that guide the fuzzer towards generating inputs that contain or
62*33edd672SMark   // are equal to target strings. This limit comes from the corresponding libFuzzer hooks that
63*33edd672SMark   // Jazzer uses under the hood.
64*33edd672SMark   private byte sanitizer = REFLECTIVE_CALL_SANITIZER_ID;
65*33edd672SMark 
66*33edd672SMark   // Common constructors
Zer()67*33edd672SMark   public Zer() {
68*33edd672SMark     reportFindingIfEnabled();
69*33edd672SMark   }
70*33edd672SMark 
Zer(String arg1)71*33edd672SMark   public Zer(String arg1) {
72*33edd672SMark     reportFindingIfEnabled();
73*33edd672SMark   }
74*33edd672SMark 
Zer(String arg1, Throwable arg2)75*33edd672SMark   public Zer(String arg1, Throwable arg2) {
76*33edd672SMark     reportFindingIfEnabled();
77*33edd672SMark   }
78*33edd672SMark 
Zer(byte sanitizer)79*33edd672SMark   public Zer(byte sanitizer) {
80*33edd672SMark     this.sanitizer = sanitizer;
81*33edd672SMark     reportFindingIfEnabled();
82*33edd672SMark   }
83*33edd672SMark 
84*33edd672SMark   // A special static method that is called by the expression language injection sanitizer. We
85*33edd672SMark   // choose a parameterless method to keep the string that the sanitizer guides the fuzzer to
86*33edd672SMark   // generate within the 64-byte boundary required by the corresponding guiding methods.
el()87*33edd672SMark   public static void el() {
88*33edd672SMark     if (isSanitizerEnabled(EXPRESSION_LANGUAGE_SANITIZER_ID)) {
89*33edd672SMark       reportFinding();
90*33edd672SMark     }
91*33edd672SMark   }
92*33edd672SMark 
reportFindingIfEnabled()93*33edd672SMark   private void reportFindingIfEnabled() {
94*33edd672SMark     if (isSanitizerEnabled(sanitizer)) {
95*33edd672SMark       reportFinding();
96*33edd672SMark     }
97*33edd672SMark   }
98*33edd672SMark 
reportFinding()99*33edd672SMark   private static void reportFinding() {
100*33edd672SMark     Jazzer.reportFindingFromHook(new FuzzerSecurityIssueHigh("Remote Code Execution\n"
101*33edd672SMark         + "Unrestricted class/object creation based on externally controlled data may allow\n"
102*33edd672SMark         + "remote code execution depending on available classes on the classpath."));
103*33edd672SMark   }
104*33edd672SMark 
isSanitizerEnabled(byte sanitizerId)105*33edd672SMark   private static boolean isSanitizerEnabled(byte sanitizerId) {
106*33edd672SMark     String allDisabledHooks = System.getProperty("jazzer.disabled_hooks");
107*33edd672SMark     if (allDisabledHooks == null || allDisabledHooks.equals("")) {
108*33edd672SMark       return true;
109*33edd672SMark     }
110*33edd672SMark 
111*33edd672SMark     String sanitizer;
112*33edd672SMark     switch (sanitizerId) {
113*33edd672SMark       case DESERIALIZATION_SANITIZER_ID:
114*33edd672SMark         sanitizer = "com.code_intelligence.jazzer.sanitizers.Deserialization";
115*33edd672SMark         break;
116*33edd672SMark       case EXPRESSION_LANGUAGE_SANITIZER_ID:
117*33edd672SMark         sanitizer = "com.code_intelligence.jazzer.sanitizers.ExpressionLanguageInjection";
118*33edd672SMark         break;
119*33edd672SMark       default:
120*33edd672SMark         sanitizer = "com.code_intelligence.jazzer.sanitizers.ReflectiveCall";
121*33edd672SMark     }
122*33edd672SMark     return Arrays.stream(allDisabledHooks.split(",")).noneMatch(sanitizer::equals);
123*33edd672SMark   }
124*33edd672SMark 
125*33edd672SMark   // Getter/Setter
126*33edd672SMark 
getJaz()127*33edd672SMark   public Object getJaz() {
128*33edd672SMark     reportFindingIfEnabled();
129*33edd672SMark     return this;
130*33edd672SMark   }
131*33edd672SMark 
setJaz(String jaz)132*33edd672SMark   public void setJaz(String jaz) {
133*33edd672SMark     reportFindingIfEnabled();
134*33edd672SMark   }
135*33edd672SMark 
136*33edd672SMark   @Override
hashCode()137*33edd672SMark   public int hashCode() {
138*33edd672SMark     reportFindingIfEnabled();
139*33edd672SMark     return super.hashCode();
140*33edd672SMark   }
141*33edd672SMark 
142*33edd672SMark   @Override
equals(Object obj)143*33edd672SMark   public boolean equals(Object obj) {
144*33edd672SMark     reportFindingIfEnabled();
145*33edd672SMark     return super.equals(obj);
146*33edd672SMark   }
147*33edd672SMark 
148*33edd672SMark   @Override
toString()149*33edd672SMark   public String toString() {
150*33edd672SMark     reportFindingIfEnabled();
151*33edd672SMark     return super.toString();
152*33edd672SMark   }
153*33edd672SMark 
154*33edd672SMark   // Common interface stubs
155*33edd672SMark 
156*33edd672SMark   @Override
close()157*33edd672SMark   public void close() {
158*33edd672SMark     reportFindingIfEnabled();
159*33edd672SMark   }
160*33edd672SMark 
161*33edd672SMark   @Override
flush()162*33edd672SMark   public void flush() {
163*33edd672SMark     reportFindingIfEnabled();
164*33edd672SMark   }
165*33edd672SMark 
166*33edd672SMark   @Override
compareTo(Zer o)167*33edd672SMark   public int compareTo(Zer o) {
168*33edd672SMark     reportFindingIfEnabled();
169*33edd672SMark     return 0;
170*33edd672SMark   }
171*33edd672SMark 
172*33edd672SMark   @Override
compare(Object o1, Object o2)173*33edd672SMark   public int compare(Object o1, Object o2) {
174*33edd672SMark     reportFindingIfEnabled();
175*33edd672SMark     return 0;
176*33edd672SMark   }
177*33edd672SMark 
178*33edd672SMark   @Override
size()179*33edd672SMark   public int size() {
180*33edd672SMark     reportFindingIfEnabled();
181*33edd672SMark     return 0;
182*33edd672SMark   }
183*33edd672SMark 
184*33edd672SMark   @Override
isEmpty()185*33edd672SMark   public boolean isEmpty() {
186*33edd672SMark     reportFindingIfEnabled();
187*33edd672SMark     return false;
188*33edd672SMark   }
189*33edd672SMark 
190*33edd672SMark   @Override
contains(Object o)191*33edd672SMark   public boolean contains(Object o) {
192*33edd672SMark     reportFindingIfEnabled();
193*33edd672SMark     return false;
194*33edd672SMark   }
195*33edd672SMark 
196*33edd672SMark   @Override
toArray()197*33edd672SMark   public Object[] toArray() {
198*33edd672SMark     reportFindingIfEnabled();
199*33edd672SMark     return new Object[0];
200*33edd672SMark   }
201*33edd672SMark 
202*33edd672SMark   @Override
add(Object o)203*33edd672SMark   public boolean add(Object o) {
204*33edd672SMark     reportFindingIfEnabled();
205*33edd672SMark     return false;
206*33edd672SMark   }
207*33edd672SMark 
208*33edd672SMark   @Override
remove(Object o)209*33edd672SMark   public boolean remove(Object o) {
210*33edd672SMark     reportFindingIfEnabled();
211*33edd672SMark     return false;
212*33edd672SMark   }
213*33edd672SMark 
214*33edd672SMark   @Override
addAll(Collection c)215*33edd672SMark   public boolean addAll(Collection c) {
216*33edd672SMark     reportFindingIfEnabled();
217*33edd672SMark     return false;
218*33edd672SMark   }
219*33edd672SMark 
220*33edd672SMark   @Override
addAll(int index, Collection c)221*33edd672SMark   public boolean addAll(int index, Collection c) {
222*33edd672SMark     reportFindingIfEnabled();
223*33edd672SMark     return false;
224*33edd672SMark   }
225*33edd672SMark 
226*33edd672SMark   @Override
clear()227*33edd672SMark   public void clear() {
228*33edd672SMark     reportFindingIfEnabled();
229*33edd672SMark   }
230*33edd672SMark 
231*33edd672SMark   @Override
get(int index)232*33edd672SMark   public Object get(int index) {
233*33edd672SMark     reportFindingIfEnabled();
234*33edd672SMark     return this;
235*33edd672SMark   }
236*33edd672SMark 
237*33edd672SMark   @Override
set(int index, Object element)238*33edd672SMark   public Object set(int index, Object element) {
239*33edd672SMark     reportFindingIfEnabled();
240*33edd672SMark     return this;
241*33edd672SMark   }
242*33edd672SMark 
243*33edd672SMark   @Override
add(int index, Object element)244*33edd672SMark   public void add(int index, Object element) {
245*33edd672SMark     reportFindingIfEnabled();
246*33edd672SMark   }
247*33edd672SMark 
248*33edd672SMark   @Override
remove(int index)249*33edd672SMark   public Object remove(int index) {
250*33edd672SMark     reportFindingIfEnabled();
251*33edd672SMark     return this;
252*33edd672SMark   }
253*33edd672SMark 
254*33edd672SMark   @Override
indexOf(Object o)255*33edd672SMark   public int indexOf(Object o) {
256*33edd672SMark     reportFindingIfEnabled();
257*33edd672SMark     return 0;
258*33edd672SMark   }
259*33edd672SMark 
260*33edd672SMark   @Override
lastIndexOf(Object o)261*33edd672SMark   public int lastIndexOf(Object o) {
262*33edd672SMark     reportFindingIfEnabled();
263*33edd672SMark     return 0;
264*33edd672SMark   }
265*33edd672SMark 
266*33edd672SMark   @Override
267*33edd672SMark   @SuppressWarnings("ConstantConditions")
listIterator()268*33edd672SMark   public ListIterator listIterator() {
269*33edd672SMark     reportFindingIfEnabled();
270*33edd672SMark     return null;
271*33edd672SMark   }
272*33edd672SMark 
273*33edd672SMark   @Override
274*33edd672SMark   @SuppressWarnings("ConstantConditions")
listIterator(int index)275*33edd672SMark   public ListIterator listIterator(int index) {
276*33edd672SMark     reportFindingIfEnabled();
277*33edd672SMark     return null;
278*33edd672SMark   }
279*33edd672SMark 
280*33edd672SMark   @Override
subList(int fromIndex, int toIndex)281*33edd672SMark   public List subList(int fromIndex, int toIndex) {
282*33edd672SMark     reportFindingIfEnabled();
283*33edd672SMark     return this;
284*33edd672SMark   }
285*33edd672SMark 
286*33edd672SMark   @Override
retainAll(Collection c)287*33edd672SMark   public boolean retainAll(Collection c) {
288*33edd672SMark     reportFindingIfEnabled();
289*33edd672SMark     return false;
290*33edd672SMark   }
291*33edd672SMark 
292*33edd672SMark   @Override
removeAll(Collection c)293*33edd672SMark   public boolean removeAll(Collection c) {
294*33edd672SMark     reportFindingIfEnabled();
295*33edd672SMark     return false;
296*33edd672SMark   }
297*33edd672SMark 
298*33edd672SMark   @Override
containsAll(Collection c)299*33edd672SMark   public boolean containsAll(Collection c) {
300*33edd672SMark     reportFindingIfEnabled();
301*33edd672SMark     return false;
302*33edd672SMark   }
303*33edd672SMark 
304*33edd672SMark   @Override
toArray(Object[] a)305*33edd672SMark   public Object[] toArray(Object[] a) {
306*33edd672SMark     reportFindingIfEnabled();
307*33edd672SMark     return new Object[0];
308*33edd672SMark   }
309*33edd672SMark 
310*33edd672SMark   @Override
iterator()311*33edd672SMark   public Iterator iterator() {
312*33edd672SMark     reportFindingIfEnabled();
313*33edd672SMark     return this;
314*33edd672SMark   }
315*33edd672SMark 
316*33edd672SMark   @Override
run()317*33edd672SMark   public void run() {
318*33edd672SMark     reportFindingIfEnabled();
319*33edd672SMark   }
320*33edd672SMark 
321*33edd672SMark   @Override
hasNext()322*33edd672SMark   public boolean hasNext() {
323*33edd672SMark     reportFindingIfEnabled();
324*33edd672SMark     return false;
325*33edd672SMark   }
326*33edd672SMark 
327*33edd672SMark   @Override
next()328*33edd672SMark   public Object next() {
329*33edd672SMark     reportFindingIfEnabled();
330*33edd672SMark     return this;
331*33edd672SMark   }
332*33edd672SMark 
333*33edd672SMark   @Override
call()334*33edd672SMark   public Object call() throws Exception {
335*33edd672SMark     reportFindingIfEnabled();
336*33edd672SMark     return this;
337*33edd672SMark   }
338*33edd672SMark 
339*33edd672SMark   @Override
apply(Object o)340*33edd672SMark   public Object apply(Object o) {
341*33edd672SMark     reportFindingIfEnabled();
342*33edd672SMark     return this;
343*33edd672SMark   }
344*33edd672SMark 
345*33edd672SMark   @Override
346*33edd672SMark   @SuppressWarnings("MethodDoesntCallSuperMethod")
clone()347*33edd672SMark   public Object clone() {
348*33edd672SMark     reportFindingIfEnabled();
349*33edd672SMark     return this;
350*33edd672SMark   }
351*33edd672SMark 
352*33edd672SMark   // readObject calls can directly result in RCE, see https://github.com/frohoff/ysoserial for
353*33edd672SMark   // examples. Since deserialization doesn't call constructors (see
354*33edd672SMark   // https://docs.oracle.com/javase/7/docs/platform/serialization/spec/input.html#2971), we emit a
355*33edd672SMark   // finding right in the readObject method.
readObject(ObjectInputStream stream)356*33edd672SMark   private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
357*33edd672SMark     // Need to read in ourselves to initialize the sanitizer field.
358*33edd672SMark     stream.defaultReadObject();
359*33edd672SMark     reportFindingIfEnabled();
360*33edd672SMark   }
361*33edd672SMark }
362