8
8
*/
9
9
10
10
import java
11
+ import semmle.code.java.dataflow.DataFlow3
11
12
import semmle.code.java.dataflow.TaintTracking
13
+ import semmle.code.java.dataflow.TaintTracking2
14
+ import semmle.code.java.dataflow.TaintTracking3
12
15
import DataFlow:: PathGraph
13
16
14
17
/** The Java class `java.security.MessageDigest`. */
15
18
class MessageDigest extends RefType {
16
19
MessageDigest ( ) { this .hasQualifiedName ( "java.security" , "MessageDigest" ) }
17
20
}
18
21
22
+ class MDConstructor extends StaticMethodAccess {
23
+ MDConstructor ( ) {
24
+ exists ( Method m | m = this .getMethod ( ) |
25
+ m .getDeclaringType ( ) instanceof MessageDigest and
26
+ m .hasName ( "getInstance" )
27
+ )
28
+ }
29
+ }
30
+
19
31
/** The method `digest()` declared in `java.security.MessageDigest`. */
20
32
class MDDigestMethod extends Method {
21
33
MDDigestMethod ( ) {
@@ -48,24 +60,20 @@ class PasswordVarExpr extends Expr {
48
60
}
49
61
50
62
/** Taint configuration tracking flow from an expression whose name suggests it holds password data to a method call that generates a hash without a salt. */
51
- class HashWithoutSaltConfiguration extends TaintTracking :: Configuration {
52
- HashWithoutSaltConfiguration ( ) { this = "HashWithoutSaltConfiguration " }
63
+ class PasswordHashConfiguration extends TaintTracking3 :: Configuration {
64
+ PasswordHashConfiguration ( ) { this = "PasswordHashConfiguration " }
53
65
54
- override predicate isSource ( DataFlow :: Node source ) { source .asExpr ( ) instanceof PasswordVarExpr }
66
+ override predicate isSource ( DataFlow3 :: Node source ) { source .asExpr ( ) instanceof PasswordVarExpr }
55
67
56
- override predicate isSink ( DataFlow :: Node sink ) {
68
+ override predicate isSink ( DataFlow3 :: Node sink ) {
57
69
exists (
58
- MethodAccess mua // invoke `md.update(password)` without the call of `md.update(digest)`
70
+ MethodAccess ma // invoke `md.update(password)` without the call of `md.update(digest)`
59
71
|
60
- sink .asExpr ( ) = mua .getArgument ( 0 ) and
61
- mua .getMethod ( ) instanceof MDUpdateMethod // md.update(password)
62
- )
63
- or
64
- // invoke `md.digest(password)` without another call of `md.update(salt)`
65
- exists ( MethodAccess mda |
66
- sink .asExpr ( ) = mda .getArgument ( 0 ) and
67
- mda .getMethod ( ) instanceof MDDigestMethod and // md.digest(password)
68
- mda .getNumArgument ( ) = 1
72
+ sink .asExpr ( ) = ma .getArgument ( 0 ) and
73
+ (
74
+ ma .getMethod ( ) instanceof MDUpdateMethod or // md.update(password)
75
+ ma .getMethod ( ) instanceof MDDigestMethod // md.digest(password)
76
+ )
69
77
)
70
78
}
71
79
@@ -76,48 +84,64 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
76
84
* `byte[] messageDigest = md.digest(allBytes);`
77
85
* Or the password is concatenated with a salt as a string.
78
86
*/
79
- override predicate isSanitizer ( DataFlow :: Node node ) {
87
+ override predicate isSanitizer ( DataFlow3 :: Node node ) {
80
88
exists ( MethodAccess ma |
81
89
ma .getMethod ( ) .getDeclaringType ( ) .hasQualifiedName ( "java.lang" , "System" ) and
82
90
ma .getMethod ( ) .hasName ( "arraycopy" ) and
83
91
ma .getArgument ( 0 ) = node .asExpr ( )
84
92
) // System.arraycopy(password.getBytes(), ...)
85
93
or
86
94
exists ( AddExpr e | node .asExpr ( ) = e .getAnOperand ( ) ) // password+salt
87
- or
88
- exists ( MethodAccess mua , MethodAccess ma |
89
- ma .getArgument ( 0 ) = node .asExpr ( ) and // Detect wrapper methods that invoke `md.update(salt)`
90
- ma != mua and
95
+ }
96
+ }
97
+
98
+ class PasswordDigestConfiguration extends TaintTracking2:: Configuration {
99
+ PasswordDigestConfiguration ( ) { this = "PasswordDigestConfiguration" }
100
+
101
+ override predicate isSource ( DataFlow2:: Node source ) {
102
+ exists ( MDConstructor mc | source .asExpr ( ) = mc )
103
+ }
104
+
105
+ override predicate isSink ( DataFlow2:: Node sink ) {
106
+ exists ( MethodAccess ma |
91
107
(
92
- ma .getQualifier ( ) .getType ( ) instanceof Interface
93
- or
94
- mua .getQualifier ( ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getQualifier ( )
95
- or
96
- mua .getAnArgument ( ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getQualifier ( )
97
- or
98
- mua .getQualifier ( ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getAnArgument ( )
99
- or
100
- mua .getArgument ( 0 ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getAnArgument ( )
108
+ ma .getMethod ( ) instanceof MDUpdateMethod or
109
+ ma .getMethod ( ) instanceof MDDigestMethod
101
110
) and
102
- isMDUpdateCall ( mua .getMethod ( ) )
111
+ exists ( PasswordHashConfiguration cc | cc .hasFlowToExpr ( ma .getAnArgument ( ) ) ) and
112
+ sink .asExpr ( ) = ma .getQualifier ( )
103
113
)
104
114
}
105
115
}
106
116
107
- /** Holds if a method invokes `md.update(salt)`. */
108
- predicate isMDUpdateCall ( Callable caller ) {
109
- caller instanceof MDUpdateMethod
110
- or
111
- exists ( Callable callee |
112
- caller .polyCalls ( callee ) and
113
- (
114
- callee instanceof MDUpdateMethod or
115
- isMDUpdateCall ( callee )
117
+ class HashWithoutSaltConfiguration extends TaintTracking:: Configuration {
118
+ HashWithoutSaltConfiguration ( ) { this = "HashWithoutSaltConfiguration" }
119
+
120
+ override predicate isSource ( DataFlow:: Node source ) {
121
+ exists ( PasswordDigestConfiguration pc | pc .hasFlow ( source , _) )
122
+ }
123
+
124
+ override predicate isSink ( DataFlow:: Node sink ) {
125
+ exists ( MethodAccess ma |
126
+ ma .getMethod ( ) instanceof MDDigestMethod and // md.digest(password)
127
+ sink .asExpr ( ) = ma .getQualifier ( )
128
+ )
129
+ }
130
+
131
+ /** Holds if `md.update` or `md.digest` calls integrate something other than the password, perhaps a salt. */
132
+ override predicate isSanitizer ( DataFlow:: Node node ) {
133
+ exists ( MethodAccess ma |
134
+ (
135
+ ma .getMethod ( ) instanceof MDUpdateMethod
136
+ or
137
+ ma .getMethod ( ) instanceof MDDigestMethod and ma .getNumArgument ( ) != 0
138
+ ) and
139
+ node .asExpr ( ) = ma .getQualifier ( ) and
140
+ not exists ( PasswordHashConfiguration cc | cc .hasFlowToExpr ( ma .getAnArgument ( ) ) )
116
141
)
117
- )
142
+ }
118
143
}
119
144
120
- from DataFlow:: PathNode source , DataFlow:: PathNode sink , HashWithoutSaltConfiguration c
121
- where c .hasFlowPath ( source , sink )
122
- select sink .getNode ( ) , source , sink , "$@ is hashed without a salt." , source .getNode ( ) ,
123
- "The password"
145
+ from DataFlow:: PathNode source , DataFlow:: PathNode sink , HashWithoutSaltConfiguration cc
146
+ where cc .hasFlowPath ( source , sink )
147
+ select sink , source , sink , "$@ is hashed without a salt." , source , "The password"
0 commit comments