Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cbbc7b2

Browse files
committed
Python: support unrestrictions
Also pyOpenSSL allows SSL 2 and SSL 3 on `SSLv23`
1 parent 97d2668 commit cbbc7b2

6 files changed

Lines changed: 200 additions & 89 deletions

File tree

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

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,63 +8,67 @@ import TlsLibraryModel
88
*/
99
class InsecureContextConfiguration extends DataFlow::Configuration {
1010
TlsLibrary library;
11+
ProtocolVersion tracked_version;
1112

12-
InsecureContextConfiguration() { this = library + ["AllowsTLSv1", "AllowsTLSv1_1"] }
13+
InsecureContextConfiguration() {
14+
this = library + "Allows" + tracked_version and
15+
tracked_version.isInsecure()
16+
}
17+
18+
ProtocolVersion getTrackedVersion() { result = tracked_version }
1319

1420
override predicate isSource(DataFlow::Node source) {
15-
source = library.unspecific_context_creation()
21+
// source = library.unspecific_context_creation()
22+
exists(ProtocolUnrestriction pu |
23+
pu = library.protocol_unrestriction() and
24+
pu.getUnrestriction() = tracked_version
25+
|
26+
source = pu.getContext()
27+
)
1628
}
1729

1830
override predicate isSink(DataFlow::Node sink) {
1931
sink = library.connection_creation().getContext()
2032
}
2133

22-
abstract string flag();
23-
2434
override predicate isBarrierOut(DataFlow::Node node) {
2535
exists(ProtocolRestriction r |
2636
r = library.protocol_restriction() and
2737
node = r.getContext() and
28-
r.getRestriction() = flag()
38+
r.getRestriction() = tracked_version
2939
)
3040
}
31-
}
32-
33-
/** Configuration to specifically track the insecure protocol TLS 1.0 */
34-
class AllowsTLSv1 extends InsecureContextConfiguration {
35-
AllowsTLSv1() { this = library + "AllowsTLSv1" }
36-
37-
override string flag() { result = "TLSv1" }
38-
}
3941

40-
/** Configuration to specifically track the insecure protocol TLS 1.1 */
41-
class AllowsTLSv1_1 extends InsecureContextConfiguration {
42-
AllowsTLSv1_1() { this = library + "AllowsTLSv1_1" }
43-
44-
override string flag() { result = "TLSv1_1" }
42+
override predicate isBarrierIn(DataFlow::Node node) {
43+
exists(ProtocolUnrestriction r |
44+
r = library.protocol_unrestriction() and
45+
node = r.getContext() and
46+
r.getUnrestriction() = tracked_version
47+
)
48+
}
4549
}
4650

4751
/**
4852
* A connection is created from a context allowing an insecure protocol,
4953
* and that protocol has not been restricted appropriately.
5054
*/
5155
predicate unsafe_connection_creation(
52-
DataFlow::Node node, ProtocolVersion insecure_version, CallNode call
56+
DataFlow::Node creation, ProtocolVersion insecure_version, DataFlow::Node source, boolean specific
5357
) {
54-
// Connection created from a context allowing TLS 1.0.
55-
exists(AllowsTLSv1 c, ContextCreation cc | c.hasFlow(cc, node) | cc.getNode() = call) and
56-
insecure_version = "TLSv1"
57-
or
58-
// Connection created from a context allowing TLS 1.1.
59-
exists(AllowsTLSv1_1 c, ContextCreation cc | c.hasFlow(cc, node) | cc.getNode() = call) and
60-
insecure_version = "TLSv1_1"
58+
// Connection created from a context allowing `insecure_version`.
59+
exists(InsecureContextConfiguration c, ProtocolUnrestriction cc | c.hasFlow(cc, creation) |
60+
insecure_version = c.getTrackedVersion() and
61+
source = cc and
62+
specific = false
63+
)
6164
or
62-
// Connection created from a context for an insecure protocol.
65+
// Connection created from a context specifying `insecure_version`.
6366
exists(TlsLibrary l, DataFlow::CfgNode cc |
6467
cc = l.insecure_connection_creation(insecure_version)
6568
|
66-
cc = node and
67-
cc.getNode() = call
69+
creation = cc and
70+
source = cc and
71+
specific = true
6872
)
6973
}
7074

