@@ -7,6 +7,7 @@ import codeql.util.Option
77 * that flows to the input artifact of a mac operation.
88 */
99predicate isDecryptToMacFlow ( ArtifactFlow:: PathNode src , ArtifactFlow:: PathNode sink ) {
10+ // Simply flow from decrypt output to a mac input
1011 ArtifactFlow:: flowPath ( src , sink ) and
1112 exists ( Crypto:: CipherOperationNode cipherOp |
1213 cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
@@ -17,30 +18,163 @@ predicate isDecryptToMacFlow(ArtifactFlow::PathNode src, ArtifactFlow::PathNode
1718 )
1819}
1920
21+ /**
22+ * Experimental interface for graph generation, supply the
23+ * node to determine if a issue exists, and if so
24+ * the graph can add a property on the node.
25+ */
2026predicate isDecryptToMacNode ( Crypto:: ArtifactNode node ) {
2127 exists ( ArtifactFlow:: PathNode src |
2228 isDecryptToMacFlow ( src , _) and
2329 node .asElement ( ) = src .getNode ( ) .asExpr ( )
2430 )
2531}
2632
33+ predicate isDecryptThenMacFlow ( DecryptThenMacFlow:: PathNode src , DecryptThenMacFlow:: PathNode sink ) {
34+ DecryptThenMacFlow:: flowPath ( src , sink )
35+ }
36+
2737/**
2838 * Holds when the src node is used as plaintext input to both
2939 * an encryption operation and a mac operation, via the
3040 * argument represented by InterimArg.
3141 */
3242predicate isPlaintextInEncryptionAndMac (
3343 PlaintextUseAsMacAndCipherInputFlow:: PathNode src ,
34- PlaintextUseAsMacAndCipherInputFlow:: PathNode sink , InterimArg arg
44+ PlaintextUseAsMacAndCipherInputFlow:: PathNode sink , EncryptOrMacCallArg arg
3545) {
3646 PlaintextUseAsMacAndCipherInputFlow:: flowPath ( src , sink ) and
37- arg = sink .getState ( ) .asSome ( )
47+ arg = sink .getState ( ) .asSome ( ) and
48+ // the above pathing adds flow steps that may not have consideration for the calling context
49+ // TODO: this is something we want to look into to improving, but for now
50+ // we can filter bad flows with one additional flow check, that the source goes to both
51+ // src and sink through a generic flow
52+ // note that the flow path above ensures src gets to the interim arg, so we just need to
53+ // verify the source to sink.
54+ // TODO: having to copy the generic data flow config into a use-use variant
55+ // should we fix this at the language level to allow use use more intuitively?
56+ // Seems to be a common issue.
57+ GenericDataSourceFlowUseUseFlow:: flow ( src .getNode ( ) , sink .getNode ( ) )
3858}
3959
40- module ArgToSinkConfig implements DataFlow:: ConfigSig {
60+ /**
61+ * A copy of GenericDataSourceFlow but with use-use flows enabled by removing the barrier out
62+ */
63+ module GenericDataSourceFlowUseUseConfig implements DataFlow:: ConfigSig {
64+ predicate isSource ( DataFlow:: Node source ) {
65+ source = any ( Crypto:: GenericSourceInstance i ) .getOutputNode ( )
66+ }
67+
68+ predicate isSink ( DataFlow:: Node sink ) {
69+ sink = any ( Crypto:: FlowAwareElement other ) .getInputNode ( )
70+ }
71+
72+ predicate isBarrierIn ( DataFlow:: Node node ) {
73+ node = any ( Crypto:: FlowAwareElement element ) .getOutputNode ( )
74+ }
75+
76+ predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
77+ node1 .( AdditionalFlowInputStep ) .getOutput ( ) = node2
78+ or
79+ exists ( MethodCall m |
80+ m .getMethod ( ) .hasQualifiedName ( "java.lang" , "String" , "getBytes" ) and
81+ node1 .asExpr ( ) = m .getQualifier ( ) and
82+ node2 .asExpr ( ) = m
83+ )
84+ }
85+ }
86+
87+ module GenericDataSourceFlowUseUseFlow = TaintTracking:: Global< GenericDataSourceFlowUseUseConfig > ;
88+
89+ module WrapperArgFlowConfig implements DataFlow:: ConfigSig {
90+ predicate isSource ( DataFlow:: Node source ) {
91+ // Start from a parameter and not a call to avoid flow going out of
92+ // the call. We want to flow down a call, so start from a parameter
93+ // and barrier flows through returns
94+ exists ( Method m | m .getParameter ( _) = source .asParameter ( ) )
95+ }
96+
97+ predicate isSink ( DataFlow:: Node sink ) {
98+ exists ( Crypto:: CipherOperationNode cipherOp |
99+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( )
100+ )
101+ or
102+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( ) )
103+ }
104+
105+ predicate isBarrierIn ( DataFlow:: Node node ) {
106+ node = any ( Crypto:: FlowAwareElement element ) .getOutputNode ( )
107+ }
108+
109+ predicate isBarrierOut ( DataFlow:: Node node ) {
110+ // stop all flow out of a call return
111+ // TODO: this might be too strict and remove taint flow, need to reassess
112+ exists ( Call c | c = node .asExpr ( ) ) or
113+ node = any ( Crypto:: FlowAwareElement element ) .getInputNode ( )
114+ }
115+
116+ predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
117+ node1 .( AdditionalFlowInputStep ) .getOutput ( ) = node2
118+ or
119+ exists ( MethodCall m |
120+ m .getMethod ( ) .hasQualifiedName ( "java.lang" , "String" , "getBytes" ) and
121+ node1 .asExpr ( ) = m .getQualifier ( ) and
122+ node2 .asExpr ( ) = m
123+ )
124+ }
125+ }
126+
127+ module WrapperArgFlow = TaintTracking:: Global< WrapperArgFlowConfig > ;
128+
129+ predicate encryptWrapperArg ( DataFlow:: Node n , DataFlow:: Node sink ) {
130+ exists ( Crypto:: CipherOperationNode cipherOp |
131+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TEncryptMode ( ) and
132+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( )
133+ ) and
134+ (
135+ exists ( Parameter p , DataFlow:: Node src |
136+ p = src .asParameter ( ) and
137+ WrapperArgFlow:: flow ( src , sink ) and
138+ n .asExpr ( ) = p .getAnArgument ( )
139+ )
140+ or
141+ n = sink // the call the target operation is considered a wrapper arg to itself
142+ )
143+ }
144+
145+ predicate decryptWrapperArg ( DataFlow:: Node n , DataFlow:: Node sink ) {
146+ exists ( Crypto:: CipherOperationNode cipherOp |
147+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
148+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( )
149+ ) and
150+ (
151+ exists ( Parameter p , DataFlow:: Node src |
152+ p = src .asParameter ( ) and
153+ WrapperArgFlow:: flow ( src , sink ) and
154+ n .asExpr ( ) = p .getAnArgument ( )
155+ )
156+ or
157+ n = sink // the call the target operation is considered a wrapper arg to itself
158+ )
159+ }
160+
161+ predicate macWrapperArg ( DataFlow:: Node n , DataFlow:: Node sink ) {
162+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( ) ) and
163+ (
164+ exists ( Parameter p , DataFlow:: Node src |
165+ p = src .asParameter ( ) and
166+ WrapperArgFlow:: flow ( src , sink ) and
167+ n .asExpr ( ) = p .getAnArgument ( )
168+ )
169+ or
170+ n = sink // the call the target operation is considered a wrapper arg to itself
171+ )
172+ }
173+
174+ module ArgToEncryptOrMacConfig implements DataFlow:: ConfigSig {
41175 predicate isSource ( DataFlow:: Node source ) { exists ( Call c | c .getAnArgument ( ) = source .asExpr ( ) ) }
42176
43- predicate isSink ( DataFlow:: Node sink ) { targetSinks ( sink ) }
177+ predicate isSink ( DataFlow:: Node sink ) { encryptOrMacSink ( sink ) }
44178
45179 // Don't go in to a known out node, this will prevent the plaintext
46180 // from tracing out of cipher operations for example, we just want to trace
@@ -62,12 +196,12 @@ module ArgToSinkConfig implements DataFlow::ConfigSig {
62196 }
63197}
64198
65- module ArgToSinkFlow = TaintTracking:: Global< ArgToSinkConfig > ;
199+ module ArgToEncryptOrMacFlow = TaintTracking:: Global< ArgToEncryptOrMacConfig > ;
66200
67201/**
68202 * Target sinks for this query are either encryption operations or mac operation message inputs
69203 */
70- predicate targetSinks ( DataFlow:: Node n ) {
204+ predicate encryptOrMacSink ( DataFlow:: Node n ) {
71205 exists ( Crypto:: CipherOperationNode cipherOp |
72206 cipherOp .getKeyOperationSubtype ( ) = Crypto:: TEncryptMode ( ) and
73207 cipherOp .getAnInputArtifact ( ) .asElement ( ) = n .asExpr ( )
@@ -77,44 +211,48 @@ predicate targetSinks(DataFlow::Node n) {
77211}
78212
79213/**
80- * An argument of a target sink or a parent call whose parameter flows to a target sink
214+ * Target sinks for decryption operations
81215 */
82- class InterimArg extends DataFlow:: Node {
83- DataFlow:: Node targetSink ;
84-
85- InterimArg ( ) {
86- targetSinks ( targetSink ) and
87- (
88- this = targetSink
89- or
90- ArgToSinkFlow:: flow ( this , targetSink ) and
91- this .getEnclosingCallable ( ) .calls + ( targetSink .getEnclosingCallable ( ) )
92- )
93- }
94-
95- DataFlow:: Node getTargetSink ( ) { result = targetSink }
216+ predicate decryptSink ( DataFlow:: Node n ) {
217+ exists ( Crypto:: CipherOperationNode cipherOp |
218+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
219+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = n .asExpr ( )
220+ )
96221}
97222
98- /**
99- * A wrapper class to represent a target argument dataflow node.
100- */
101- class TargetArg extends DataFlow:: Node {
102- TargetArg ( ) { targetSinks ( this ) }
223+ // /**
224+ // * An argument of a target sink or a parent call whose parameter flows to a target sink
225+ // */
226+ // class EncryptOrMacPartialFlowArg extends DataFlow::Node {
227+ // DataFlow::Node targetSink;
228+ // EncryptOrMacPartialFlowArg() {
229+ // encryptWrapperArg(this, targetSink)
230+ // or
231+ // macWrapperArg(this, targetSink)
232+ // }
233+ // DataFlow::Node getTargetSink() { result = targetSink }
234+ // }
235+ class EncryptOrMacCallArg extends DataFlow:: Node {
236+ boolean isEncryption ;
103237
104- predicate isCipher ( ) {
238+ EncryptOrMacCallArg ( ) {
105239 exists ( Crypto:: CipherOperationNode cipherOp |
106240 cipherOp .getKeyOperationSubtype ( ) = Crypto:: TEncryptMode ( ) and
107241 cipherOp .getAnInputArtifact ( ) .asElement ( ) = this .asExpr ( )
108- )
242+ ) and
243+ isEncryption = true
244+ or
245+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = this .asExpr ( ) ) and
246+ isEncryption = false
109247 }
110248
111- predicate isMac ( ) {
112- exists ( Crypto :: MacOperationNode macOp | macOp . getAnInputArtifact ( ) . asElement ( ) = this . asExpr ( ) )
113- }
249+ predicate isEncryption ( ) { isEncryption = true }
250+
251+ predicate isMac ( ) { isEncryption = false }
114252}
115253
116254module PlaintextUseAsMacAndCipherInputConfig implements DataFlow:: StateConfigSig {
117- class FlowState = Option< TargetArg > :: Option ;
255+ class FlowState = Option< EncryptOrMacCallArg > :: Option ;
118256
119257 // TODO: can we approximate a message source better?
120258 predicate isSource ( DataFlow:: Node source , FlowState state ) {
@@ -128,12 +266,9 @@ module PlaintextUseAsMacAndCipherInputConfig implements DataFlow::StateConfigSig
128266 }
129267
130268 predicate isSink ( DataFlow:: Node sink , FlowState state ) {
131- sink instanceof TargetArg and
132- (
133- sink .( TargetArg ) .isMac ( ) and state .asSome ( ) .isCipher ( )
134- or
135- sink .( TargetArg ) .isCipher ( ) and state .asSome ( ) .isMac ( )
136- )
269+ sink .( EncryptOrMacCallArg ) .isMac ( ) and state .asSome ( ) .isEncryption ( )
270+ or
271+ sink .( EncryptOrMacCallArg ) .isEncryption ( ) and state .asSome ( ) .isMac ( )
137272 }
138273
139274 predicate isBarrierOut ( DataFlow:: Node node , FlowState state ) {
@@ -163,16 +298,51 @@ module PlaintextUseAsMacAndCipherInputConfig implements DataFlow::StateConfigSig
163298 predicate isAdditionalFlowStep (
164299 DataFlow:: Node node1 , FlowState state1 , DataFlow:: Node node2 , FlowState state2
165300 ) {
166- ( exists ( state1 . asSome ( ) ) or state1 . isNone ( ) ) and
167- targetSinks ( node1 ) and
168- node1 instanceof TargetArg and
169- //use-use flow, either flow directly from the node1 use
170- //or find a parent call in the call in the call stack
171- //and continue flow from that parameter
172- node2 . ( InterimArg ) . getTargetSink ( ) = node1 and
301+ // TODO: should we consider isSome cases?
302+ state1 . isNone ( ) and
303+ (
304+ encryptWrapperArg ( node2 , node1 )
305+ or
306+ macWrapperArg ( node2 , node1 )
307+ ) and
173308 state2 .asSome ( ) = node1
174309 }
175310}
176311
177312module PlaintextUseAsMacAndCipherInputFlow =
178313 TaintTracking:: GlobalWithState< PlaintextUseAsMacAndCipherInputConfig > ;
314+
315+ module DecryptThenMacConfig implements DataFlow:: ConfigSig {
316+ predicate isSource ( DataFlow:: Node source ) {
317+ exists ( Crypto:: CipherOperationNode cipherOp |
318+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
319+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = source .asExpr ( )
320+ )
321+ }
322+
323+ predicate isSink ( DataFlow:: Node sink ) {
324+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( ) )
325+ }
326+
327+ // Don't go in to a known out node, prevents
328+ // from tracing out of an operation
329+ // NOTE: we are not using a barrier out on input nodes, because
330+ // that would remove 'use-use' flows, which we need
331+ predicate isBarrierIn ( DataFlow:: Node node ) {
332+ node = any ( Crypto:: FlowAwareElement element ) .getOutputNode ( )
333+ }
334+
335+ predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
336+ node1 .( AdditionalFlowInputStep ) .getOutput ( ) = node2
337+ or
338+ exists ( MethodCall m |
339+ m .getMethod ( ) .hasQualifiedName ( "java.lang" , "String" , "getBytes" ) and
340+ node1 .asExpr ( ) = m .getQualifier ( ) and
341+ node2 .asExpr ( ) = m
342+ )
343+ or
344+ decryptWrapperArg ( node2 , node1 )
345+ }
346+ }
347+
348+ module DecryptThenMacFlow = TaintTracking:: Global< DecryptThenMacConfig > ;
0 commit comments