Skip to content

Commit 968c18d

Browse files
committed
Query to detect LDAP injections in Java
Refactoring according to review comments.
1 parent bed6a98 commit 968c18d

File tree

2 files changed

+55
-90
lines changed

2 files changed

+55
-90
lines changed

java/ql/src/Security/CWE/CWE-90/LdapInjection.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* external/cwe/cwe-090
1111
*/
1212

13-
import semmle.code.java.Expr
13+
import java
1414
import semmle.code.java.dataflow.FlowSources
1515
import LdapInjectionLib
1616
import DataFlow::PathGraph

java/ql/src/Security/CWE/CWE-90/LdapInjectionLib.qll

Lines changed: 54 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,13 @@ import semmle.code.java.frameworks.UnboundId
66
import semmle.code.java.frameworks.SpringLdap
77
import semmle.code.java.frameworks.ApacheLdap
88

9-
/** Holds if the parameter of `c` at index `paramIndex` is varargs. */
10-
bindingset[paramIndex]
11-
predicate isVarargs(Callable c, int paramIndex) {
12-
c.isVarargs() and paramIndex >= c.getNumberOfParameters() - 1
13-
}
14-
15-
/** A data flow source for unvalidated user input that is used to construct LDAP queries. */
16-
abstract class LdapInjectionSource extends DataFlow::Node { }
17-
18-
/** A data flow sink for unvalidated user input that is used to construct LDAP queries. */
19-
abstract class LdapInjectionSink extends DataFlow::ExprNode { }
20-
219
/**
2210
* A taint-tracking configuration for unvalidated user input that is used to construct LDAP queries.
2311
*/
2412
class LdapInjectionFlowConfig extends TaintTracking::Configuration {
2513
LdapInjectionFlowConfig() { this = "LdapInjectionFlowConfig" }
2614

27-
override predicate isSource(DataFlow::Node source) { source instanceof LdapInjectionSource }
15+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
2816

2917
override predicate isSink(DataFlow::Node sink) { sink instanceof LdapInjectionSink }
3018

@@ -56,101 +44,77 @@ class LdapInjectionFlowConfig extends TaintTracking::Configuration {
5644
}
5745
}
5846

