Skip to content

Commit c1f6a55

Browse files
committed
C#: Turn the insecure certificate validation query into a data flow query.
1 parent e67e8a9 commit c1f6a55

File tree

2 files changed

+162
-88
lines changed

2 files changed

+162
-88
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @name Unsafe `CertificateValidationCallback` use.
33
* @description Using a `CertificateValidationCallback` that always returns `true` is insecure, as it allows any certificate to be accepted as valid.
4-
* @kind alert
4+
* @kind path-problem
55
* @problem.severity error
66
* @precision high
77
* @id cs/unsafe-certificate-validation
@@ -11,90 +11,12 @@
1111
*/
1212

1313
import csharp
14-
15-
/**
16-
* Provides an abstract base class for properties related to certificate validation.
17-
*/
18-
abstract private class CertificateValidationProperty extends Property { }
19-
20-
/**
21-
* Represents the `ServerCertificateValidationCallback` property of the `ServicePointManager` class.
22-
*/
23-
private class ServicePointManagerServerCertificateValidationCallback extends CertificateValidationProperty
24-
{
25-
ServicePointManagerServerCertificateValidationCallback() {
26-
this.getDeclaringType().hasFullyQualifiedName("System.Net", "ServicePointManager") and
27-
this.hasName("ServerCertificateValidationCallback")
28-
}
29-
}
30-
31-
/**
32-
* Represents the `ServerCertificateCustomValidationCallback` property of the `HttpClientHandler` class.
33-
*/
34-
private class HttpClientHandlerServerCertificateCustomValidationCallback extends CertificateValidationProperty
35-
{
36-
HttpClientHandlerServerCertificateCustomValidationCallback() {
37-
this.getDeclaringType().hasFullyQualifiedName("System.Net.Http", "HttpClientHandler") and
38-
this.hasName("ServerCertificateCustomValidationCallback")
39-
}
40-
}
41-
42-
/**
43-
* Represents the creation of an `SslStream` object.
44-
*/
45-
private class SslStreamCreation extends ObjectCreation {
46-
SslStreamCreation() {
47-
this.getObjectType().hasFullyQualifiedName("System.Net.Security", "SslStream")
48-
}
49-
50-
/**
51-
* Gets the expression used as the server certificate validation callback argument
52-
* in the creation of the `SslStream` object.
53-
*/
54-
Expr getServerCertificateValidationCallback() { result = this.getArgument(2) }
55-
}
56-
57-
/**
58-
* Represents the `ServerCertificateValidationCallback` property of the `HttpWebRequest` class.
59-
*/
60-
private class HttpWebRequestServerCertificateValidationCallback extends CertificateValidationProperty
61-
{
62-
HttpWebRequestServerCertificateValidationCallback() {
63-
this.getDeclaringType().hasFullyQualifiedName("System.Net", "HttpWebRequest") and
64-
this.hasName("ServerCertificateValidationCallback")
65-
}
66-
}
67-
68-
/**
69-
* Holds if `c` always returns `true`.
70-
*/
71-
private predicate alwaysReturnsTrue(Callable c) {
72-
forex(ReturnStmt rs | rs.getEnclosingCallable() = c |
73-
rs.getExpr().(BoolLiteral).getBoolValue() = true
74-
)
75-
or
76-
c.getBody().(BoolLiteral).getBoolValue() = true
77-
}
78-
79-
/**
80-
* Gets the actual callable object referred to by expression `e`.
81-
* This can be a direct reference to a callable, a delegate creation, or an anonymous function.
82-
*/
83-
Callable getActualCallable(Expr e) {
84-
exists(Expr dcArg | dcArg = e.(DelegateCreation).getArgument() |
85-
result = dcArg.(CallableAccess).getTarget() or result = dcArg.(AnonymousFunctionExpr)
86-
)
87-
or
88-
result = e.(Callable)
89-
}
90-
91-
from Expr e, Callable c
92-
where
93-
[
94-
any(SslStreamCreation yy).getServerCertificateValidationCallback(),
95-
any(CertificateValidationProperty xx).getAnAssignedValue()
96-
] = e and
97-
getActualCallable(e) = c and
98-
alwaysReturnsTrue(c)
99-
select e, "$@ that is defined $@ and accepts any certificate as valid, is used here.", e,
100-
"This certificate callback", c, "here"
14+
import DataFlow
15+
import InsecureCertificateValidationQuery
16+
import InsecureCertificateValidation::PathGraph
17+
18+
from InsecureCertificateValidation::PathNode source, InsecureCertificateValidation::PathNode sink
19+
where InsecureCertificateValidation::flowPath(source, sink)
20+
select sink.getNode(), source, sink,
21+
"$@ that is defined $@ and accepts any certificate as valid, is used here.", sink,
22+
"This certificate callback", source, "here"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* Provides a taint-tracking configuration for reasoning
3+
* about insecure certificate validation vulnerabilities.
4+
*/
5+
6+
private import csharp
7+
private import DataFlow
8+
private import semmle.code.csharp.controlflow.Guards
9+
10+
/**
11+
* A source specific to unsafe certificate validation vulnerabilities.
12+
*/
13+
abstract private class Source extends DataFlow::Node { }
14+
15+
/**
16+
* A sink specific to unsafe certificate validation vulnerabilities.
17+
*/
18+
abstract private class Sink extends DataFlow::Node { }
19+
20+
/**
21+
* A sanitizer specific to unsafe certificate validation vulnerabilities.
22+
*/
23+
abstract private class Sanitizer extends DataFlow::ExprNode { }
24+
25+
/**
26+
* Provides an abstract base class for properties related to certificate validation.
27+
*/
28+
abstract private class CertificateValidationProperty extends Property { }
29+
30+
/**
31+
* Represents the `ServerCertificateValidationCallback` property of the `ServicePointManager` class.
32+
*/
33+
private class ServicePointManagerServerCertificateValidationCallback extends CertificateValidationProperty
34+
{
35+
ServicePointManagerServerCertificateValidationCallback() {
36+
this.getDeclaringType().hasFullyQualifiedName("System.Net", "ServicePointManager") and
37+
this.hasName("ServerCertificateValidationCallback")
38+
}
39+
}
40+
41+
/**
42+
* Represents the `ServerCertificateCustomValidationCallback` property of the `HttpClientHandler` class.
43+
*/
44+
private class HttpClientHandlerServerCertificateCustomValidationCallback extends CertificateValidationProperty
45+
{
46+
HttpClientHandlerServerCertificateCustomValidationCallback() {
47+
this.getDeclaringType().hasFullyQualifiedName("System.Net.Http", "HttpClientHandler") and
48+
this.hasName("ServerCertificateCustomValidationCallback")
49+
}
50+
}
51+
52+
/**
53+
* Represents the `ServerCertificateValidationCallback` property of the `HttpWebRequest` class.
54+
*/
55+
private class HttpWebRequestServerCertificateValidationCallback extends CertificateValidationProperty
56+
{
57+
HttpWebRequestServerCertificateValidationCallback() {
58+
this.getDeclaringType().hasFullyQualifiedName("System.Net", "HttpWebRequest") and
59+
this.hasName("ServerCertificateValidationCallback")
60+
}
61+
}
62+
63+
/**
64+
* Represents the creation of an `SslStream` object.
65+
*/
66+
private class SslStreamCreation extends ObjectCreation {
67+
SslStreamCreation() {
68+
this.getObjectType().hasFullyQualifiedName("System.Net.Security", "SslStream")
69+
}
70+
71+
/**
72+
* Gets the expression used as the server certificate validation callback argument
73+
* in the creation of the `SslStream` object.
74+
*/
75+
Expr getServerCertificateValidationCallback() { result = this.getArgument(2) }
76+
}
77+
78+
/**
79+
* Holds if `c` always returns `true`.
80+
*/
81+
private predicate alwaysReturnsTrue(Callable c) {
82+
forex(ReturnStmt rs | rs.getEnclosingCallable() = c |
83+
rs.getExpr().(BoolLiteral).getBoolValue() = true
84+
)
85+
or
86+
c.getBody().(BoolLiteral).getBoolValue() = true
87+
}
88+
89+
/**
90+
* Gets the actual callable object referred to by expression `e`.
91+
* This can be a direct reference to a callable, a delegate creation, or an anonymous function.
92+
*/
93+
private Callable getActualCallable(Expr e) {
94+
exists(Expr dcArg | dcArg = e.(DelegateCreation).getArgument() |
95+
result = dcArg.(CallableAccess).getTarget() or
96+
result = dcArg.(AnonymousFunctionExpr)
97+
)
98+
or
99+
result = e
100+
}
101+
102+
private predicate ignoreCertificateValidation(Guard guard, AbstractValue v) {
103+
guard =
104+
any(PropertyAccess access |
105+
access.getProperty().hasFullyQualifiedName("", "Settings", "IgnoreCertificateValidation") and
106+
v.(AbstractValues::BooleanValue).getValue() = true
107+
)
108+
}
109+
110+
private class AddIgnoreCheck extends Sanitizer {
111+
AddIgnoreCheck() {
112+
exists(Guard g, AbstractValues::BooleanValue v | ignoreCertificateValidation(g, v) |
113+
g.controlsNode(this.getControlFlowNode(), v)
114+
)
115+
}
116+
}
117+
118+
private class CallableAlwaysReturnsTrue extends Callable {
119+
CallableAlwaysReturnsTrue() { alwaysReturnsTrue(this) }
120+
}
121+
122+
private class AddCallableAlwaysReturnsTrue extends Source {
123+
AddCallableAlwaysReturnsTrue() {
124+
getActualCallable(this.asExpr()) instanceof CallableAlwaysReturnsTrue
125+
}
126+
}
127+
128+
private class AddSinks extends Sink {
129+
AddSinks() {
130+
exists(Expr e | e = this.asExpr() |
131+
e =
132+
[
133+
any(CertificateValidationProperty p).getAnAssignedValue(),
134+
any(SslStreamCreation yy).getServerCertificateValidationCallback()
135+
] and
136+
not e.getFile().getAbsolutePath().matches("example")
137+
)
138+
}
139+
}
140+
141+
private module InsecureCertificateValidationConfig implements DataFlow::ConfigSig {
142+
predicate isSource(DataFlow::Node node) { node instanceof Source }
143+
144+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
145+
146+
predicate isSink(DataFlow::Node node) { node instanceof Sink }
147+
}
148+
149+
/**
150+
* A taint-tracking module for insecure certificate validation vulnerabilities.
151+
*/
152+
module InsecureCertificateValidation = TaintTracking::Global<InsecureCertificateValidationConfig>;

0 commit comments

Comments
 (0)