-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathLDAPInjection.ql
More file actions
86 lines (76 loc) · 3.3 KB
/
LDAPInjection.ql
File metadata and controls
86 lines (76 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* @name Python LDAP Injection
* @description Python LDAP Injection through search filter
* @kind path-problem
* @problem.severity error
* @id python/ldap-injection
* @tags experimental
* security
* external/cwe/cwe-090
*/
import python
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.internal.TaintTrackingPublic
import DataFlow::PathGraph
class InitializeSink extends DataFlow::Node {
InitializeSink() {
exists(SsaVariable initVar, CallNode searchCall |
// get variable whose value equals a call to ldap.initialize
initVar.getDefinition().getImmediateDominator() = Value::named("ldap.initialize").getACall() and
// get the Call in which the previous variable is used
initVar.getAUse().getNode() = searchCall.getNode().getFunc().(Attribute).getObject() and
// restrict that call's attribute (something.this) to match %search%
searchCall.getNode().getFunc().(Attribute).getName().matches("%search%") and
// set the third argument (search_filter) as sink
this.asExpr() = searchCall.getArg(2).getNode()
// set the first argument (DN) as sink
// or this.asExpr() = searchCall.getArg(0) // Should this be set?
)
}
}
class ConnectionSink extends DataFlow::Node {
ConnectionSink() {
exists(SsaVariable connVar, CallNode searchCall |
// get variable whose value equals a call to ldap.initialize
connVar.getDefinition().getImmediateDominator() = Value::named("ldap3.Connection").getACall() and
// get the Call in which the previous variable is used
connVar.getAUse().getNode() = searchCall.getNode().getFunc().(Attribute).getObject() and
// restrict that call's attribute (something.this) to match %search%
searchCall.getNode().getFunc().(Attribute).getName().matches("%search%") and
// set the second argument (search_filter) as sink
this.asExpr() = searchCall.getArg(1).getNode()
// set the first argument (DN) as sink
// or this.asExpr() = searchCall.getArg(0) // Should this be set?
)
}
}
class EscapeSanitizer extends DataFlow::Node {
EscapeSanitizer() {
exists(Call c |
(
// avoid flow through any %escape% function
c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%()
c.getFunc().(Name).getId().matches("%escape%") // %escape%()
) and
this.asExpr() = c
)
}
}
class LDAPInjectionSink extends DataFlow::Node {
LDAPInjectionSink() {
this instanceof InitializeSink or
this instanceof ConnectionSink
}
}
class LDAPInjectionFlowConfig extends TaintTracking::Configuration {
LDAPInjectionFlowConfig() { this = "LDAPInjectionFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof LDAPInjectionSink }
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer }
}
from LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ LDAP query executes $@.", sink.getNode(), "This",
source.getNode(), "a user-provided value"