Skip to content

Commit a0942e0

Browse files
edvraaedvraa
authored andcommitted
JsonConvert
1 parent f4cb6c5 commit a0942e0

File tree

4 files changed

+122
-2
lines changed

4 files changed

+122
-2
lines changed

csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,16 @@ where
4646
exists(TaintToConstructorOrStaticMethodTrackingConfig taintTracking2 |
4747
taintTracking2.hasFlowPath(userInput, deserializeCallArg)
4848
)
49+
or
50+
// JsonConvert static method call, but with additional unsafe typename tracking
51+
exists(
52+
JsonConvertTrackingConfig taintTrackingJsonConvert, TypeNameTrackingConfig typenameTracking,
53+
DataFlow::PathNode settingsCallArg
54+
|
55+
taintTrackingJsonConvert.hasFlowPath(userInput, deserializeCallArg) and
56+
typenameTracking.hasFlowPath(_, settingsCallArg) and
57+
deserializeCallArg.getNode().asExpr().getParent() =
58+
settingsCallArg.getNode().asExpr().getParent()
59+
)
4960
select deserializeCallArg, userInput, deserializeCallArg, "$@ flows to unsafe deserializer.",
5061
userInput, "User-provided data"

csharp/ql/src/semmle/code/csharp/frameworks/JsonNET.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ module JsonNET {
1717
JsonClass() { this.getParent() instanceof JsonNETNamespace }
1818
}
1919

