Skip to content

Commit 44ff623

Browse files
authored
Merge pull request github#5508 from edvraa/deserializers
deserialization sinks
2 parents 221a259 + db2f9ad commit 44ff623

35 files changed

+1993
-69
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ it may be necessary to use a different deserialization framework.</p>
2727

2828
<sample src="UnsafeDeserializationUntrustedInputGood.cs" />
2929

30+
<p>In the following example potentially untrusted stream and type is deserialized using a
31+
<code>DataContractJsonSerializer</code> which is known to be vulnerable with user supplied types.</p>
32+
33+
<sample src="UnsafeDeserializationUntrustedInputTypeBad.cs" />
34+
35+
<p>To fix this specific vulnerability, we are using hardcoded
36+
Plain Old CLR Object (<a href="https://en.wikipedia.org/wiki/Plain_old_CLR_object">POCO</a>) type. In other cases,
37+
it may be necessary to use a different deserialization framework.</p>
38+
39+
<sample src="UnsafeDeserializationUntrustedInputTypeGood.cs" />
40+
3041
</example>
3142
<references>
3243

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

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,46 @@ import csharp
1515
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery
1616
import DataFlow::PathGraph
1717

18-
from TaintTrackingConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
19-
where config.hasFlowPath(source, sink)
20-
select sink.getNode(), source, sink, "$@ flows to unsafe deserializer.", source.getNode(),
21-
"User-provided data"
18+
from DataFlow::PathNode userInput, DataFlow::PathNode deserializeCallArg
19+
where
20+
exists(TaintToObjectMethodTrackingConfig taintTracking |
21+
// all flows from user input to deserialization with weak and strong type serializers
22+
taintTracking.hasFlowPath(userInput, deserializeCallArg)
23+
) and
24+
// intersect with strong types, but user controlled or weak types deserialization usages
25+
(
26+
exists(
27+
DataFlow::Node weakTypeCreation, DataFlow::Node weakTypeUsage,
28+
WeakTypeCreationToUsageTrackingConfig weakTypeDeserializerTracking, MethodCall mc
29+
|
30+
weakTypeDeserializerTracking.hasFlow(weakTypeCreation, weakTypeUsage) and
31+
mc.getQualifier() = weakTypeUsage.asExpr() and
32+
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
33+
)
34+
or
35+
exists(
36+
TaintToObjectTypeTrackingConfig userControlledTypeTracking, DataFlow::Node taintedTypeUsage,
37+
DataFlow::Node userInput2, MethodCall mc
38+
|
39+
userControlledTypeTracking.hasFlow(userInput2, taintedTypeUsage) and
40+
mc.getQualifier() = taintedTypeUsage.asExpr() and
41+
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
42+
)
43+
)
44+
or
45+
// no type check needed - straightforward taint -> sink
46+
exists(TaintToConstructorOrStaticMethodTrackingConfig taintTracking2 |
47+
taintTracking2.hasFlowPath(userInput, deserializeCallArg)
48+
)
49+
or
50+
// JsonConvert static method call, but with additional unsafe typename tracking
51+
exists(
52+
JsonConvertTrackingConfig taintTrackingJsonConvert, TypeNameTrackingConfig typenameTracking,
53+
DataFlow::Node settingsCallArg
54+
|
55+
taintTrackingJsonConvert.hasFlowPath(userInput, deserializeCallArg) and
56+
typenameTracking.hasFlow(_, settingsCallArg) and
57+
deserializeCallArg.getNode().asExpr().getParent() = settingsCallArg.asExpr().getParent()
58+
)
59+
select deserializeCallArg, userInput, deserializeCallArg, "$@ flows to unsafe deserializer.",
60+
userInput, "User-provided data"

csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInputGood.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class Good
66
public static object Deserialize(TextBox textBox)
77
{
88
JavaScriptSerializer sr = new JavaScriptSerializer();
9-
// GOOD
9+
// GOOD: no unsafe type resolver
1010
return sr.DeserializeObject(textBox.Text);
1111
}
1212
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Runtime.Serialization.Json;
2+
using System.IO;
3+
using System;
4+
5+
class BadDataContractJsonSerializer
6+
{
7+
public static object Deserialize(string type, Stream s)
8+
{
9+
// BAD: stream and type are potentially untrusted
10+
var ds = new DataContractJsonSerializer(Type.GetType(type));
11+
return ds.ReadObject(s);
12+
}
13+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Runtime.Serialization.Json;
2+
using System.IO;
3+
using System;
4+
5+
class Poco
6+
{
7+
public int Count;
8+
9+
public string Comment;
10+
}
11+
12+
class GoodDataContractJsonSerializer
13+
{
14+
public static Poco Deserialize(Stream s)
15+
{
16+
// GOOD: while stream is potentially untrusted, the instantiated type is hardcoded
17+
var ds = new DataContractJsonSerializer(typeof(Poco));
18+
return (Poco)ds.ReadObject(s);
19+
}
20+
}

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") }

0 commit comments

Comments
 (0)