Skip to content

Commit dfe16aa

Browse files
committed
Python: Handle both positional and keyword args for LDAP bind
1 parent 1d7ddce commit dfe16aa

File tree

4 files changed

+56
-7
lines changed

4 files changed

+56
-7
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ private module LDAP {
8282
private class LDAP2Bind extends DataFlow::CallCfgNode, LDAPBind::Range {
8383
LDAP2Bind() { this.getFunction() = ldapBind() }
8484

85-
override DataFlow::Node getPassword() { result = this.getArg(1) }
85+
override DataFlow::Node getPassword() {
86+
result in [this.getArg(1), this.getArgByName("cred")]
87+
}
8688
}
8789

8890
/**
@@ -147,7 +149,9 @@ private module LDAP {
147149
class LDAP3Bind extends DataFlow::CallCfgNode, LDAPBind::Range {
148150
LDAP3Bind() { this = ldap3Connection().getACall() }
149151

150-
override DataFlow::Node getPassword() { result = this.getArgByName("password") }
152+
override DataFlow::Node getPassword() {
153+
result in [this.getArg(2), this.getArgByName("password")]
154+
}
151155
}
152156

153157
/**
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
| The following LDAP bind operation is executed without authentication | auth_bad_2.py:18:5:18:42 | ControlFlowNode for Attribute() |
22
| The following LDAP bind operation is executed without authentication | auth_bad_2.py:33:5:33:44 | ControlFlowNode for Attribute() |
33
| The following LDAP bind operation is executed without authentication | auth_bad_2.py:48:5:48:43 | ControlFlowNode for Attribute() |
4-
| The following LDAP bind operation is executed without authentication | auth_bad_2.py:63:5:63:39 | ControlFlowNode for Attribute() |
5-
| The following LDAP bind operation is executed without authentication | auth_bad_3.py:18:12:18:57 | ControlFlowNode for Connection() |
6-
| The following LDAP bind operation is executed without authentication | auth_bad_3.py:33:12:33:55 | ControlFlowNode for Connection() |
7-
| The following LDAP bind operation is executed without authentication | auth_bad_3.py:48:12:48:42 | ControlFlowNode for Connection() |
4+
| The following LDAP bind operation is executed without authentication | auth_bad_2.py:62:5:62:52 | ControlFlowNode for Attribute() |
5+
| The following LDAP bind operation is executed without authentication | auth_bad_2.py:76:5:76:39 | ControlFlowNode for Attribute() |
6+
| The following LDAP bind operation is executed without authentication | auth_bad_2.py:91:5:91:48 | ControlFlowNode for Attribute() |
7+
| The following LDAP bind operation is executed without authentication | auth_bad_3.py:18:12:18:43 | ControlFlowNode for Connection() |
8+
| The following LDAP bind operation is executed without authentication | auth_bad_3.py:33:12:33:57 | ControlFlowNode for Connection() |
9+
| The following LDAP bind operation is executed without authentication | auth_bad_3.py:47:12:47:55 | ControlFlowNode for Connection() |
10+
| The following LDAP bind operation is executed without authentication | auth_bad_3.py:62:12:62:42 | ControlFlowNode for Connection() |

python/ql/test/experimental/query-tests/Security/CWE-287/auth_bad_2.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ def bind_s_example():
4848
ldap_connection.bind_s('cn=root', None)
4949
user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter)
5050

51+
@app.route("/bind_s_example")
52+
def bind_s_example_kwargs():
53+
"""
54+
A RemoteFlowSource is used directly as DN and search filter while the bind's password
55+
is set to None
56+
"""
57+
58+
dn = request.args['dc']
59+
search_filter = request.args['search']
60+
61+
ldap_connection = ldap.initialize("ldap://127.0.0.1:1337")
62+
ldap_connection.bind_s(who='cn=root', cred=None)
63+
user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter)
5164

5265
@app.route("/bind_example")
5366
def bind_example():
@@ -64,5 +77,20 @@ def bind_example():
6477
user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter)
6578

6679

80+
@app.route("/bind_example")
81+
def bind_example():
82+
"""
83+
A RemoteFlowSource is used directly as DN and search filter while the bind's password
84+
is set to None
85+
"""
86+
87+
dn = request.args['dc']
88+
search_filter = request.args['search']
89+
90+
ldap_connection = ldap.initialize("ldap://127.0.0.1:1337")
91+
ldap_connection.bind(who='cn=root', cred="")
92+
user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter)
93+
94+
6795
# if __name__ == "__main__":
6896
# app.run(debug=True)

python/ql/test/experimental/query-tests/Security/CWE-287/auth_bad_3.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,24 @@ def passwordNone():
1515
search_filter = request.args['search']
1616

1717
srv = Server('servername', get_info=ALL)
18-
conn = Connection(srv, user='user_dn', password=None)
18+
conn = Connection(srv, 'user_dn', None)
1919
status, result, response, _ = conn.search(dn, search_filter)
2020

2121

22+
@app.route("/passwordNone")
23+
def passwordNoneKwargs():
24+
"""
25+
A RemoteFlowSource is used directly as DN and search filter while the connection's password
26+
is set to None
27+
"""
28+
29+
dn = request.args['dc']
30+
search_filter = request.args['search']
31+
32+
srv = Server('servername', get_info=ALL)
33+
conn = Connection(srv, user='user_dn', password=None)
34+
status, result, response, _ = conn.search(dn, search_filter)
35+
2236
@app.route("/passwordEmpty")
2337
def passwordEmpty():
2438
"""

0 commit comments

Comments
 (0)