Skip to content

Commit 9cbb7e0

Browse files
committed
Change query objective
1 parent 5704ac3 commit 9cbb7e0

File tree

4 files changed

+16
-130
lines changed

4 files changed

+16
-130
lines changed
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @name Improper LDAP Authentication
33
* @description A user-controlled query carries no authentication
4-
* @kind path-problem
4+
* @kind problem
55
* @problem.severity warning
66
* @id py/improper-ldap-auth
77
* @tags experimental
@@ -12,10 +12,7 @@
1212
// Determine precision above
1313
import python
1414
import experimental.semmle.python.security.LDAPImproperAuth
15-
import DataFlow::PathGraph
1615

17-
from LDAPImproperAuthenticationConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
18-
where config.hasFlowPath(source, sink)
19-
select sink.getNode(), source, sink,
20-
"$@ LDAP query parameter contains $@ and is executed without authentication.", sink.getNode(),
21-
"This", source.getNode(), "a user-provided value"
16+
from LDAPBind ldapBind
17+
where authenticatesImproperly(ldapBind)
18+
select "The following LDAP bind operation is executed without authentication", ldapBind

python/ql/src/experimental/semmle/python/Concepts.qll

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,7 @@ module LDAPBind {
159159
/**
160160
* Gets the argument containing the binding expression.
161161
*/
162-
abstract DataFlow::Node getPasswordNode();
163-
164-
/**
165-
* Gets the argument containing the executed query.
166-
*/
167-
abstract DataFlow::Node getQueryNode();
162+
abstract DataFlow::Node getPassword();
168163
}
169164
}
170165

@@ -179,7 +174,5 @@ class LDAPBind extends DataFlow::Node {
179174

180175
LDAPBind() { this = range }
181176

182-
DataFlow::Node getPasswordNode() { result = range.getPasswordNode() }
183-
184-
DataFlow::Node getQueryNode() { result = range.getQueryNode() }
177+
DataFlow::Node getPassword() { result = range.getPassword() }
185178
}

python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -98,86 +98,3 @@ private module Re {
9898
override DataFlow::Node getRegexNode() { result = regexNode }
9999
}
100100
}
101-
102-
/**
103-
* Provides models for Python's ldap-related libraries.
104-
*/
105-
private module LDAP {
106-
/**
107-
* Provides models for Python's `ldap` library.
108-
*
109-
* See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html
110-
*/
111-
private module LDAP2 {
112-
/**
113-
* List of `ldap` methods used to execute a query.
114-
*
115-
* See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions
116-
*/
117-
private class LDAP2QueryMethods extends string {
118-
LDAP2QueryMethods() {
119-
this in ["search", "search_s", "search_st", "search_ext", "search_ext_s"]
120-
}
121-
}
122-
123-
/**
124-
* A class to find `ldap` methods binding a connection.
125-
*
126-
* See `LDAP2QueryMethods`
127-
*/
128-
class LDAP2Bind extends DataFlow::CallCfgNode, LDAPBind::Range {
129-
DataFlow::Node queryNode;
130-
131-
LDAP2Bind() {
132-
exists(
133-
DataFlow::AttrRead bindMethod, DataFlow::CallCfgNode searchCall,
134-
DataFlow::AttrRead searchMethod
135-
|
136-
this.getFunction() = bindMethod and
137-
API::moduleImport("ldap").getMember("initialize").getACall() =
138-
bindMethod.getObject().getALocalSource() and
139-
bindMethod.getAttributeName().matches("%bind%") and
140-
searchCall.getFunction() = searchMethod and
141-
bindMethod.getObject().getALocalSource() = searchMethod.getObject().getALocalSource() and
142-
searchMethod.getAttributeName() instanceof LDAP2QueryMethods and
143-
(
144-
queryNode = searchCall.getArg(2) or
145-
queryNode = searchCall.getArgByName("filterstr")
146-
)
147-
)
148-
}
149-
150-
override DataFlow::Node getPasswordNode() { result = this.getArg(1) }
151-
152-
override DataFlow::Node getQueryNode() { result = queryNode }
153-
}
154-
}
155-
156-
/**
157-
* Provides models for Python's `ldap3` library.
158-
*
159-
* See https://pypi.org/project/ldap3/
160-
*/
161-
private module LDAP3 {
162-
/**
163-
* A class to find `ldap3` methods binding a connection.
164-
*/
165-
class LDAP3Bind extends DataFlow::CallCfgNode, LDAPBind::Range {
166-
DataFlow::Node queryNode;
167-
168-
LDAP3Bind() {
169-
exists(DataFlow::CallCfgNode searchCall, DataFlow::AttrRead searchMethod |
170-
this = API::moduleImport("ldap3").getMember("Connection").getACall() and
171-
searchMethod.getObject().getALocalSource() = this and
172-
searchCall.getFunction() = searchMethod and
173-
searchMethod.getAttributeName() = "search" and
174-
queryNode = searchCall.getArg(1)
175-
)
176-
}
177-
178-
override DataFlow::Node getPasswordNode() { result = this.getArgByName("password") }
179-
180-
override DataFlow::Node getQueryNode() { result = queryNode }
181-
}
182-
}
183-
}

python/ql/src/experimental/semmle/python/security/LDAPImproperAuth.qll

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,14 @@ import semmle.python.dataflow.new.DataFlow
88
import semmle.python.dataflow.new.TaintTracking
99
import semmle.python.dataflow.new.RemoteFlowSources
1010

11-
/**
12-
* A class to find `LDAPBind` methods using an empty password or set as None.
13-
*/
14-
class LDAPImproperAuthSink extends DataFlow::Node {
15-
LDAPImproperAuthSink() {
16-
exists(LDAPBind ldapBind |
17-
(
18-
(
19-
DataFlow::localFlow(DataFlow::exprNode(any(None noneName)), ldapBind.getPasswordNode()) or
20-
not exists(ldapBind.getPasswordNode())
21-
)
22-
or
23-
exists(StrConst emptyString |
24-
emptyString.getText() = "" and
25-
DataFlow::localFlow(DataFlow::exprNode(emptyString), ldapBind.getPasswordNode())
26-
)
27-
) and
28-
this = ldapBind.getQueryNode()
29-
)
30-
}
31-
}
32-
33-
/**
34-
* A taint-tracking configuration for detecting LDAP improper authentications.
35-
*/
36-
class LDAPImproperAuthenticationConfig extends TaintTracking::Configuration {
37-
LDAPImproperAuthenticationConfig() { this = "LDAPImproperAuthenticationConfig" }
38-
39-
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
40-
41-
override predicate isSink(DataFlow::Node sink) { sink instanceof LDAPImproperAuthSink }
11+
predicate authenticatesImproperly(LDAPBind ldapBind) {
12+
(
13+
DataFlow::localFlow(DataFlow::exprNode(any(None noneName)), ldapBind.getPassword()) or
14+
not exists(ldapBind.getPassword())
15+
)
16+
or
17+
exists(StrConst emptyString |
18+
emptyString.getText() = "" and
19+
DataFlow::localFlow(DataFlow::exprNode(emptyString), ldapBind.getPassword())
20+
)
4221
}

0 commit comments

Comments
 (0)