Skip to content

Commit 12f47bc

Browse files
committed
Add UnsafeDeserialization
1 parent 0c724a8 commit 12f47bc

38 files changed

+2054
-3
lines changed

java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ may have unforeseen effects, such as the execution of arbitrary code.
1414
</p>
1515
<p>
1616
There are many different serialization frameworks. This query currently
17-
supports Kryo, XmlDecoder, XStream, SnakeYaml, and Java IO serialization through
18-
<code>ObjectInputStream</code>/<code>ObjectOutputStream</code>.
17+
supports Kryo, XmlDecoder, XStream, SnakeYaml, Hessian, JsonIO, YAMLBeans, Castor, Burlap,
18+
and Java IO serialization through <code>ObjectInputStream</code>/<code>ObjectOutputStream</code>.
1919
</p>
2020
</overview>
2121

@@ -75,6 +75,22 @@ Alvaro Muñoz &amp; Christian Schneider, RSAConference 2016:
7575
SnakeYaml documentation on deserialization:
7676
<a href="https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-loading-yaml">SnakeYaml deserialization</a>.
7777
</li>
78+
<li>
79+
Hessian deserialization and related gadget chains:
80+
<a href="https://paper.seebug.org/1137/">Hessian deserialization</a>.
81+
</li>
82+
<li>
83+
Castor and Hessian java deserialization vulnerabilities:
84+
<a href="https://securitylab.github.com/research/hessian-java-deserialization-castor-vulnerabilities/">Castor and Hessian deserialization</a>.
85+
</li>
86+
<li>
87+
Remote code execution in JYaml library:
88+
<a href="https://www.cybersecurity-help.cz/vdb/SB2020022512">JYaml deserialization</a>.
89+
</li>
90+
<li>
91+
JsonIO deserialization vulnerabilities:
92+
<a href="https://klezvirus.github.io/Advanced-Web-Hacking/Serialisation/">JsonIO deserialization</a>.
93+
</li>
7894
</references>
7995

8096
</qhelp>

java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,39 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
2121
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
2222

2323
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserializationSink }
24+
25+
override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) {
26+
exists(ClassInstanceExpr cie |
27+
cie.getConstructor().getDeclaringType() instanceof JsonReader and
28+
cie.getArgument(0) = prod.asExpr() and
29+
cie = succ.asExpr() and
30+
not exists(SafeJsonIo sji | sji.hasFlowToExpr(cie.getArgument(1)))
31+
)
32+
or
33+
exists(ClassInstanceExpr cie |
34+
cie.getConstructor().getDeclaringType() instanceof YamlReader and
35+
cie.getArgument(0) = prod.asExpr() and
36+
cie = succ.asExpr()
37+
)
38+
or
39+
exists(ClassInstanceExpr cie |
40+
cie.getConstructor().getDeclaringType() instanceof UnSafeHessianInput and
41+
cie.getArgument(0) = prod.asExpr() and
42+
cie = succ.asExpr()
43+
)
44+
or
45+
exists(ClassInstanceExpr cie |
46+
cie.getConstructor().getDeclaringType() instanceof BurlapInput and
47+
cie.getArgument(0) = prod.asExpr() and
48+
cie = succ.asExpr()
49+
)
50+
or
51+
exists(MethodAccess ma |
52+
ma.getMethod() instanceof BurlapInputInitMethod and
53+
ma.getArgument(0) = prod.asExpr() and
54+
ma.getQualifier() = succ.asExpr()
55+
)
56+
}
2457
}
2558

