Skip to content

Commit ac29184

Browse files
author
edvraa
committed
deserialization sinks
1 parent 07ca09e commit ac29184

35 files changed

+1755
-75
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import semmle.code.csharp.serialization.Deserializers
1616

1717
from Call deserialization, Cast cast
1818
where
19-
deserialization.getTarget() instanceof UnsafeDeserializer and
19+
deserialization.getTarget() instanceof UnsafeDeserializerCallable and
2020
cast.getExpr() = deserialization and
2121
cast.getTargetType() instanceof SystemLinqExpressions::DelegateExtType
2222
select deserialization, "Deserialization of delegate type."

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,17 @@
1313
import csharp
1414
import semmle.code.csharp.security.dataflow.UnsafeDeserialization::UnsafeDeserialization
1515

16-
from Call deserializeCall, Sink sink
17-
where deserializeCall.getAnArgument() = sink.asExpr()
16+
from Call deserializeCall, ObjectMethodSink sink
17+
where
18+
deserializeCall.getAnArgument() = sink.asExpr() and
19+
not exists(
20+
DataFlow::PathNode constructor, DataFlow::PathNode usage,
21+
SafeConstructorTrackingConfig constructorTracking
22+
|
23+
constructorTracking.hasFlowPath(constructor, usage) and
24+
usage.getNode().asExpr().getParent() = sink.asExpr().getParent()
25+
)
26+
or
27+
exists(ConstructorOrStaticMethodSink sink2 | deserializeCall.getAnArgument() = sink2.asExpr())
1828
select deserializeCall,
1929
"Unsafe deserializer is used. Make sure the value being deserialized comes from a trusted source."

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: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,54 @@
1313
import csharp
1414
import semmle.code.csharp.security.dataflow.UnsafeDeserialization::UnsafeDeserialization
1515
import DataFlow::PathGraph
16+
import semmle.code.csharp.security.dataflow.flowsources.Remote
17+
import semmle.code.csharp.security.dataflow.flowsources.Local
1618

17-
from TaintTrackingConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
18-
where config.hasFlowPath(source, sink)
19-
select sink.getNode(), source, sink, "$@ flows to unsafe deserializer.", source.getNode(),
19+
class RemoteSource extends Source {
20+
RemoteSource() { this instanceof RemoteFlowSource }
21+
}
22+
23+
class LocalSource extends Source {
24+
LocalSource() { this instanceof LocalFlowSource }
25+
}
26+
27+
from
28+
TaintToObjectMethodTrackingConfig taintTracking, DataFlow::PathNode userInput,
29+
DataFlow::PathNode deserializeCall
30+
where
31+
// all flows from user input to deserialization with weak and strong type serializers
32+
taintTracking.hasFlowPath(userInput, deserializeCall) and
33+
// intersect with strong types, but user controlled or weak types deserialization usages
34+
(
35+
exists(
36+
DataFlow::PathNode weakTypeCreation, DataFlow::PathNode weakTypeUsage,
37+
WeakTypeCreationToUsageTrackingConfig weakTypeDeserializerTracking
38+
|
39+
weakTypeDeserializerTracking.hasFlowPath(weakTypeCreation, weakTypeUsage) and
40+
weakTypeUsage.getNode().asExpr().getParent() = deserializeCall.getNode().asExpr().getParent()
41+
)
42+
or
43+
exists(
44+
TaintToObjectTypeTrackingConfig userControlledTypeTracking,
45+
DataFlow::PathNode taintedTypeUsage, DataFlow::PathNode userInput2
46+
|
47+
userControlledTypeTracking.hasFlowPath(userInput2, taintedTypeUsage) and
48+
taintedTypeUsage.getNode().asExpr().getParent() =
49+
deserializeCall.getNode().asExpr().getParent()
50+
)
51+
) and
52+
// exclude deserialization flows with safe instances (i.e. JavaScriptSerializer without resolver)
53+
not exists(
54+
SafeConstructorTrackingConfig safeConstructorTracking, DataFlow::PathNode safeCreation,
55+
DataFlow::PathNode safeTypeUsage
56+
|
57+
safeConstructorTracking.hasFlowPath(safeCreation, safeTypeUsage) and
58+
safeTypeUsage.getNode().asExpr().getParent() = deserializeCall.getNode().asExpr().getParent()
59+
)
60+
or
61+
// no type check needed - straightforward taint -> sink
62+
exists(TaintToConstructorOrStaticMethodTrackingConfig taintTracking2 |
63+
taintTracking2.hasFlowPath(userInput, deserializeCall)
64+
)
65+
select deserializeCall, userInput, deserializeCall, "$@ flows to unsafe deserializer.", userInput,
2066
"User-provided data"
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+
}

0 commit comments

Comments
 (0)