59-
/** A source of remote user input. */
60-
class RemoteSource extends LdapInjectionSource {
61-
RemoteSource() { this instanceof RemoteFlowSource }
62-
}
63-
64-
/** A source of local user input. */
65-
class LocalSource extends LdapInjectionSource {
66-
LocalSource() { this instanceof LocalUserInput }
67-
}
68-
6947
/**
7048
* JNDI sink for LDAP injection vulnerabilities, i.e. 1st (DN) or 2nd (filter) argument to
7149
* `search` method from `DirContext`.
7250
*/
73-
class JndiLdapInjectionSink extends LdapInjectionSink {
74-
JndiLdapInjectionSink() {
75-
exists(MethodAccess ma, Method m, int index |
76-
ma.getMethod() = m and
77-
ma.getArgument(index) = this.getExpr()
78-
|
79-
m.getDeclaringType().getAnAncestor() instanceof TypeDirContext and
80-
m.hasName("search") and
81-
index in [0 .. 1]
82-
)
83-
}
51+
predicate jndiLdapInjectionSinkMethod(Method m, int index) {
52+
m.getDeclaringType().getAnAncestor() instanceof TypeDirContext and
53+
m.hasName("search") and
54+
index in [0 .. 1]
8455
}
8556

8657
/**
8758
* UnboundID sink for LDAP injection vulnerabilities,
8859
* i.e. LDAPConnection.search, LDAPConnection.asyncSearch or LDAPConnection.searchForEntry method.
8960
*/
90-
class UnboundIdLdapInjectionSink extends LdapInjectionSink {
91-
UnboundIdLdapInjectionSink() {
92-
exists(MethodAccess ma, Method m, int index, Parameter param |
93-
ma.getMethod() = m and
94-
ma.getArgument(index) = this.getExpr() and
95-
m.getParameter(index) = param
96-
|
97-
// LDAPConnection.search, LDAPConnection.asyncSearch or LDAPConnection.searchForEntry method
98-
(
99-
m instanceof MethodUnboundIdLDAPConnectionSearch or
100-
m instanceof MethodUnboundIdLDAPConnectionAsyncSearch or
101-
m instanceof MethodUnboundIdLDAPConnectionSearchForEntry
102-
) and
103-
// Parameter is not varargs
104-
not isVarargs(m, index)
105-
)
106-
}
61+
predicate unboundIdLdapInjectionSinkMethod(Method m, int index) {
62+
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
63+
m instanceof MethodUnboundIdLDAPConnectionSearch or
64+
m instanceof MethodUnboundIdLDAPConnectionAsyncSearch or
65+
m instanceof MethodUnboundIdLDAPConnectionSearchForEntry
66+
)
10767
}
10868

10969
/**
11070
* Spring LDAP sink for LDAP injection vulnerabilities,
11171
* i.e. LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method.
11272
*/
113-
class SpringLdapInjectionSink extends LdapInjectionSink {
114-
SpringLdapInjectionSink() {
115-
exists(MethodAccess ma, Method m, int index, RefType paramType |
116-
ma.getMethod() = m and
117-
ma.getArgument(index) = this.getExpr() and
118-
m.getParameterType(index) = paramType
119-
|
120-
// LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method
121-
(
122-
m instanceof MethodSpringLdapTemplateAuthenticate or
123-
m instanceof MethodSpringLdapTemplateFind or
124-
m instanceof MethodSpringLdapTemplateFindOne or
125-
m instanceof MethodSpringLdapTemplateSearch or
126-
m instanceof MethodSpringLdapTemplateSearchForContext or
127-
m instanceof MethodSpringLdapTemplateSearchForObject
128-
) and
129-
(
130-
// Parameter index is 1 (DN or query) or 2 (filter) if method is not authenticate
131-
index in [0 .. 1] and
132-
not m instanceof MethodSpringLdapTemplateAuthenticate
133-
or
134-
// But it's not the last parameter in case of authenticate method (last param is password)
135-
index in [0 .. 1] and
136-
index < m.getNumberOfParameters() - 1 and
137-
m instanceof MethodSpringLdapTemplateAuthenticate
138-
)
139-
)
140-
}
73+
predicate springLdapInjectionSinkMethod(Method m, int index) {
74+
// LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method
75+
(
76+
m instanceof MethodSpringLdapTemplateAuthenticate or
77+
m instanceof MethodSpringLdapTemplateFind or
78+
m instanceof MethodSpringLdapTemplateFindOne or
79+
m instanceof MethodSpringLdapTemplateSearch or
80+
m instanceof MethodSpringLdapTemplateSearchForContext or
81+
m instanceof MethodSpringLdapTemplateSearchForObject
82+
) and
83+
(
84+
// Parameter index is 1 (DN or query) or 2 (filter) if method is not authenticate
85+
index in [0 .. 1] and
86+
not m instanceof MethodSpringLdapTemplateAuthenticate
87+
or
88+
// But it's not the last parameter in case of authenticate method (last param is password)
89+
index in [0 .. 1] and
90+
index < m.getNumberOfParameters() - 1 and
91+
m instanceof MethodSpringLdapTemplateAuthenticate
92+
)
14193
}
14294

14395
/** Apache LDAP API sink for LDAP injection vulnerabilities, i.e. LdapConnection.search method. */
144-
class ApacheLdapInjectionSink extends LdapInjectionSink {
145-
ApacheLdapInjectionSink() {
146-
exists(MethodAccess ma, Method m, int index, RefType paramType |
96+
predicate apacheLdapInjectionSinkMethod(Method m, int index) {
97+
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
98+
m.getDeclaringType().getAnAncestor() instanceof TypeApacheLdapConnection and
99+
m.hasName("search")
100+
)
101+
}
102+
103+
/** Holds if parameter at index `index` in method `m` is LDAP injection sink. */
104+
predicate ldapInjectionSinkMethod(Method m, int index) {
105+
jndiLdapInjectionSinkMethod(m, index) or
106+
unboundIdLdapInjectionSinkMethod(m, index) or
107+
springLdapInjectionSinkMethod(m, index) or
108+
apacheLdapInjectionSinkMethod(m, index)
109+
}
110+
111+
/** A data flow sink for unvalidated user input that is used to construct LDAP queries. */
112+
class LdapInjectionSink extends DataFlow::ExprNode {
113+
LdapInjectionSink() {
114+
exists(MethodAccess ma, Method m, int index |
147115
ma.getMethod() = m and
148116
ma.getArgument(index) = this.getExpr() and
149-
m.getParameterType(index) = paramType
150-
|
151-
m.getDeclaringType().getAnAncestor() instanceof TypeApacheLdapConnection and
152-
m.hasName("search") and
153-
not isVarargs(m, index)
117+
ldapInjectionSinkMethod(m, index)
154118
)
155119
}
156120
}
@@ -235,12 +199,13 @@ predicate filterToStringStep(ExprNode n1, ExprNode n2) {
235199
* `SearchRequest`, i.e. `new SearchRequest(tainted)`.
236200
*/
237201
predicate unboundIdSearchRequestStep(ExprNode n1, ExprNode n2) {
238-
exists(ConstructorCall cc, int index |
202+
exists(ConstructorCall cc, int index, Parameter param |
239203
cc.getConstructedType() instanceof TypeUnboundIdSearchRequest
240204
|
241205
n1.asExpr() = cc.getArgument(index) and
242206
n2.asExpr() = cc and
243-
not isVarargs(cc.getConstructor(), index)
207+
cc.getConstructor().getParameter(index) = param and
208+
not param.isVarargs()
244209
)
245210
}
246211

0 commit comments

Comments
 (0)