Skip to content

Commit df9921f

Browse files
committed
Update according to the review comments
1 parent f893954 commit df9921f

File tree

10 files changed

+423
-145
lines changed

10 files changed

+423
-145
lines changed

java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,32 @@ class JndiInjectionFlowConfig extends TaintTracking::Configuration {
2121
}
2222

2323
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
24-
compositeNameStep(node1, node2) or
24+
nameStep(node1, node2) or
2525
jmxServiceUrlStep(node1, node2) or
2626
jmxConnectorStep(node1, node2) or
27-
rmiConnectorStep(node1, node2)
27+
rmiConnectorStep(node1, node2) or
28+
providerUrlEnvStep(node1, node2)
29+
}
30+
}
31+
32+
/** The class `javax.naming.directory.SearchControls`. */
33+
class TypeSearchControls extends Class {
34+
TypeSearchControls() { this.hasQualifiedName("javax.naming.directory", "SearchControls") }
35+
}
36+
37+
/** The interface `org.springframework.ldap.core.LdapOperations`. */
38+
class TypeSpringLdapOperations extends Interface {
39+
TypeSpringLdapOperations() {
40+
this.hasQualifiedName("org.springframework.ldap.core", "LdapOperations") or
41+
this.hasQualifiedName("org.springframework.ldap", "LdapOperations")
42+
}
43+
}
44+
45+
/** The interface `org.springframework.ldap.core.ContextMapper`. */
46+
class TypeSpringContextMapper extends Interface {
47+
TypeSpringContextMapper() {
48+
this.hasQualifiedName("org.springframework.ldap.core", "ContextMapper<T>") or
49+
this.hasQualifiedName("org.springframework.ldap", "ContextMapper<T>")
2850
}
2951
}
3052

@@ -50,6 +72,11 @@ class TypeJMXServiceURL extends Class {
5072
TypeJMXServiceURL() { this.hasQualifiedName("javax.management.remote", "JMXServiceURL") }
5173
}
5274

75+
/** The interface `javax.naming.Context`. */
76+
class TypeNamingContext extends Interface {
77+
TypeNamingContext() { this.hasQualifiedName("javax.naming", "Context") }
78+
}
79+
5380
/**
5481
* JNDI sink for JNDI injection vulnerabilities, i.e. 1st argument to `lookup`, `lookupLink`,
5582
* `doLookup`, `rename`, `list` or `listBindings` method from `InitialContext`.
@@ -73,17 +100,38 @@ predicate jndiSinkMethod(Method m, int index) {
73100
*/
74101
predicate springJndiTemplateSinkMethod(Method m, int index) {
75102
m.getDeclaringType() instanceof TypeSpringJndiTemplate and
76-
m.hasName("lookup") and
103+
(m.hasName("lookup") or m.hasName("setEnvironment")) and
77104
index = 0
78105
}
79106

