Skip to content

Commit 03e8865

Browse files
authored
Merge pull request #20025 from owen-mc/java/unsafe-deserialization
Java: add extra sink for `java/unsafe-deserialization`
2 parents 14a362d + 7764fbb commit 03e8865

File tree

7 files changed

+313
-153
lines changed

7 files changed

+313
-153
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The qualifiers of a calls to `readObject` on any classes that implement `java.io.ObjectInput` are now recognised as sinks for `java/unsafe-deserialization`. Previously this was only the case for classes which extend `java.io.ObjectInputStream`.

java/ql/lib/semmle/code/java/JDK.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ class TypeObjectOutputStream extends RefType {
211211
TypeObjectOutputStream() { this.hasQualifiedName("java.io", "ObjectOutputStream") }
212212
}
213213

214+
/** The type `java.io.ObjectInput`. */
215+
class TypeObjectInput extends RefType {
216+
TypeObjectInput() { this.hasQualifiedName("java.io", "ObjectInput") }
217+
}
218+
214219
/** The type `java.io.ObjectInputStream`. */
215220
class TypeObjectInputStream extends RefType {
216221
TypeObjectInputStream() { this.hasQualifiedName("java.io", "ObjectInputStream") }

java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@ private import semmle.code.java.frameworks.google.Gson
2323
private import semmle.code.java.frameworks.apache.Lang
2424
private import semmle.code.java.Reflection
2525

26-
private class ObjectInputStreamReadObjectMethod extends Method {
27-
ObjectInputStreamReadObjectMethod() {
26+
private class ObjectInputReadObjectMethod extends Method {
27+
ObjectInputReadObjectMethod() {
28+
this.getDeclaringType().getASourceSupertype*() instanceof TypeObjectInput and
29+
this.hasName("readObject")
30+
}
31+
}
32+
33+
private class ObjectInputStreamReadUnsharedMethod extends Method {
34+
ObjectInputStreamReadUnsharedMethod() {
2835
this.getDeclaringType().getASourceSupertype*() instanceof TypeObjectInputStream and
29-
(this.hasName("readObject") or this.hasName("readUnshared"))
36+
this.hasName("readUnshared")
3037
}
3138
}
3239

@@ -147,12 +154,13 @@ private module SafeKryoFlow = DataFlow::Global<SafeKryoConfig>;
147154
*/
148155
predicate unsafeDeserialization(MethodCall ma, Expr sink) {
149156
exists(Method m | m = ma.getMethod() |
150-
m instanceof ObjectInputStreamReadObjectMethod and
157+
m instanceof ObjectInputReadObjectMethod and
151158
sink = ma.getQualifier() and
152-
not exists(DataFlow::ExprNode node |
153-
node.getExpr() = sink and
154-
node.getTypeBound() instanceof SafeObjectInputStreamType
155-
)
159+
not DataFlow::exprNode(sink).getTypeBound() instanceof SafeObjectInputStreamType
160+
or
161+
m instanceof ObjectInputStreamReadUnsharedMethod and
162+
sink = ma.getQualifier() and
163+
not DataFlow::exprNode(sink).getTypeBound() instanceof SafeObjectInputStreamType
156164
or
157165
m instanceof XmlDecoderReadObjectMethod and
158166
sink = ma.getQualifier()

java/ql/test/query-tests/security/CWE-502/A.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
package unsafedeserialization;
2+
13
import java.io.*;
24
import java.net.Socket;
35
import java.beans.XMLDecoder;
6+
import com.example.MyObjectInput;
47
import com.thoughtworks.xstream.XStream;
58
import com.esotericsoftware.kryo.Kryo;
69
import com.esotericsoftware.kryo.io.Input;
@@ -10,13 +13,23 @@
1013
import org.nibblesec.tools.SerialKiller;
1114

1215
public class A {
13-
public Object deserialize1(Socket sock) throws java.io.IOException, ClassNotFoundException {
16+
public Object deserialize1a(Socket sock) throws java.io.IOException, ClassNotFoundException {
1417
InputStream inputStream = sock.getInputStream(); // $ Source
1518
ObjectInputStream in = new ObjectInputStream(inputStream);
1619
return in.readObject(); // $ Alert
1720
}
1821

19-
public Object deserialize2(Socket sock) throws java.io.IOException, ClassNotFoundException {
22+
public Object deserialize2() throws java.io.IOException, ClassNotFoundException {
23+
ObjectInput objectInput = A.getTaintedObjectInput(); // $ Source
24+
return objectInput.readObject(); // $ Alert
25+
}
26+
27+
public Object deserialize3() throws java.io.IOException, ClassNotFoundException {
28+
MyObjectInput objectInput = A.getTaintedMyObjectInput(); // $ Source
29+
return objectInput.readObject(); // $ Alert
30+
}
31+
32+
public Object deserialize4(Socket sock) throws java.io.IOException, ClassNotFoundException {
2033
InputStream inputStream = sock.getInputStream(); // $ Source
2134
ObjectInputStream in = new ObjectInputStream(inputStream);
2235
return in.readUnshared(); // $ Alert
@@ -28,20 +41,20 @@ public Object deserializeWithSerialKiller(Socket sock) throws java.io.IOExceptio
2841
return in.readUnshared(); // OK
2942
}
3043

31-
public Object deserialize3(Socket sock) throws java.io.IOException {
44+
public Object deserialize5(Socket sock) throws java.io.IOException {
3245
InputStream inputStream = sock.getInputStream(); // $ Source
3346
XMLDecoder d = new XMLDecoder(inputStream);
3447
return d.readObject(); // $ Alert
3548
}
3649

37-
public Object deserialize4(Socket sock) throws java.io.IOException {
50+
public Object deserialize6(Socket sock) throws java.io.IOException {
3851
XStream xs = new XStream();
3952
InputStream inputStream = sock.getInputStream(); // $ Source
4053
Reader reader = new InputStreamReader(inputStream);
4154
return xs.fromXML(reader); // $ Alert
4255
}
4356

44-
public void deserialize5(Socket sock) throws java.io.IOException {
57+
public void deserialize7(Socket sock) throws java.io.IOException {
4558
Kryo kryo = new Kryo();
4659
Input input = new Input(sock.getInputStream()); // $ Source
4760
A a1 = kryo.readObject(input, A.class); // $ Alert
@@ -56,7 +69,7 @@ private Kryo getSafeKryo() throws java.io.IOException {
5669
return kryo;
5770
}
5871

59-
public void deserialize6(Socket sock) throws java.io.IOException {
72+
public void deserialize8(Socket sock) throws java.io.IOException {
6073
Kryo kryo = getSafeKryo();
6174
Input input = new Input(sock.getInputStream());
6275
Object o = kryo.readClassAndObject(input); // OK
@@ -101,4 +114,8 @@ public void deserializeSnakeYaml4(Socket sock) throws java.io.IOException {
101114
A o4 = yaml.loadAs(input, A.class); // $ Alert
102115
A o5 = yaml.loadAs(new InputStreamReader(input), A.class); // $ Alert
103116
}
117+
118+
static ObjectInput getTaintedObjectInput() { return null; }
119+
120+
static MyObjectInput getTaintedMyObjectInput() { return null; }
104121
}

0 commit comments

Comments
 (0)