python/ql/src/Security/CWE-327/InsecureProtocol.ql

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,25 @@ string callName(AstNode call) {
1818
exists(Attribute a | a = call | result = callName(a.getObject()) + "." + a.getName())
1919
}
2020

21-
from DataFlow::Node node, string insecure_version, CallNode call
21+
string sourceName(DataFlow::Node source) {
22+
result = "call to " + callName(source.asCfgNode().(CallNode).getFunction().getNode())
23+
or
24+
not source.asCfgNode() instanceof CallNode and
25+
not source instanceof ContextCreation and
26+
result = "context modification"
27+
}
28+
29+
string verb(boolean specific) {
30+
specific = true and result = "specified"
31+
or
32+
specific = false and result = "allowed"
33+
}
34+
35+
from DataFlow::Node creation, string insecure_version, DataFlow::Node source, boolean specific
2236
where
23-
unsafe_connection_creation(node, insecure_version, call)
37+
unsafe_connection_creation(creation, insecure_version, source, specific)
2438
or
25-
unsafe_context_creation(node, insecure_version, call)
26-
select node, "Insecure SSL/TLS protocol version " + insecure_version + " specified in $@ ", call,
27-
"call to " + callName(call.getFunction().getNode())
28-
//+ " specified in call to " + method_name + "."
39+
unsafe_context_creation(creation, insecure_version, source.asCfgNode()) and specific = true
40+
select creation,
41+
"Insecure SSL/TLS protocol version " + insecure_version + " " + verb(specific) + " by $@ ",
42+
source, sourceName(source)

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class ConnectionCall extends ConnectionCreation {
2626
}
2727
}
2828

29+
// This cannot be used to unrestrict,
30+
// see https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_options
2931
class SetOptionsCall extends ProtocolRestriction {
3032
override CallNode node;
3133

@@ -42,6 +44,10 @@ class SetOptionsCall extends ProtocolRestriction {
4244
}
4345
}
4446