80107
/**
81-
* Spring sink for JNDI injection vulnerabilities, i.e. 1st argument to `lookup` or `lookupContext`
82-
* method from Spring's `LdapTemplate`.
108+
* Spring sink for JNDI injection vulnerabilities, i.e. 1st argument to `lookup`, `lookupContext`,
109+
* `findByDn`, `rename`, `list`, `listBindings`, `unbind`, `search` or `searchForObject` method
110+
* from Spring's `LdapOperations`.
83111
*/
84-
predicate springLdapTemplateSinkMethod(Method m, int index) {
85-
m.getDeclaringType() instanceof TypeSpringLdapTemplate and
86-
(m.hasName("lookup") or m.hasName("lookupContext")) and
112+
predicate springLdapTemplateSinkMethod(MethodAccess ma, Method m, int index) {
113+
m.getDeclaringType().getAnAncestor() instanceof TypeSpringLdapOperations and
114+
(
115+
m.hasName("lookup")
116+
or
117+
m.hasName("lookupContext")
118+
or
119+
m.hasName("findByDn")
120+
or
121+
m.hasName("rename")
122+
or
123+
m.hasName("list")
124+
or
125+
m.hasName("listBindings")
126+
or
127+
m.hasName("unbind") and ma.getArgument(1).(CompileTimeConstantExpr).getBooleanValue() = true
128+
or
129+
m.getName().matches("search%") and
130+
m.getParameterType(m.getNumberOfParameters() - 1) instanceof TypeSpringContextMapper and
131+
not m.getAParamType() instanceof TypeSearchControls
132+
or
133+
m.hasName("search") and ma.getArgument(3).(CompileTimeConstantExpr).getBooleanValue() = true
134+
) and
87135
index = 0
88136
}
89137

@@ -108,10 +156,10 @@ predicate jmxConnectorFactorySinkMethod(Method m, int index) {
108156
}
109157

110158
/** Holds if parameter at index `index` in method `m` is JNDI injection sink. */
111-
predicate jndiInjectionSinkMethod(Method m, int index) {
159+
predicate jndiInjectionSinkMethod(MethodAccess ma, Method m, int index) {
112160
jndiSinkMethod(m, index) or
113161
springJndiTemplateSinkMethod(m, index) or
114-
springLdapTemplateSinkMethod(m, index) or
162+
springLdapTemplateSinkMethod(ma, m, index) or
115163
shiroSinkMethod(m, index) or
116164
jmxConnectorFactorySinkMethod(m, index)
117165
}
@@ -122,7 +170,7 @@ class JndiInjectionSink extends DataFlow::ExprNode {
122170
exists(MethodAccess ma, Method m, int index |
123171
ma.getMethod() = m and
124172
ma.getArgument(index) = this.getExpr() and
125-
jndiInjectionSinkMethod(m, index)
173+
jndiInjectionSinkMethod(ma, m, index)
126174
)
127175
or
128176
exists(MethodAccess ma, Method m |
@@ -131,15 +179,25 @@ class JndiInjectionSink extends DataFlow::ExprNode {
131179
m.getDeclaringType().getAnAncestor() instanceof TypeJMXConnector and
132180
m.hasName("connect")
133181
)
182+
or
183+
exists(ConstructorCall cc |
184+
cc.getConstructedType().getAnAncestor() instanceof TypeInitialContext or
185+
cc.getConstructedType() instanceof TypeSpringJndiTemplate
186+
|
187+
cc.getArgument(0) = this.getExpr()
188+
)
134189
}
135190
}
136191

137192
/**
138-
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `CompositeName`,
139-
* i.e. `new CompositeName(tainted)`.
193+
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `CompositeName` or
194+
* `CompoundName`, i.e. `new CompositeName(tainted)` or `new CompoundName(tainted)`.
140195
*/
141-
predicate compositeNameStep(ExprNode n1, ExprNode n2) {
142-
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeCompositeName |
196+
predicate nameStep(ExprNode n1, ExprNode n2) {
197+
exists(ConstructorCall cc |
198+
cc.getConstructedType() instanceof TypeCompositeName or
199+
cc.getConstructedType() instanceof TypeCompoundName
200+
|
143201
n1.asExpr() = cc.getAnArgument() and
144202
n2.asExpr() = cc
145203
)
@@ -178,3 +236,27 @@ predicate rmiConnectorStep(ExprNode n1, ExprNode n2) {
178236
n2.asExpr() = cc
179237
)
180238
}
239+
240+
/**
241+
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and
242+
* `Hashtable` or `Properties`, i.e. `env.put(Context.PROVIDER_URL, tainted)` or
243+
* `env.setProperty(Context.PROVIDER_URL, tainted)`.
244+
*/
245+
predicate providerUrlEnvStep(ExprNode n1, ExprNode n2) {
246+
exists(MethodAccess ma, Method m |
247+
n1.asExpr() = ma.getArgument(1) and n2.asExpr() = ma.getQualifier()
248+
|
249+
ma.getMethod() = m and
250+
m.getDeclaringType().getAnAncestor() instanceof TypeProperty and
251+
(m.hasName("put") or m.hasName("setProperty")) and
252+
(
253+
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url"
254+
or
255+
exists(Field f |
256+
ma.getArgument(0) = f.getAnAccess() and
257+
f.hasName("PROVIDER_URL") and
258+
f.getDeclaringType() instanceof TypeNamingContext
259+
)
260+
)
261+
)
262+
}

java/ql/src/experimental/semmle/code/java/frameworks/Jndi.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ class TypeInitialContext extends Class {
99
class TypeCompositeName extends Class {
1010
TypeCompositeName() { this.hasQualifiedName("javax.naming", "CompositeName") }
1111
}
12+
13+
/** The class `javax.naming.CompoundName`. */
14+
class TypeCompoundName extends Class {
15+
TypeCompoundName() { this.hasQualifiedName("javax.naming", "CompoundName") }
16+
}

0 commit comments

Comments
 (0)