Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 2134757

Browse files
authored
Merge pull request #261 from smowton/smowton/admin/cleanup-cwe-322
Polish CWE-322: detect and exclude cases where host-checking is optional
2 parents 2831ffd + d7c0671 commit 2134757

16 files changed

+225
-96
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Query "Use of insecure HostKeyCallback implementation" (`go/insecure-hostkeycallback`) is promoted from experimental status. This checks for insecurely omitting SSH host-key verification.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* @name Use of insecure HostKeyCallback implementation
3+
* @description Detects insecure SSL client configurations with an implementation of the `HostKeyCallback` that accepts all host keys.
4+
* @kind path-problem
5+
* @problem.severity warning
6+
* @precision high
7+
* @id go/insecure-hostkeycallback
8+
* @tags security
9+
*/
10+
11+
import go
12+
import DataFlow::PathGraph
13+
14+
/** The `ssh.InsecureIgnoreHostKey` function, which allows connecting to any host regardless of its host key. */
15+
class InsecureIgnoreHostKey extends Function {
16+
InsecureIgnoreHostKey() {
17+
this.hasQualifiedName("golang.org/x/crypto/ssh", "InsecureIgnoreHostKey")
18+
}
19+
}
20+
21+
/** An SSH host-key checking function. */
22+
class HostKeyCallbackFunc extends DataFlow::Node {
23+
HostKeyCallbackFunc() {
24+
exists(NamedType nt | nt.hasQualifiedName("golang.org/x/crypto/ssh", "HostKeyCallback") |
25+
getType().getUnderlyingType() = nt.getUnderlyingType()
26+
) and
27+
// Restrict possible sources to either function definitions or
28+
// the result of some external function call (e.g. InsecureIgnoreHostKey())
29+
// Otherwise we also consider typecasts, variables and other such intermediates
30+
// as sources.
31+
(
32+
this instanceof DataFlow::FunctionNode
33+
or
34+
exists(DataFlow::CallNode call | not exists(call.getACallee().getBody()) |
35+
this = call.getAResult()
36+
)
37+
)
38+
}
39+
}
40+
41+
/** A callback function value that is insecure when used as a `HostKeyCallback`, because it always returns `nil`. */
42+
class InsecureHostKeyCallbackFunc extends HostKeyCallbackFunc {
43+
InsecureHostKeyCallbackFunc() {
44+
// Either a call to InsecureIgnoreHostKey(), which we know returns an insecure callback.
45+
this = any(InsecureIgnoreHostKey f).getACall().getAResult()
46+
or
47+
// Or a callback function in the source code (named or anonymous) that always returns nil.
48+
forex(DataFlow::ResultNode returnValue |
49+
returnValue = this.(DataFlow::FunctionNode).getAResult()
50+
|
51+
returnValue = Builtin::nil().getARead()
52+
)
53+
}
54+
}
55+
56+
/**
57+
* A data-flow configuration for identifying `HostKeyCallbackFunc` instances that reach `ClientConfig.HostKeyCallback` fields.
58+
*/
59+
class HostKeyCallbackAssignmentConfig extends DataFlow::Configuration {
60+
HostKeyCallbackAssignmentConfig() { this = "HostKeyCallbackAssignmentConfig" }
61+
62+
override predicate isSource(DataFlow::Node source) { source instanceof HostKeyCallbackFunc }
63+
64+
/**
65+
* Holds if `sink` is a value written by `write` to a field `ClientConfig.HostKeyCallback`.
66+
*/
67+
predicate isSink(DataFlow::Node sink, Write write) {
68+
exists(Field f |
69+
f.hasQualifiedName("golang.org/x/crypto/ssh", "ClientConfig", "HostKeyCallback") and
70+
write.writesField(_, f, sink)
71+
)
72+
}
73+
74+
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
75+
}
76+
77+
/**
78+
* Holds if a secure host-check function reaches `sink` or another similar sink.
79+
*
80+
* A sink is considered similar if it writes to the same variable and field.
81+
*/
82+
predicate hostCheckReachesSink(DataFlow::PathNode sink) {
83+
exists(HostKeyCallbackAssignmentConfig config, DataFlow::PathNode source |
84+
not source.getNode() instanceof InsecureHostKeyCallbackFunc and
85+
(
86+
config.hasFlowPath(source, sink)
87+
or
88+
exists(
89+
DataFlow::PathNode otherSink, Write sinkWrite, Write otherSinkWrite,
90+
SsaWithFields sinkAccessPath, SsaWithFields otherSinkAccessPath
91+
|
92+
config.hasFlowPath(source, otherSink) and
93+
config.isSink(sink.getNode(), sinkWrite) and
94+
config.isSink(otherSink.getNode(), otherSinkWrite) and
95+
sinkWrite.writesField(sinkAccessPath.getAUse(), _, sink.getNode()) and
96+
otherSinkWrite.writesField(otherSinkAccessPath.getAUse(), _, otherSink.getNode()) and
97+
otherSinkAccessPath = sinkAccessPath.similar()
98+
)
99+
)
100+
)
101+
}
102+
103+
from HostKeyCallbackAssignmentConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
104+
where
105+
config.hasFlowPath(source, sink) and
106+
source.getNode() instanceof InsecureHostKeyCallbackFunc and
107+
// Exclude cases where a good access-path function reaches the same or a similar sink
108+
// (these probably indicate optional host-checking)
109+
not hostCheckReachesSink(sink)
110+
select sink, source, sink,
111+
"Configuring SSH ClientConfig with insecure HostKeyCallback implementation from $@.",
112+
source.getNode(), "this source"

