Skip to content

Commit 34cc938

Browse files
committed
Python: Adjust InsecureProtocol query.
1 parent 5469a82 commit 34cc938

File tree

5 files changed

+141
-71
lines changed

5 files changed

+141
-71
lines changed

python/ql/src/Security/CWE-327/FluentApiModel.qll

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,48 +20,100 @@ import TlsLibraryModel
2020
* Since we really want "the last unrestriction, not nullified by a restriction",
2121
* we also disallow flow into restrictions.
2222
*/
23-
class InsecureContextConfiguration extends DataFlow::Configuration {
24-
TlsLibrary library;
25-
ProtocolVersion tracked_version;
23+
module InsecureContextConfiguration2 implements DataFlow::StateConfigSig {
24+
private newtype TFlowState =
25+
TMkFlowState(TlsLibrary library, int bits) {
26+
bits in [0 .. max(any(ProtocolVersion v).getBit()) * 2 - 1]
27+
}
2628

27-
InsecureContextConfiguration() {
28-
this = library + "Allows" + tracked_version and
29-
tracked_version.isInsecure()
30-
}
29+
class FlowState extends TFlowState {
30+
int getBits() { this = TMkFlowState(_, result) }
3131

32-
ProtocolVersion getTrackedVersion() { result = tracked_version }
32+
TlsLibrary getLibrary() { this = TMkFlowState(result, _) }
3333

34-
override predicate isSource(DataFlow::Node source) { this.isUnrestriction(source) }
34+
predicate allowsInsecureVersion(ProtocolVersion v) {
35+
v.isInsecure() and this.getBits().bitAnd(v.getBit()) != 0
36+
}
3537

36-
override predicate isSink(DataFlow::Node sink) {
37-
sink = library.connection_creation().getContext()
38+
string toString() {
39+
result =
40+
"FlowState(" + this.getLibrary().toString() + ", " +
41+
concat(ProtocolVersion v | this.allowsInsecureVersion(v) | v, ", ") + ")"
42+
}
3843
}
3944

40-
override predicate isBarrierIn(DataFlow::Node node) {
41-
this.isRestriction(node)
45+
private predicate relevantState(FlowState state) {
46+
isSource(_, state)
4247
or
43-
this.isUnrestriction(node)
48+
exists(FlowState state0 | relevantState(state0) |
49+
exists(ProtocolRestriction r |
50+
r = state0.getLibrary().protocol_restriction() and
51+
state.getBits() = state0.getBits().bitAnd(sum(r.getRestriction().getBit()).bitNot()) and
52+
state0.getLibrary() = state.getLibrary()
53+
)
54+
or
55+
exists(ProtocolUnrestriction pu |
56+
pu = state0.getLibrary().protocol_unrestriction() and
57+
state.getBits() = state0.getBits().bitOr(sum(pu.getUnrestriction().getBit())) and
58+
state0.getLibrary() = state.getLibrary()
59+
)
60+
)
61+
}
62+
63+
predicate isSource(DataFlow::Node source, FlowState state) {
64+
exists(ProtocolFamily family |
65+
source = state.getLibrary().unspecific_context_creation(family) and
66+
state.getBits() = family.getBits()
67+
)
68+
}
69+
70+
predicate isSink(DataFlow::Node sink, FlowState state) {
71+
sink = state.getLibrary().connection_creation().getContext() and
72+
state.allowsInsecureVersion(_)
4473
}
4574

46-
private predicate isRestriction(DataFlow::Node node) {
47-
exists(ProtocolRestriction r |
48-
r = library.protocol_restriction() and
49-
r.getRestriction() = tracked_version
50-
|
51-
node = r.getContext()
75+
predicate isAdditionalFlowStep(
76+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
77+
) {
78+
DataFlow::localFlowStep(node1, node2) and
79+
relevantState(state1) and
80+
(
81+
exists(ProtocolRestriction r |
82+
r = state1.getLibrary().protocol_restriction() and
83+
node2 = r.getContext() and
84+
state2.getBits() = state1.getBits().bitAnd(sum(r.getRestriction().getBit()).bitNot()) and
85+
state1.getLibrary() = state2.getLibrary()
86+
)
87+
or
88+
exists(ProtocolUnrestriction pu |
89+
pu = state1.getLibrary().protocol_unrestriction() and
90+
node2 = pu.getContext() and
91+
state2.getBits() = state1.getBits().bitOr(sum(pu.getUnrestriction().getBit())) and
92+
state1.getLibrary() = state2.getLibrary()
93+
)
5294
)
5395
}
5496

55-
private predicate isUnrestriction(DataFlow::Node node) {
56-
exists(ProtocolUnrestriction pu |
57-
pu = library.protocol_unrestriction() and
58-
pu.getUnrestriction() = tracked_version
59-
|
60-
node = pu.getContext()
97+
predicate isBarrier(DataFlow::Node node, FlowState state) {
98+
relevantState(state) and
99+
(
100+
exists(ProtocolRestriction r |
101+
r = state.getLibrary().protocol_restriction() and
102+
node = r.getContext() and
103+
state.allowsInsecureVersion(r.getRestriction())
104+
)
105+
or
106+
exists(ProtocolUnrestriction pu |
107+
pu = state.getLibrary().protocol_unrestriction() and
108+
node = pu.getContext() and
109+
not state.allowsInsecureVersion(pu.getUnrestriction())
110+
)
61111
)
62112
}
63113
}
64114

115+
private module InsecureContextFlow = DataFlow::MakeWithState<InsecureContextConfiguration2>;
116+
65117
/**
66118
* Holds if `conectionCreation` marks the creation of a connection based on the contex
67119
* found at `contextOrigin` and allowing `insecure_version`.
@@ -74,8 +126,11 @@ predicate unsafe_connection_creation_with_context(
74126
boolean specific
75127
) {
76128
// Connection created from a context allowing `insecure_version`.
77-
exists(InsecureContextConfiguration c | c.hasFlow(contextOrigin, connectionCreation) |
78-
insecure_version = c.getTrackedVersion() and
129+
exists(InsecureContextFlow::PathNode src, InsecureContextFlow::PathNode sink |
130+
InsecureContextFlow::hasFlowPath(src, sink) and
131+
src.getNode() = contextOrigin and
132+
sink.getNode() = connectionCreation and
133+
sink.getState().allowsInsecureVersion(insecure_version) and
79134
specific = false
80135
)
81136
or

python/ql/src/Security/CWE-327/PyOpenSSL.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
5252
}
5353

5454
class UnspecificPyOpenSslContextCreation extends PyOpenSslContextCreation, UnspecificContextCreation {
55-
UnspecificPyOpenSslContextCreation() { library instanceof PyOpenSsl }
55+
// UnspecificPyOpenSslContextCreation() { library instanceof PyOpenSsl }
5656
}
5757

5858
class PyOpenSsl extends TlsLibrary {

python/ql/src/Security/CWE-327/Ssl.qll

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -162,23 +162,21 @@ class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction, Data
162162
}
163163

164164
class UnspecificSslContextCreation extends SslContextCreation, UnspecificContextCreation {
165-
UnspecificSslContextCreation() { library instanceof Ssl }
166-
167-
override ProtocolVersion getUnrestriction() {
168-
result = UnspecificContextCreation.super.getUnrestriction() and
169-
// These are turned off by default since Python 3.6
170-
// see https://docs.python.org/3.6/library/ssl.html#ssl.SSLContext
171-
not result in ["SSLv2", "SSLv3"]
172-
}
165+
// UnspecificSslContextCreation() { library instanceof Ssl }
166+
// override ProtocolVersion getUnrestriction() {
167+
// result = UnspecificContextCreation.super.getUnrestriction() and
168+
// // These are turned off by default since Python 3.6
169+
// // see https://docs.python.org/3.6/library/ssl.html#ssl.SSLContext
170+
// not result in ["SSLv2", "SSLv3"]
171+
// }
173172
}
174173

175-
class UnspecificSslDefaultContextCreation extends SslDefaultContextCreation, ProtocolUnrestriction {
176-
override DataFlow::Node getContext() { result = this }
177-
178-
// see https://docs.python.org/3/library/ssl.html#ssl.create_default_context
179-
override ProtocolVersion getUnrestriction() {
180-
result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
181-
}
174+
class UnspecificSslDefaultContextCreation extends SslDefaultContextCreation {
175+
// override DataFlow::Node getContext() { result = this }
176+
// // see https://docs.python.org/3/library/ssl.html#ssl.create_default_context
177+
// override ProtocolVersion getUnrestriction() {
178+
// result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
179+
// }
182180
}
183181

184182
class Ssl extends TlsLibrary {

python/ql/src/Security/CWE-327/TlsLibraryModel.qll

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,45 @@ class ProtocolVersion extends string {
1515
or
1616
this = "TLSv1" and version = ["TLSv1_1", "TLSv1_2", "TLSv1_3"]
1717
or
18-
this = ["TLSv1", "TLSv1_1"] and version = ["TLSv1_2", "TLSv1_3"]
18+
this = "TLSv1_1" and version = ["TLSv1_2", "TLSv1_3"]
1919
or
20-
this = ["TLSv1", "TLSv1_1", "TLSv1_2"] and version = "TLSv1_3"
20+
this = "TLSv1_2" and version = "TLSv1_3"
2121
}
2222

2323
/** Holds if this protocol version is known to be insecure. */
2424
predicate isInsecure() { this in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1"] }
25+
26+
/** Gets the bit mask for this protocol version. */
27+
int getBit() {
28+
this = "SSLv2" and result = 1
29+
or
30+
this = "SSLv3" and result = 2
31+
or
32+
this = "TLSv1" and result = 4
33+
or
34+
this = "TLSv1_1" and result = 8
35+
or
36+
this = "TLSv1_2" and result = 16
37+
or
38+
this = "TLSv1_3" and result = 32
39+
}
40+
41+
/** Gets the protocol family for this protocol version. */
42+
ProtocolFamily getFamily() {
43+
result = "SSLv23" and this in ["SSLv2", "SSLv3"]
44+
or
45+
result = "TLS" and this in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
46+
}
2547
}
2648

2749
/** An unspecific protocol version */
2850
class ProtocolFamily extends string {
2951
ProtocolFamily() { this in ["SSLv23", "TLS"] }
52+
53+
/** Gets the bit mask for this protocol family. */
54+
int getBits() {
55+
result = sum(ProtocolVersion version | version.getFamily() = this | version.getBit())
56+
}
3057
}
3158

3259
/** The creation of a context. */
@@ -63,21 +90,14 @@ abstract class ProtocolUnrestriction extends DataFlow::Node {
6390
* A context is being created with a range of allowed protocols.
6491
* This also serves as unrestricting these protocols.
6592
*/
66-
abstract class UnspecificContextCreation extends ContextCreation, ProtocolUnrestriction {
67-
TlsLibrary library;
68-
ProtocolFamily family;
69-
70-
UnspecificContextCreation() { this.getProtocol() = family }
71-
72-
override DataFlow::CfgNode getContext() { result = this }
73-
74-
override ProtocolVersion getUnrestriction() {
75-
// There is only one family, the two names are aliases in OpenSSL.
76-
// see https://github.com/openssl/openssl/blob/13888e797c5a3193e91d71e5f5a196a2d68d266f/include/openssl/ssl.h.in#L1953-L1955
77-
family in ["SSLv23", "TLS"] and
78-
// see https://docs.python.org/3/library/ssl.html#ssl-contexts
79-
result in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
80-
}
93+
abstract class UnspecificContextCreation extends ContextCreation {
94+
// override ProtocolVersion getUnrestriction() {
95+
// // There is only one family, the two names are aliases in OpenSSL.
96+
// // see https://github.com/openssl/openssl/blob/13888e797c5a3193e91d71e5f5a196a2d68d266f/include/openssl/ssl.h.in#L1953-L1955
97+
// family in ["SSLv23", "TLS"] and
98+
// // see https://docs.python.org/3/library/ssl.html#ssl-contexts
99+
// result in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
100+
// }
81101
}
82102

83103
/** A model of a SSL/TLS library. */

python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,25 @@
1212
| InsecureProtocol.py:24:1:24:35 | ControlFlowNode for SSLContext() | Insecure SSL/TLS protocol version SSLv2 specified by $@. | InsecureProtocol.py:24:1:24:35 | ControlFlowNode for SSLContext() | call to SSLContext |
1313
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
1414
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
15-
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
16-
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
1715
| pyOpenSSL_fluent.py:18:27:18:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:15:15:15:44 | ControlFlowNode for Attribute() | call to SSL.Context |
1816
| pyOpenSSL_fluent.py:18:27:18:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | pyOpenSSL_fluent.py:15:15:15:44 | ControlFlowNode for Attribute() | call to SSL.Context |
19-
| pyOpenSSL_fluent.py:18:27:18:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | pyOpenSSL_fluent.py:15:15:15:44 | ControlFlowNode for Attribute() | call to SSL.Context |
2017
| ssl_fluent.py:9:14:9:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:6:15:6:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2118
| ssl_fluent.py:9:14:9:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:6:15:6:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2219
| ssl_fluent.py:19:14:19:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:15:15:15:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2320
| ssl_fluent.py:28:14:28:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:24:15:24:53 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2421
| ssl_fluent.py:37:14:37:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:33:15:33:53 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
25-
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
26-
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
22+
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
23+
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2724
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2825
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:101:15:101:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2926
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
27+
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:65:15:65:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
3028
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:101:15:101:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
31-
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:117:5:117:11 | ControlFlowNode for context | context modification |
32-
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:135:5:135:11 | ControlFlowNode for context | context modification |
29+
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:115:15:115:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
3330
| ssl_fluent.py:77:14:77:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
3431
| ssl_fluent.py:77:14:77:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
35-
| ssl_fluent.py:97:14:97:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:95:5:95:11 | ControlFlowNode for context | context modification |
36-
| ssl_fluent.py:146:14:146:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:143:5:143:11 | ControlFlowNode for context | context modification |
37-
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | ssl_fluent.py:162:5:162:11 | ControlFlowNode for context | context modification |
32+
| ssl_fluent.py:97:14:97:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:65:15:65:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
33+
| ssl_fluent.py:146:14:146:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:142:15:142:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
34+
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | ssl_fluent.py:161:15:161:65 | ControlFlowNode for Attribute() | call to ssl.create_default_context |
3835
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:161:15:161:65 | ControlFlowNode for Attribute() | call to ssl.create_default_context |
3936
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:161:15:161:65 | ControlFlowNode for Attribute() | call to ssl.create_default_context |

0 commit comments

Comments
 (0)