47+
class UnspecificPyOpenSSLContextCreation extends PyOpenSSLContextCreation, UnspecificContextCreation {
48+
UnspecificPyOpenSSLContextCreation() { library = "pyOpenSSL" }
49+
}
50+
4551
class PyOpenSSL extends TlsLibrary {
4652
PyOpenSSL() { this = "pyOpenSSL" }
4753

@@ -50,11 +56,9 @@ class PyOpenSSL extends TlsLibrary {
5056
result = version + "_METHOD"
5157
}
5258

53-
override string unspecific_version_name() {
54-
result in [
55-
"TLS_METHOD", // This is not actually available in pyOpenSSL yet
56-
"SSLv23_METHOD" // This is what can negotiate TLS 1.3 (indeed, I know, I did test that..)
57-
]
59+
override string unspecific_version_name(ProtocolFamily family) {
60+
// `"TLS_METHOD"` is not actually available in pyOpenSSL yet, but should be coming soon..
61+
result = family + "_METHOD"
5862
}
5963

6064
override API::Node version_constants() { result = API::moduleImport("OpenSSL").getMember("SSL") }
@@ -70,4 +74,8 @@ class PyOpenSSL extends TlsLibrary {
7074
override ConnectionCreation connection_creation() { result instanceof ConnectionCall }
7175

7276
override ProtocolRestriction protocol_restriction() { result instanceof SetOptionsCall }
77+
78+
override ProtocolUnrestriction protocol_unrestriction() {
79+
result instanceof UnspecificPyOpenSSLContextCreation
80+
}
7381
}

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

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,31 @@ class OptionsAugOr extends ProtocolRestriction {
5656
override ProtocolVersion getRestriction() { result = restriction }
5757
}
5858

59+
class OptionsAugAndNot extends ProtocolUnrestriction {
60+
ProtocolVersion restriction;
61+
62+
OptionsAugAndNot() {
63+
exists(AugAssign aa, AttrNode attr, Expr flag, UnaryExpr notFlag |
64+
aa.getOperation().getOp() instanceof BitAnd and
65+
aa.getTarget() = attr.getNode() and
66+
attr.getName() = "options" and
67+
attr.getObject() = node and
68+
notFlag.getOp() instanceof Invert and
69+
notFlag.getOperand() = flag and
70+
flag = API::moduleImport("ssl").getMember("OP_NO_" + restriction).getAUse().asExpr() and
71+
(
72+
aa.getValue() = notFlag
73+
or
74+
impliesValue(aa.getValue(), notFlag, true, true)
75+
)
76+
)
77+
}
78+
79+
override DataFlow::CfgNode getContext() { result = this }
80+
81+
override ProtocolVersion getUnrestriction() { result = restriction }
82+
}
83+
5984
/** Whether `part` evaluates to `partIsTrue` if `whole` evaluates to `wholeIsTrue`. */
6085
predicate impliesValue(BinaryExpr whole, Expr part, boolean partIsTrue, boolean wholeIsTrue) {
6186
whole.getOp() instanceof BitAnd and
@@ -75,8 +100,8 @@ predicate impliesValue(BinaryExpr whole, Expr part, boolean partIsTrue, boolean
75100
)
76101
}
77102

78-
class ContextSetVersion extends ProtocolRestriction {
79-
string restriction;
103+
class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction {
104+
ProtocolVersion restriction;
80105

81106
ContextSetVersion() {
82107
exists(Attributes::AttrWrite aw |
@@ -90,6 +115,21 @@ class ContextSetVersion extends ProtocolRestriction {
90115
override DataFlow::CfgNode getContext() { result = this }
91116

92117
override ProtocolVersion getRestriction() { result.lessThan(restriction) }
118+
119+
override ProtocolVersion getUnrestriction() {
120+
restriction = result or restriction.lessThan(result)
121+
}
122+
}
123+
124+
class UnspecificSSLContextCreation extends SSLContextCreation, UnspecificContextCreation {
125+
UnspecificSSLContextCreation() { library = "ssl" }
126+
127+
override ProtocolVersion getUnrestriction() {
128+
result = UnspecificContextCreation.super.getUnrestriction() and
129+
// These are turned off by default
130+
// see https://docs.python.org/3/library/ssl.html#ssl-contexts
131+
not result in ["SSLv2", "SSLv3"]
132+
}
93133
}
94134

95135
class Ssl extends TlsLibrary {
@@ -100,16 +140,7 @@ class Ssl extends TlsLibrary {
100140
result = "PROTOCOL_" + version
101141
}
102142

103-
override string unspecific_version_name() {
104-
result =
105-
"PROTOCOL_" +
106-
[
107-
"TLS",
108-
// This can negotiate a TLS 1.3 connection (!)
109-
// see https://docs.python.org/3/library/ssl.html#ssl-contexts
110-
"SSLv23"
111-
]
112-
}
143+
override string unspecific_version_name(ProtocolFamily family) { result = "PROTOCOL_" + family }
113144

114145
override API::Node version_constants() { result = API::moduleImport("ssl") }
115146

@@ -132,4 +163,12 @@ class Ssl extends TlsLibrary {
132163
or
133164
result instanceof ContextSetVersion
134165
}
166+
167+
override ProtocolUnrestriction protocol_unrestriction() {
168+
result instanceof OptionsAugAndNot
169+
or
170+
result instanceof ContextSetVersion
171+
or
172+
result instanceof UnspecificSSLContextCreation
173+
}
135174
}

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

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ class ProtocolVersion extends string {
1919
or
2020
this = ["TLSv1", "TLSv1_1", "TLSv1_2"] and version = "TLSv1_3"
2121
}
22+
23+
predicate isInsecure() { this in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1"] }
24+
}
25+
26+
/** An unspecific protocol version */
27+
class ProtocolFamily extends string {
28+
ProtocolFamily() { this in ["SSLv23", "TLS"] }
2229
}
2330

2431
/** The creation of a context. */
@@ -42,14 +49,42 @@ abstract class ProtocolRestriction extends DataFlow::CfgNode {
4249
abstract ProtocolVersion getRestriction();
4350
}
4451

52+
/** A context is being relaxed on which protocols it can accepts. */
53+
abstract class ProtocolUnrestriction extends DataFlow::CfgNode {
54+
/** Gets the context being relaxed. */
55+
abstract DataFlow::CfgNode getContext();
56+
57+
/** Gets the protocol version being allowed. */
58+
abstract ProtocolVersion getUnrestriction();
59+
}
60+
61+
abstract class UnspecificContextCreation extends ContextCreation, ProtocolUnrestriction {
62+
TlsLibrary library;
63+
ProtocolFamily family;
64+
65+
UnspecificContextCreation() { this.getProtocol() = library.unspecific_version(family) }
66+
67+
override DataFlow::CfgNode getContext() { result = this }
68+
69+
override ProtocolVersion getUnrestriction() {
70+
family = "TLS" and
71+
result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
72+
or
73+
// This can negotiate a TLS 1.3 connection (!)
74+
// see https://docs.python.org/3/library/ssl.html#ssl-contexts
75+
family = "SSLv23" and
76+
result in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
77+
}
78+
}
79+
4580
abstract class TlsLibrary extends string {
4681
TlsLibrary() { this in ["ssl", "pyOpenSSL"] }
4782

4883
/** The name of a specific protocol version, known to be insecure. */
4984
abstract string specific_insecure_version_name(ProtocolVersion version);
5085

5186
/** The name of an unspecific protocol version, say TLS, known to have insecure instances. */
52-
abstract string unspecific_version_name();
87+
abstract string unspecific_version_name(ProtocolFamily family);
5388

5489
/** The module or class holding the version constants. */
5590
abstract API::Node version_constants();
@@ -60,8 +95,8 @@ abstract class TlsLibrary extends string {
6095
}
6196

6297
/** A dataflow node representing an unspecific protocol version, say TLS, known to have insecure instances. */
63-
DataFlow::Node unspecific_version() {
64-
result = version_constants().getMember(unspecific_version_name()).getAUse()
98+
DataFlow::Node unspecific_version(ProtocolFamily family) {
99+
result = version_constants().getMember(unspecific_version_name(family)).getAUse()
65100
}
66101

67102
/** The creation of a context with a deafult protocol. */
@@ -77,11 +112,11 @@ abstract class TlsLibrary extends string {
77112
}
78113

79114
/** The creation of a context with an unspecific protocol version, say TLS, known to have insecure instances. */
80-
DataFlow::CfgNode unspecific_context_creation() {
115+
DataFlow::CfgNode unspecific_context_creation(ProtocolFamily family) {
81116
result = default_context_creation()
82117
or
83118
result = specific_context_creation() and
84-
result.(ContextCreation).getProtocol() = unspecific_version()
119+
result.(ContextCreation).getProtocol() = unspecific_version(family)
85120
}
86121

87122
/** A connection is created in an insecure manner, not from a context. */
@@ -92,4 +127,7 @@ abstract class TlsLibrary extends string {
92127

93128
/** A context is being restricted on which protocols it can accepts. */
94129
abstract ProtocolRestriction protocol_restriction();
130+
131+
/** A context is being relaxed on which protocols it can accepts. */
132+
abstract ProtocolUnrestriction protocol_unrestriction();
95133
}

0 commit comments

Comments
 (0)