ql/src/experimental/CWE-322/InsecureHostKeyCallback.ql

Lines changed: 0 additions & 53 deletions
This file was deleted.

ql/test/experimental/CWE-322/InsecureHostKeyCallback.expected

Lines changed: 0 additions & 34 deletions
This file was deleted.

ql/test/experimental/CWE-322/InsecureHostKeyCallback.qlref

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
edges
2+
| InsecureHostKeyCallbackExample.go:16:4:18:4 | function literal : signature type | InsecureHostKeyCallbackExample.go:15:20:18:5 | type conversion |
3+
| InsecureHostKeyCallbackExample.go:31:14:34:4 | type conversion : signature type | InsecureHostKeyCallbackExample.go:39:20:39:27 | callback |
4+
| InsecureHostKeyCallbackExample.go:32:3:34:3 | function literal : signature type | InsecureHostKeyCallbackExample.go:31:14:34:4 | type conversion : signature type |
5+
| InsecureHostKeyCallbackExample.go:45:3:47:3 | function literal : signature type | InsecureHostKeyCallbackExample.go:52:20:52:48 | type conversion |
6+
| InsecureHostKeyCallbackExample.go:58:39:58:46 | definition of callback : HostKeyCallback | InsecureHostKeyCallbackExample.go:62:20:62:27 | callback |
7+
| InsecureHostKeyCallbackExample.go:58:39:58:46 | definition of callback : signature type | InsecureHostKeyCallbackExample.go:62:20:62:27 | callback |
8+
| InsecureHostKeyCallbackExample.go:68:48:68:55 | definition of callback : signature type | InsecureHostKeyCallbackExample.go:78:28:78:35 | callback |
9+
| InsecureHostKeyCallbackExample.go:94:3:94:45 | ... := ...[0] : HostKeyCallback | InsecureHostKeyCallbackExample.go:95:28:95:35 | callback |
10+
| InsecureHostKeyCallbackExample.go:102:22:105:4 | type conversion : signature type | InsecureHostKeyCallbackExample.go:107:35:107:50 | insecureCallback : signature type |
11+
| InsecureHostKeyCallbackExample.go:103:3:105:3 | function literal : signature type | InsecureHostKeyCallbackExample.go:102:22:105:4 | type conversion : signature type |
12+
| InsecureHostKeyCallbackExample.go:107:35:107:50 | insecureCallback : signature type | InsecureHostKeyCallbackExample.go:58:39:58:46 | definition of callback : signature type |
13+
| InsecureHostKeyCallbackExample.go:109:31:115:4 | type conversion : signature type | InsecureHostKeyCallbackExample.go:117:35:117:59 | potentiallySecureCallback : signature type |
14+
| InsecureHostKeyCallbackExample.go:109:31:115:4 | type conversion : signature type | InsecureHostKeyCallbackExample.go:120:44:120:68 | potentiallySecureCallback : signature type |
15+
| InsecureHostKeyCallbackExample.go:110:3:115:3 | function literal : signature type | InsecureHostKeyCallbackExample.go:109:31:115:4 | type conversion : signature type |
16+
| InsecureHostKeyCallbackExample.go:117:35:117:59 | potentiallySecureCallback : signature type | InsecureHostKeyCallbackExample.go:58:39:58:46 | definition of callback : signature type |
17+
| InsecureHostKeyCallbackExample.go:118:35:118:61 | call to InsecureIgnoreHostKey : HostKeyCallback | InsecureHostKeyCallbackExample.go:58:39:58:46 | definition of callback : HostKeyCallback |
18+
| InsecureHostKeyCallbackExample.go:120:44:120:68 | potentiallySecureCallback : signature type | InsecureHostKeyCallbackExample.go:68:48:68:55 | definition of callback : signature type |
19+
nodes
20+
| InsecureHostKeyCallbackExample.go:15:20:18:5 | type conversion | semmle.label | type conversion |
21+
| InsecureHostKeyCallbackExample.go:16:4:18:4 | function literal : signature type | semmle.label | function literal : signature type |
22+
| InsecureHostKeyCallbackExample.go:26:20:26:46 | call to InsecureIgnoreHostKey | semmle.label | call to InsecureIgnoreHostKey |
23+
| InsecureHostKeyCallbackExample.go:31:14:34:4 | type conversion : signature type | semmle.label | type conversion : signature type |
24+
| InsecureHostKeyCallbackExample.go:32:3:34:3 | function literal : signature type | semmle.label | function literal : signature type |
25+
| InsecureHostKeyCallbackExample.go:39:20:39:27 | callback | semmle.label | callback |
26+
| InsecureHostKeyCallbackExample.go:45:3:47:3 | function literal : signature type | semmle.label | function literal : signature type |
27+
| InsecureHostKeyCallbackExample.go:52:20:52:48 | type conversion | semmle.label | type conversion |
28+
| InsecureHostKeyCallbackExample.go:58:39:58:46 | definition of callback : HostKeyCallback | semmle.label | definition of callback : HostKeyCallback |
29+
| InsecureHostKeyCallbackExample.go:58:39:58:46 | definition of callback : signature type | semmle.label | definition of callback : signature type |
30+
| InsecureHostKeyCallbackExample.go:62:20:62:27 | callback | semmle.label | callback |
31+
| InsecureHostKeyCallbackExample.go:68:48:68:55 | definition of callback : signature type | semmle.label | definition of callback : signature type |
32+
| InsecureHostKeyCallbackExample.go:76:28:76:54 | call to InsecureIgnoreHostKey | semmle.label | call to InsecureIgnoreHostKey |
33+
| InsecureHostKeyCallbackExample.go:78:28:78:35 | callback | semmle.label | callback |
34+
| InsecureHostKeyCallbackExample.go:92:28:92:54 | call to InsecureIgnoreHostKey | semmle.label | call to InsecureIgnoreHostKey |
35+
| InsecureHostKeyCallbackExample.go:94:3:94:45 | ... := ...[0] : HostKeyCallback | semmle.label | ... := ...[0] : HostKeyCallback |
36+
| InsecureHostKeyCallbackExample.go:95:28:95:35 | callback | semmle.label | callback |
37+
| InsecureHostKeyCallbackExample.go:102:22:105:4 | type conversion : signature type | semmle.label | type conversion : signature type |
38+
| InsecureHostKeyCallbackExample.go:103:3:105:3 | function literal : signature type | semmle.label | function literal : signature type |
39+
| InsecureHostKeyCallbackExample.go:107:35:107:50 | insecureCallback : signature type | semmle.label | insecureCallback : signature type |
40+
| InsecureHostKeyCallbackExample.go:109:31:115:4 | type conversion : signature type | semmle.label | type conversion : signature type |
41+
| InsecureHostKeyCallbackExample.go:110:3:115:3 | function literal : signature type | semmle.label | function literal : signature type |
42+
| InsecureHostKeyCallbackExample.go:117:35:117:59 | potentiallySecureCallback : signature type | semmle.label | potentiallySecureCallback : signature type |
43+
| InsecureHostKeyCallbackExample.go:118:35:118:61 | call to InsecureIgnoreHostKey : HostKeyCallback | semmle.label | call to InsecureIgnoreHostKey : HostKeyCallback |
44+
| InsecureHostKeyCallbackExample.go:120:44:120:68 | potentiallySecureCallback : signature type | semmle.label | potentiallySecureCallback : signature type |
45+
#select
46+
| InsecureHostKeyCallbackExample.go:15:20:18:5 | type conversion | InsecureHostKeyCallbackExample.go:16:4:18:4 | function literal : signature type | InsecureHostKeyCallbackExample.go:15:20:18:5 | type conversion | Configuring SSH ClientConfig with insecure HostKeyCallback implementation from $@. | InsecureHostKeyCallbackExample.go:16:4:18:4 | function literal | this source |
47+
| InsecureHostKeyCallbackExample.go:26:20:26:46 | call to InsecureIgnoreHostKey | InsecureHostKeyCallbackExample.go:26:20:26:46 | call to InsecureIgnoreHostKey | InsecureHostKeyCallbackExample.go:26:20:26:46 | call to InsecureIgnoreHostKey | Configuring SSH ClientConfig with insecure HostKeyCallback implementation from $@. | InsecureHostKeyCallbackExample.go:26:20:26:46 | call to InsecureIgnoreHostKey | this source |
48+
| InsecureHostKeyCallbackExample.go:39:20:39:27 | callback | InsecureHostKeyCallbackExample.go:32:3:34:3 | function literal : signature type | InsecureHostKeyCallbackExample.go:39:20:39:27 | callback | Configuring SSH ClientConfig with insecure HostKeyCallback implementation from $@. | InsecureHostKeyCallbackExample.go:32:3:34:3 | function literal | this source |
49+
| InsecureHostKeyCallbackExample.go:52:20:52:48 | type conversion | InsecureHostKeyCallbackExample.go:45:3:47:3 | function literal : signature type | InsecureHostKeyCallbackExample.go:52:20:52:48 | type conversion | Configuring SSH ClientConfig with insecure HostKeyCallback implementation from $@. | InsecureHostKeyCallbackExample.go:45:3:47:3 | function literal | this source |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE-322/InsecureHostKeyCallback.ql

0 commit comments

Comments
 (0)