Skip to content

Commit eff94ef

Browse files
committed
Crypto: To allow for graph generation to have properties informed by assessments, altering a few queries weak/vuln/bad crypto to have qll files that can be accessed for other purposes, like graph generation. Also altering weak symmetric cipher to look for non-aes algorithms to be more comprehensive.
1 parent cc436e8 commit eff94ef

File tree

7 files changed

+228
-196
lines changed

7 files changed

+228
-196
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import java
2+
import experimental.quantum.Language
3+
import codeql.util.Option
4+
5+
/**
6+
* Holds when the src node is the output artifact of a decrypt operation
7+
* that flows to the input artifact of a mac operation.
8+
*/
9+
predicate isDecryptToMacFlow(ArtifactFlow::PathNode src, ArtifactFlow::PathNode sink) {
10+
ArtifactFlow::flowPath(src, sink) and
11+
exists(Crypto::CipherOperationNode cipherOp |
12+
cipherOp.getKeyOperationSubtype() = Crypto::TDecryptMode() and
13+
cipherOp.getAnOutputArtifact().asElement() = src.getNode().asExpr()
14+
) and
15+
exists(Crypto::MacOperationNode macOp |
16+
macOp.getAnInputArtifact().asElement() = sink.getNode().asExpr()
17+
)
18+
}
19+
20+
/**
21+
* Holds when the src node is used as plaintext input to both
22+
* an encryption operation and a mac operation, via the
23+
* argument represented by InterimArg.
24+
*/
25+
predicate isPlaintextInEncryptionAndMac(
26+
PlaintextUseAsMacAndCipherInputFlow::PathNode src,
27+
PlaintextUseAsMacAndCipherInputFlow::PathNode sink, InterimArg arg
28+
) {
29+
PlaintextUseAsMacAndCipherInputFlow::flowPath(src, sink) and
30+
arg = sink.getState().asSome()
31+
}
32+
33+
module ArgToSinkConfig implements DataFlow::ConfigSig {
34+
predicate isSource(DataFlow::Node source) { exists(Call c | c.getAnArgument() = source.asExpr()) }
35+
36+
predicate isSink(DataFlow::Node sink) { targetSinks(sink) }
37+
38+
// Don't go in to a known out node, this will prevent the plaintext
39+
// from tracing out of cipher operations for example, we just want to trace
40+
// the plaintext to uses.
41+
// NOTE: we are not using a barrier out on input nodes, because
42+
// that would remove 'use-use' flows, which we need
43+
predicate isBarrierIn(DataFlow::Node node) {
44+
node = any(Crypto::FlowAwareElement element).getOutputNode()
45+
}
46+
47+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
48+
node1.(AdditionalFlowInputStep).getOutput() = node2
49+
or
50+
exists(MethodCall m |
51+
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
52+
node1.asExpr() = m.getQualifier() and
53+
node2.asExpr() = m
54+
)
55+
}
56+
}
57+
58+
module ArgToSinkFlow = TaintTracking::Global<ArgToSinkConfig>;
59+
60+
/**
61+
* Target sinks for this query are either encryption operations or mac operation message inputs
62+
*/
63+
predicate targetSinks(DataFlow::Node n) {
64+
exists(Crypto::CipherOperationNode cipherOp |
65+
cipherOp.getKeyOperationSubtype() = Crypto::TEncryptMode() and
66+
cipherOp.getAnInputArtifact().asElement() = n.asExpr()
67+
)
68+
or
69+
exists(Crypto::MacOperationNode macOp | macOp.getAnInputArtifact().asElement() = n.asExpr())
70+
}
71+
72+
/**
73+
* An argument of a target sink or a parent call whose parameter flows to a target sink
74+
*/
75+
class InterimArg extends DataFlow::Node {
76+
DataFlow::Node targetSink;
77+
78+
InterimArg() {
79+
targetSinks(targetSink) and
80+
(
81+
this = targetSink
82+
or
83+
ArgToSinkFlow::flow(this, targetSink) and
84+
this.getEnclosingCallable().calls+(targetSink.getEnclosingCallable())
85+
)
86+
}
87+
88+
DataFlow::Node getTargetSink() { result = targetSink }
89+
}
90+
91+
/**
92+
* A wrapper class to represent a target argument dataflow node.
93+
*/
94+
class TargetArg extends DataFlow::Node {
95+
TargetArg() { targetSinks(this) }
96+
97+
predicate isCipher() {
98+
exists(Crypto::CipherOperationNode cipherOp |
99+
cipherOp.getKeyOperationSubtype() = Crypto::TEncryptMode() and
100+
cipherOp.getAnInputArtifact().asElement() = this.asExpr()
101+
)
102+
}
103+
104+
predicate isMac() {
105+
exists(Crypto::MacOperationNode macOp | macOp.getAnInputArtifact().asElement() = this.asExpr())
106+
}
107+
}
108+
109+
module PlaintextUseAsMacAndCipherInputConfig implements DataFlow::StateConfigSig {
110+
class FlowState = Option<TargetArg>::Option;
111+
112+
// TODO: can we approximate a message source better?
113+
predicate isSource(DataFlow::Node source, FlowState state) {
114+
// TODO: can we find the 'closest' parameter to the sinks?
115+
// i.e., use a generic source if we have it, but also isolate the
116+
// lowest level in the flow to the closest parameter node in the call graph?
117+
exists(Crypto::GenericSourceNode other |
118+
other.asElement() = CryptoInput::dfn_to_element(source)
119+
) and
120+
state.isNone()
121+
}
122+
123+
predicate isSink(DataFlow::Node sink, FlowState state) {
124+
sink instanceof TargetArg and
125+
(
126+
sink.(TargetArg).isMac() and state.asSome().isCipher()
127+
or
128+
sink.(TargetArg).isCipher() and state.asSome().isMac()
129+
)
130+
}
131+
132+
predicate isBarrierOut(DataFlow::Node node, FlowState state) {
133+
// Stop at the first sink for now
134+
isSink(node, state)
135+
}
136+
137+
// Don't go in to a known out node, this will prevent the plaintext
138+
// from tracing out of cipher operations for example, we just want to trace
139+
// the plaintext to uses.
140+
// NOTE: we are not using a barrier out on input nodes, because
141+
// that would remove 'use-use' flows, which we need
142+
predicate isBarrierIn(DataFlow::Node node) {
143+
node = any(Crypto::FlowAwareElement element).getOutputNode()
144+
}
145+
146+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
147+
node1.(AdditionalFlowInputStep).getOutput() = node2
148+
or
149+
exists(MethodCall m |
150+
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
151+
node1.asExpr() = m.getQualifier() and
152+
node2.asExpr() = m
153+
)
154+
}
155+
156+
predicate isAdditionalFlowStep(
157+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
158+
) {
159+
(exists(state1.asSome()) or state1.isNone()) and
160+
targetSinks(node1) and
161+
node1 instanceof TargetArg and
162+
//use-use flow, either flow directly from the node1 use
163+
//or find a parent call in the call in the call stack
164+
//and continue flow from that parameter
165+
node2.(InterimArg).getTargetSink() = node1 and
166+
state2.asSome() = node1
167+
}
168+
}
169+
170+
module PlaintextUseAsMacAndCipherInputFlow =
171+
TaintTracking::GlobalWithState<PlaintextUseAsMacAndCipherInputConfig>;