20+
/** Newtonsoft.Json.TypeNameHandling enum */
21+
class TypeNameHandlingEnum extends Enum {
22+
TypeNameHandlingEnum() {
23+
this.getParent() instanceof JsonNETNamespace and
24+
this.hasName("TypeNameHandling")
25+
}
26+
}
27+
28+
/** Newtonsoft.Json.JsonSerializerSettings class */
29+
class JsonSerializerSettingsClass extends JsonClass {
30+
JsonSerializerSettingsClass() { this.hasName("JsonSerializerSettings") }
31+
}
32+
2033
/** The class `Newtonsoft.Json.JsonConvert`. */
2134
class JsonConvertClass extends JsonClass, LibraryTypeDataFlow {
2235
JsonConvertClass() { this.hasName("JsonConvert") }

csharp/ql/src/semmle/code/csharp/security/dataflow/UnsafeDeserialization.qll

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ module UnsafeDeserialization {
5959
* User input to object method call deserialization flow tracking.
6060
*/
6161
class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
62-
TaintToObjectMethodTrackingConfig() { this = "UnsafeDeserialization1" }
62+
TaintToObjectMethodTrackingConfig() { this = "TaintToObjectMethodTrackingConfig" }
6363

6464
override predicate isSource(DataFlow::Node source) { source instanceof Source }
6565

@@ -68,11 +68,81 @@ module UnsafeDeserialization {
6868
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
6969
}
7070

71+
/**
72+
* User input to `JsonConvert` call deserialization flow tracking.
73+
*/
74+
class JsonConvertTrackingConfig extends TaintTracking::Configuration {
75+
JsonConvertTrackingConfig() { this = "JsonConvertTrackingConfig" }
76+
77+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
78+
79+
override predicate isSink(DataFlow::Node sink) {
80+
sink instanceof NewtonsoftJsonConvertDeserializeObjectMethodSink
81+
}
82+
83+
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
84+
}
85+
86+
/**
87+
* Tracks unsafe `TypeNameHandling` setting to `JsonConvert` call
88+
*/
89+
class TypeNameTrackingConfig extends DataFlow::Configuration {
90+
TypeNameTrackingConfig() { this = "TypeNameTrackingConfig" }
91+
92+
override predicate isSource(DataFlow::Node source) {
93+
(
94+
source.asExpr() instanceof MemberConstantAccess and
95+
source.getType() instanceof TypeNameHandlingEnum
96+
or
97+
source.asExpr() instanceof IntegerLiteral
98+
) and
99+
source.asExpr().hasValue() and
100+
not source.asExpr().getValue() = "0"
101+
}
102+
103+
override predicate isSink(DataFlow::Node sink) {
104+
exists(MethodCall mc, Method m, Expr expr |
105+
m = mc.getTarget() and
106+
(
107+
not mc.getArgument(0).hasValue() and
108+
m instanceof NewtonsoftJsonConvertClassDeserializeObjectMethod
109+
) and
110+
expr = mc.getAnArgument() and
111+
sink.asExpr() = expr and
112+
expr.getType() instanceof JsonSerializerSettingsClass
113+
)
114+
}
115+
116+
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
117+
node1.asExpr() instanceof IntegerLiteral and
118+
node2.asExpr().(CastExpr).getExpr() = node1.asExpr()
119+
or
120+
node1.getType() instanceof TypeNameHandlingEnum and
121+
exists(PropertyWrite pw, Property p, Assignment a |
122+
a.getLValue() = pw and
123+
pw.getProperty() = p and
124+
p.getDeclaringType() instanceof JsonSerializerSettingsClass and
125+
p.hasName("TypeNameHandling") and
126+
(
127+
node1.asExpr() = a.getRValue() and
128+
node2.asExpr() = pw.getQualifier()
129+
or
130+
exists(ObjectInitializer oi |
131+
node1.asExpr() = oi.getAMemberInitializer().getRValue() and
132+
node2.asExpr() = oi
133+
)
134+
)
135+
)
136+
}
137+
}
138+
71139
/**
72140
* User input to static method or constructor call deserialization flow tracking.
73141
*/
74142
class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Configuration {
75-
TaintToConstructorOrStaticMethodTrackingConfig() { this = "UnsafeDeserialization2" }
143+
TaintToConstructorOrStaticMethodTrackingConfig() {
144+
this = "TaintToConstructorOrStaticMethodTrackingConfig"
145+
}
76146

77147
override predicate isSource(DataFlow::Node source) { source instanceof Source }
78148

@@ -826,4 +896,18 @@ module UnsafeDeserialization {
826896
)
827897
}
828898
}
899+
900+
/** Newtonsoft.Json.JsonConvert */
901+
private class NewtonsoftJsonConvertDeserializeObjectMethodSink extends ConstructorOrStaticMethodSink {
902+
NewtonsoftJsonConvertDeserializeObjectMethodSink() {
903+
exists(MethodCall mc, Method m |
904+
m = mc.getTarget() and
905+
(
906+
not mc.getArgument(0).hasValue() and
907+
m instanceof NewtonsoftJsonConvertClassDeserializeObjectMethod
908+
) and
909+
this.asExpr() = mc.getArgument(0)
910+
)
911+
}
912+
}
829913
}

csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import csharp
7+
import semmle.code.csharp.frameworks.JsonNET::JsonNET
78

89
/** An unsafe deserializer. */
910
abstract class UnsafeDeserializer extends Callable { }
@@ -67,6 +68,8 @@ class WeakTypeDeserializer extends Class {
6768
this instanceof SharpSerializerClass
6869
or
6970
this instanceof YamlDotNetDeserializerClass
71+
or
72+
this instanceof JsonConvertClass
7073
}
7174
}
7275

@@ -657,3 +660,12 @@ class YamlDotNetDeserializerClasseserializeMethod extends Method, UnsafeDeserial
657660
)
658661
}
659662
}
663+
664+
/** `Newtonsoft.Json.JsonConvert.DeserializeObject` method */
665+
class NewtonsoftJsonConvertClassDeserializeObjectMethod extends Method, UnsafeDeserializer {
666+
NewtonsoftJsonConvertClassDeserializeObjectMethod() {
667+
this.getDeclaringType() instanceof JsonConvertClass and
668+
this.hasName("DeserializeObject") and
669+
this.isStatic()
670+
}
671+
}

0 commit comments

Comments
 (0)