2659
from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeDeserializationConfig conf
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Provides classes and predicates for working with the Castor framework.
3+
*/
4+
5+
import java
6+
7+
/**
8+
* The class `org.exolab.castor.xml.Unmarshaller`.
9+
*/
10+
class Unmarshaller extends RefType {
11+
Unmarshaller() { this.hasQualifiedName("org.exolab.castor.xml", "Unmarshaller") }
12+
}
13+
14+
/** A method with the name `unmarshal` declared in `org.exolab.castor.xml.Unmarshaller`. */
15+
class UnmarshalMethod extends Method {
16+
UnmarshalMethod() {
17+
this.getDeclaringType() instanceof Unmarshaller and
18+
this.getName() = "unmarshal"
19+
}
20+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Provides classes and predicates for working with the Hession framework.
3+
*/
4+
5+
import java
6+
7+
/**
8+
* The class `com.caucho.hessian.io.HessianInput` or `com.caucho.hessian.io.Hessian2Input`.
9+
*/
10+
class UnSafeHessianInput extends RefType {
11+
UnSafeHessianInput() {
12+
this.hasQualifiedName("com.caucho.hessian.io", ["HessianInput", "Hessian2Input"])
13+
}
14+
}
15+
16+
/**
17+
* A HessianInput readObject method. This is either `HessianInput.readObject` or `Hessian2Input.readObject`.
18+
*/
19+
class UnSafeHessianInputReadObjectMethod extends Method {
20+
UnSafeHessianInputReadObjectMethod() {
21+
this.getDeclaringType() instanceof UnSafeHessianInput and
22+
this.getName() = "readObject"
23+
}
24+
}
25+
26+
/**
27+
* The class `com.caucho.burlap.io.BurlapInput`.
28+
*/
29+
class BurlapInput extends RefType {
30+
BurlapInput() { this.hasQualifiedName("com.caucho.burlap.io", "BurlapInput") }
31+
}
32+
33+
/** A method with the name `readObject` declared in `com.caucho.burlap.io.BurlapInput`. */
34+
class BurlapInputReadObjectMethod extends Method {
35+
BurlapInputReadObjectMethod() {
36+
this.getDeclaringType() instanceof BurlapInput and
37+
this.getName() = "readObject"
38+
}
39+
}
40+
41+
/** A method with the name `init` declared in `com.caucho.burlap.io.BurlapInput`. */
42+
class BurlapInputInitMethod extends Method {
43+
BurlapInputInitMethod() {
44+
this.getDeclaringType() instanceof BurlapInput and
45+
this.getName() = "init"
46+
}
47+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Provides classes and predicates for working with the JYaml framework.
3+
*/
4+
5+
import java
6+
7+
/**
8+
* The class `org.ho.yaml.Yaml`.
9+
*/
10+
class JYaml extends RefType {
11+
JYaml() { this.hasQualifiedName("org.ho.yaml", "Yaml") }
12+
}
13+
14+
/**
15+
* A JYaml unsafe load method. This is either `YAML.load` or
16+
* `YAML.loadType` or `YAML.loadStream` or `YAML.loadStreamOfType`.
17+
*/
18+
class JYamlUnSafeLoadMethod extends Method {
19+
JYamlUnSafeLoadMethod() {
20+
this.getDeclaringType() instanceof JYaml and
21+
this.getName() in ["load", "loadType", "loadStream", "loadStreamOfType"]
22+
}
23+
}
24+
25+
/**
26+
* The class `org.ho.yaml.YamlConfig`.
27+
*/
28+
class JYamlConfig extends RefType {
29+
JYamlConfig() { this.hasQualifiedName("org.ho.yaml", "YamlConfig") }
30+
}
31+
32+
/**
33+
* A JYamlConfig unsafe load method. This is either `YamlConfig.load` or
34+
* `YAML.loadType` or `YamlConfig.loadStream` or `YamlConfig.loadStreamOfType`.
35+
*/
36+
class JYamlConfigUnSafeLoadMethod extends Method {
37+
JYamlConfigUnSafeLoadMethod() {
38+
this.getDeclaringType() instanceof JYamlConfig and
39+
this.getName() in ["load", "loadType", "loadStream", "loadStreamOfType"]
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Provides classes and predicates for working with the Json-io framework.
3+
*/
4+
5+
import java
6+
import semmle.code.java.Maps
7+
8+
/**
9+
* The class `com.cedarsoftware.util.io.JsonReader`.
10+
*/
11+
class JsonReader extends RefType {
12+
JsonReader() { this.hasQualifiedName("com.cedarsoftware.util.io", "JsonReader") }
13+
}
14+
15+
/** A method with the name `jsonToJava` declared in `com.cedarsoftware.util.io.JsonReader`. */
16+
class JsonIoJsonToJavaMethod extends Method {
17+
JsonIoJsonToJavaMethod() {
18+
this.getDeclaringType() instanceof JsonReader and
19+
this.getName() = "jsonToJava"
20+
}
21+
}
22+
23+
/** A method with the name `readObject` declared in `com.cedarsoftware.util.io.JsonReader`. */
24+
class JsonIoReadObjectMethod extends Method {
25+
JsonIoReadObjectMethod() {
26+
this.getDeclaringType() instanceof JsonReader and
27+
this.getName() = "readObject"
28+
}
29+
}
30+
31+
/**
32+
* A call to `Map.put` method, set the value of the `USE_MAPS` key to `true`.
33+
*/
34+
class JsonIoSafeOptionalArgs extends MethodAccess {
35+
JsonIoSafeOptionalArgs() {
36+
this.getMethod().getDeclaringType().getASourceSupertype*() instanceof MapType and
37+
this.getMethod().hasName("put") and
38+
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "USE_MAPS" and
39+
this.getArgument(1).(CompileTimeConstantExpr).getBooleanValue() = true
40+
}
41+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Provides classes and predicates for working with the YamlBeans framework.
3+
*/
4+
5+
import java
6+
7+
/**
8+
* The class `com.esotericsoftware.yamlbeans.YamlReader`.
9+
*/
10+
class YamlReader extends RefType {
11+
YamlReader() { this.hasQualifiedName("com.esotericsoftware.yamlbeans", "YamlReader") }
12+
}
13+
14+
/** A method with the name `read` declared in `com.esotericsoftware.yamlbeans.YamlReader`. */
15+
class YamlReaderReadMethod extends Method {
16+
YamlReaderReadMethod() {
17+
this.getDeclaringType() instanceof YamlReader and
18+
this.getName() = "read"
19+
}
20+
}

java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import semmle.code.java.frameworks.Kryo
22
import semmle.code.java.frameworks.XStream
33
import semmle.code.java.frameworks.SnakeYaml
44
import semmle.code.java.frameworks.FastJson
5+
import semmle.code.java.frameworks.JYaml
6+
import semmle.code.java.frameworks.JsonIo
7+
import semmle.code.java.frameworks.YamlBeans
8+
import semmle.code.java.frameworks.Hessian
9+
import semmle.code.java.frameworks.Castor
510
import semmle.code.java.frameworks.apache.Lang
611

712
class ObjectInputStreamReadObjectMethod extends Method {
@@ -50,6 +55,29 @@ class SafeKryo extends DataFlow2::Configuration {
5055
}
5156
}
5257

58+
class SafeJsonIo extends DataFlow2::Configuration {
59+
SafeJsonIo() { this = "UnsafeDeserialization::SafeJsonIo" }
60+
61+
override predicate isSource(DataFlow::Node src) {
62+
exists(MethodAccess ma |
63+
ma instanceof JsonIoSafeOptionalArgs and
64+
src.asExpr() = ma.getQualifier()
65+
)
66+
}
67+
68+
override predicate isSink(DataFlow::Node sink) {
69+
exists(MethodAccess ma |
70+
ma.getMethod() instanceof JsonIoJsonToJavaMethod and
71+
sink.asExpr() = ma.getArgument(1)
72+
)
73+
or
74+
exists(ClassInstanceExpr cie |
75+
cie.getConstructor().getDeclaringType() instanceof JsonReader and
76+
sink.asExpr() = cie.getArgument(1)
77+
)
78+
}
79+
}
80+
5381
predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
5482
exists(Method m | m = ma.getMethod() |
5583
m instanceof ObjectInputStreamReadObjectMethod and
@@ -81,6 +109,27 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
81109
ma.getMethod() instanceof FastJsonParseMethod and
82110
not fastJsonLooksSafe() and
83111
sink = ma.getArgument(0)
112+
or
113+
ma.getMethod() instanceof JYamlUnSafeLoadMethod and
114+
sink = ma.getArgument(0)
115+
or
116+
ma.getMethod() instanceof JYamlConfigUnSafeLoadMethod and
117+
sink = ma.getArgument(0)
118+
or
119+
ma.getMethod() instanceof JsonIoJsonToJavaMethod and
120+
sink = ma.getArgument(0) and
121+
not exists(SafeJsonIo sji | sji.hasFlowToExpr(ma.getArgument(1)))
122+
or
123+
ma.getMethod() instanceof JsonIoReadObjectMethod and
124+
sink = ma.getQualifier()
125+
or
126+
ma.getMethod() instanceof YamlReaderReadMethod and sink = ma.getQualifier()
127+
or
128+
ma.getMethod() instanceof UnSafeHessianInputReadObjectMethod and sink = ma.getQualifier()
129+
or
130+
ma.getMethod() instanceof UnmarshalMethod and sink = ma.getAnArgument()
131+
or
132+
ma.getMethod() instanceof BurlapInputReadObjectMethod and sink = ma.getQualifier()
84133
)
85134
}
86135

0 commit comments

Comments
 (0)