java/ql/src/experimental/quantum/Examples/BadMacOrderDecryptToMac.ql

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,9 @@
1111
import java
1212
import experimental.quantum.Language
1313
import ArtifactFlow::PathGraph
14+
import BadMacOrder
1415

1516
from ArtifactFlow::PathNode src, ArtifactFlow::PathNode sink
16-
where
17-
ArtifactFlow::flowPath(src, sink) and
18-
exists(Crypto::CipherOperationNode cipherOp |
19-
cipherOp.getKeyOperationSubtype() = Crypto::TDecryptMode() and
20-
cipherOp.getAnOutputArtifact().asElement() = src.getNode().asExpr()
21-
) and
22-
exists(Crypto::MacOperationNode macOp |
23-
macOp.getAnInputArtifact().asElement() = sink.getNode().asExpr()
24-
)
17+
where isDecryptToMacFlow(src, sink)
2518
select sink, src, sink,
2619
"MAC order potentially wrong: observed a potential decrypt operation output to MAC implying the MAC is on plaintext, and not a cipher."

java/ql/src/experimental/quantum/Examples/BadMacOrderMacOnEncryptPlaintext.ql

Lines changed: 2 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -10,156 +10,13 @@
1010

1111
import java
1212
import experimental.quantum.Language
13-
import codeql.util.Option
14-
15-
module ArgToSinkConfig implements DataFlow::ConfigSig {
16-
predicate isSource(DataFlow::Node source) { exists(Call c | c.getAnArgument() = source.asExpr()) }
17-
18-
predicate isSink(DataFlow::Node sink) { targetSinks(sink) }
19-
20-
// Don't go in to a known out node, this will prevent the plaintext
21-
// from tracing out of cipher operations for example, we just want to trace
22-
// the plaintext to uses.
23-
// NOTE: we are not using a barrier out on input nodes, because
24-
// that would remove 'use-use' flows, which we need
25-
predicate isBarrierIn(DataFlow::Node node) {
26-
node = any(Crypto::FlowAwareElement element).getOutputNode()
27-
}
28-
29-
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
30-
node1.(AdditionalFlowInputStep).getOutput() = node2
31-
or
32-
exists(MethodCall m |
33-
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
34-
node1.asExpr() = m.getQualifier() and
35-
node2.asExpr() = m
36-
)
37-
}
38-
}
39-
40-
module ArgToSinkFlow = TaintTracking::Global<ArgToSinkConfig>;
41-
42-
/**
43-
* Target sinks for this query are either encryption operations or mac operation message inputs
44-
*/
45-
predicate targetSinks(DataFlow::Node n) {
46-
exists(Crypto::CipherOperationNode cipherOp |
47-
cipherOp.getKeyOperationSubtype() = Crypto::TEncryptMode() and
48-
cipherOp.getAnInputArtifact().asElement() = n.asExpr()
49-
)
50-
or
51-
exists(Crypto::MacOperationNode macOp | macOp.getAnInputArtifact().asElement() = n.asExpr())
52-
}
53-
54-
/**
55-
* An argument of a target sink or a parent call whose parameter flows to a target sink
56-
*/
57-
class InterimArg extends DataFlow::Node {
58-
DataFlow::Node targetSink;
59-
60-
InterimArg() {
61-
targetSinks(targetSink) and
62-
(
63-
this = targetSink
64-
or
65-
ArgToSinkFlow::flow(this, targetSink) and
66-
this.getEnclosingCallable().calls+(targetSink.getEnclosingCallable())
67-
)
68-
}
69-
70-
DataFlow::Node getTargetSink() { result = targetSink }
71-
}
72-
73-
/**
74-
* A wrapper class to represent a target argument dataflow node.
75-
*/
76-
class TargetArg extends DataFlow::Node {
77-
TargetArg() { targetSinks(this) }
78-
79-
predicate isCipher() {
80-
exists(Crypto::CipherOperationNode cipherOp |
81-
cipherOp.getKeyOperationSubtype() = Crypto::TEncryptMode() and
82-
cipherOp.getAnInputArtifact().asElement() = this.asExpr()
83-
)
84-
}
85-
86-
predicate isMac() {
87-
exists(Crypto::MacOperationNode macOp | macOp.getAnInputArtifact().asElement() = this.asExpr())
88-
}
89-
}
90-
91-
module PlaintextUseAsMacAndCipherInputConfig implements DataFlow::StateConfigSig {
92-
class FlowState = Option<TargetArg>::Option;
93-
94-
// TODO: can we approximate a message source better?
95-
predicate isSource(DataFlow::Node source, FlowState state) {
96-
// TODO: can we find the 'closest' parameter to the sinks?
97-
// i.e., use a generic source if we have it, but also isolate the
98-
// lowest level in the flow to the closest parameter node in the call graph?
99-
exists(Crypto::GenericSourceNode other |
100-
other.asElement() = CryptoInput::dfn_to_element(source)
101-
) and
102-
state.isNone()
103-
}
104-
105-
predicate isSink(DataFlow::Node sink, FlowState state) {
106-
sink instanceof TargetArg and
107-
(
108-
sink.(TargetArg).isMac() and state.asSome().isCipher()
109-
or
110-
sink.(TargetArg).isCipher() and state.asSome().isMac()
111-
)
112-
}
113-
114-
predicate isBarrierOut(DataFlow::Node node, FlowState state) {
115-
// Stop at the first sink for now
116-
isSink(node, state)
117-
}
118-
119-
// Don't go in to a known out node, this will prevent the plaintext
120-
// from tracing out of cipher operations for example, we just want to trace
121-
// the plaintext to uses.
122-
// NOTE: we are not using a barrier out on input nodes, because
123-
// that would remove 'use-use' flows, which we need
124-
predicate isBarrierIn(DataFlow::Node node) {
125-
node = any(Crypto::FlowAwareElement element).getOutputNode()
126-
}
127-
128-
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
129-
node1.(AdditionalFlowInputStep).getOutput() = node2
130-
or
131-
exists(MethodCall m |
132-
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
133-
node1.asExpr() = m.getQualifier() and
134-
node2.asExpr() = m
135-
)
136-
}
137-
138-
predicate isAdditionalFlowStep(
139-
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
140-
) {
141-
(exists(state1.asSome()) or state1.isNone()) and
142-
targetSinks(node1) and
143-
node1 instanceof TargetArg and
144-
//use-use flow, either flow directly from the node1 use
145-
//or find a parent call in the call in the call stack
146-
//and continue flow from that parameter
147-
node2.(InterimArg).getTargetSink() = node1 and
148-
state2.asSome() = node1
149-
}
150-
}
151-
152-
module PlaintextUseAsMacAndCipherInputFlow =
153-
TaintTracking::GlobalWithState<PlaintextUseAsMacAndCipherInputConfig>;
154-
13+
import BadMacOrder
15514
import PlaintextUseAsMacAndCipherInputFlow::PathGraph
15615

