-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathUnsafeHostnameVerification.ql
More file actions
119 lines (105 loc) · 4.26 KB
/
UnsafeHostnameVerification.ql
File metadata and controls
119 lines (105 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
* @name Disabled hostname verification
* @description Marking a certificate as valid for a host without checking the certificate hostname allows an attacker to perform a machine-in-the-middle attack.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/insecure-hostname-verifier
* @tags security
* external/cwe/cwe-297
*/
import java
import semmle.code.java.controlflow.Guards
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.Encryption
import DataFlow::PathGraph
/**
* Holds if `m` always returns `true` ignoring any exceptional flow.
*/
private predicate alwaysReturnsTrue(HostnameVerifierVerify m) {
forex(ReturnStmt rs | rs.getEnclosingCallable() = m |
rs.getResult().(CompileTimeConstantExpr).getBooleanValue() = true
)
}
/**
* A class that overrides the `javax.net.ssl.HostnameVerifier.verify` method and **always** returns `true` (though it could also exit due to an uncaught exception), thus
* accepting any certificate despite a hostname mismatch.
*/
class TrustAllHostnameVerifier extends RefType {
TrustAllHostnameVerifier() {
this.getASupertype*() instanceof HostnameVerifier and
exists(HostnameVerifierVerify m |
m.getDeclaringType() = this and
alwaysReturnsTrue(m)
)
}
}
/**
* A configuration to model the flow of a `TrustAllHostnameVerifier` to a `set(Default)HostnameVerifier` call.
*/
class TrustAllHostnameVerifierConfiguration extends DataFlow::Configuration {
TrustAllHostnameVerifierConfiguration() { this = "TrustAllHostnameVerifierConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof TrustAllHostnameVerifier
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, Method m |
(m instanceof SetDefaultHostnameVerifierMethod or m instanceof SetHostnameVerifierMethod) and
ma.getMethod() = m
|
ma.getArgument(0) = sink.asExpr()
)
}
}
bindingset[result]
private string getAFlagName() {
result.regexpMatch("(?i).*(secure|(en|dis)able|selfCert|selfSign|validat|verif|trust|ignore).*")
}
/** A configuration to model the flow of feature flags into `Guard`s. This is used to determine whether something is guarded by such a flag. */
private class FlagTaintTracking extends TaintTracking2::Configuration {
FlagTaintTracking() { this = "FlagTaintTracking" }
override predicate isSource(DataFlow::Node source) {
exists(VarAccess v | v.getVariable().getName() = getAFlagName() | source.asExpr() = v)
or
exists(StringLiteral s | s.getRepresentedString() = getAFlagName() | source.asExpr() = s)
or
exists(MethodAccess ma | ma.getMethod().getName() = getAFlagName() | source.asExpr() = ma)
}
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof Guard }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma | ma.getMethod() = any(EnvTaintedMethod m) |
ma = node2.asExpr() and ma.getAnArgument() = node1.asExpr()
)
or
exists(MethodAccess ma |
ma.getMethod().hasName("parseBoolean") and
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "Boolean")
|
ma = node2.asExpr() and ma.getAnArgument() = node1.asExpr()
)
}
}
/** Gets a guard that depends on a flag. */
private Guard getAGuard() {
exists(FlagTaintTracking cfg, DataFlow::Node source, DataFlow::Node sink |
cfg.hasFlow(source, sink)
|
sink.asExpr() = result
)
}
/** Holds if `node` is guarded by a flag that suggests an intentionally insecure feature. */
private predicate isNodeGuardedByFlag(DataFlow::Node node) {
exists(Guard g | g.controls(node.asExpr().getBasicBlock(), _) | g = getAGuard())
}
from
DataFlow::PathNode source, DataFlow::PathNode sink, TrustAllHostnameVerifierConfiguration cfg,
RefType verifier
where
cfg.hasFlowPath(source, sink) and
not isNodeGuardedByFlag(sink.getNode()) and
verifier = source.getNode().asExpr().(ClassInstanceExpr).getConstructedType()
select sink, source, sink,
"$@ that is defined $@ and accepts any certificate as valid, is used here.", source,
"This hostname verifier", verifier, "here"