15716
from
15817
PlaintextUseAsMacAndCipherInputFlow::PathNode src,
15918
PlaintextUseAsMacAndCipherInputFlow::PathNode sink, InterimArg arg
160-
where
161-
PlaintextUseAsMacAndCipherInputFlow::flowPath(src, sink) and
162-
arg = sink.getState().asSome()
19+
where isPlaintextInEncryptionAndMac(src, sink, arg)
16320
select sink, src, sink,
16421
"Source is used as plaintext to MAC and encryption operation. Indicates possible misuse of MAC. Path shows plaintext to final use through intermediate mac or encryption operation here $@",
16522
arg.asExpr(), arg.asExpr().toString()

java/ql/src/experimental/quantum/Examples/WeakHash.ql

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,8 @@
99
* experimental
1010
*/
1111

12-
import java
13-
import experimental.quantum.Language
12+
import WeakHash
1413

1514
from Crypto::HashAlgorithmNode alg, Crypto::HashType htype, string msg
16-
where
17-
htype = alg.getHashType() and
18-
(
19-
(htype != Crypto::SHA2() and htype != Crypto::SHA3()) and
20-
msg = "Use of unapproved hash algorithm or API: " + htype.toString() + "."
21-
or
22-
(htype = Crypto::SHA2() or htype = Crypto::SHA3()) and
23-
not exists(alg.getDigestLength()) and
24-
msg =
25-
"Use of approved hash algorithm or API type " + htype.toString() + " but unknown digest size."
26-
or
27-
exists(int digestLength |
28-
digestLength = alg.getDigestLength() and
29-
(htype = Crypto::SHA2() or htype = Crypto::SHA3()) and
30-
digestLength < 256 and
31-
msg =
32-
"Use of approved hash algorithm or API type " + htype.toString() + " but weak digest size ("
33-
+ digestLength + ")."
34-
)
35-
)
15+
where isUnapprovedHash(alg, htype, msg)
3616
select alg, msg

0 commit comments

Comments
 (0)