|
| 1 | +/** |
| 2 | + * Provides a taint-tracking configuration for detecting LDAP injection vulnerabilities |
| 3 | + */ |
| 4 | + |
| 5 | +import python |
| 6 | +import semmle.python.dataflow.new.DataFlow |
| 7 | +import semmle.python.dataflow.new.TaintTracking |
| 8 | +import semmle.python.dataflow.new.RemoteFlowSources |
| 9 | +import experimental.semmle.python.Concepts |
| 10 | + |
| 11 | +string getFullHostRegex() { result = "(?i)ldap://.+" } |
| 12 | + |
| 13 | +string getSchemaRegex() { result = "(?i)ldap(://)?" } |
| 14 | + |
| 15 | +string getPrivateHostRegex() { |
| 16 | + result = |
| 17 | + "(?i)localhost(?:[:/?#].*)?|127\\.0\\.0\\.1(?:[:/?#].*)?|10(?:\\.[0-9]+){3}(?:[:/?#].*)?|172\\.16(?:\\.[0-9]+){2}(?:[:/?#].*)?|192.168(?:\\.[0-9]+){2}(?:[:/?#].*)?|\\[?0:0:0:0:0:0:0:1\\]?(?:[:/?#].*)?|\\[?::1\\]?(?:[:/?#].*)?" |
| 18 | +} |
| 19 | + |
| 20 | +// "ldap://somethingon.theinternet.com" |
| 21 | +class LDAPFullHost extends StrConst { |
| 22 | + LDAPFullHost() { |
| 23 | + exists(string s | |
| 24 | + s = this.getText() and |
| 25 | + s.regexpMatch(getFullHostRegex()) and |
| 26 | + // check what comes after the `ldap://` prefix |
| 27 | + not s.substring(7, s.length()).regexpMatch(getPrivateHostRegex()) |
| 28 | + ) |
| 29 | + } |
| 30 | +} |
| 31 | + |
| 32 | +class LDAPSchema extends StrConst { |
| 33 | + LDAPSchema() { this.getText().regexpMatch(getSchemaRegex()) } |
| 34 | +} |
| 35 | + |
| 36 | +class LDAPPrivateHost extends StrConst { |
| 37 | + LDAPPrivateHost() { this.getText().regexpMatch(getPrivateHostRegex()) } |
| 38 | +} |
| 39 | + |
| 40 | +predicate concatAndCompareAgainstFullHostRegex(LDAPSchema schema, StrConst host) { |
| 41 | + not host instanceof LDAPPrivateHost and |
| 42 | + (schema.getText() + host.getText()).regexpMatch(getFullHostRegex()) |
| 43 | +} |
| 44 | + |
| 45 | +// "ldap://" + "somethingon.theinternet.com" |
| 46 | +class LDAPBothStrings extends BinaryExpr { |
| 47 | + LDAPBothStrings() { concatAndCompareAgainstFullHostRegex(this.getLeft(), this.getRight()) } |
| 48 | +} |
| 49 | + |
| 50 | +// schema + host |
| 51 | +class LDAPBothVar extends BinaryExpr { |
| 52 | + LDAPBothVar() { |
| 53 | + exists(SsaVariable schemaVar, SsaVariable hostVar | |
| 54 | + this.getLeft() = schemaVar.getVariable().getALoad() and // getAUse is incompatible with Expr |
| 55 | + this.getRight() = hostVar.getVariable().getALoad() and |
| 56 | + concatAndCompareAgainstFullHostRegex(schemaVar |
| 57 | + .getDefinition() |
| 58 | + .getImmediateDominator() |
| 59 | + .getNode(), hostVar.getDefinition().getImmediateDominator().getNode()) |
| 60 | + ) |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +// schema + "somethingon.theinternet.com" |
| 65 | +class LDAPVarString extends BinaryExpr { |
| 66 | + LDAPVarString() { |
| 67 | + exists(SsaVariable schemaVar | |
| 68 | + this.getLeft() = schemaVar.getVariable().getALoad() and |
| 69 | + concatAndCompareAgainstFullHostRegex(schemaVar |
| 70 | + .getDefinition() |
| 71 | + .getImmediateDominator() |
| 72 | + .getNode(), this.getRight()) |
| 73 | + ) |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +// "ldap://" + host |
| 78 | +class LDAPStringVar extends BinaryExpr { |
| 79 | + LDAPStringVar() { |
| 80 | + exists(SsaVariable hostVar | |
| 81 | + this.getRight() = hostVar.getVariable().getALoad() and |
| 82 | + concatAndCompareAgainstFullHostRegex(this.getLeft(), |
| 83 | + hostVar.getDefinition().getImmediateDominator().getNode()) |
| 84 | + ) |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * A taint-tracking configuration for detecting LDAP insecure authentications. |
| 90 | + */ |
| 91 | +class LDAPInsecureAuthConfig extends TaintTracking::Configuration { |
| 92 | + LDAPInsecureAuthConfig() { this = "LDAPInsecureAuthConfig" } |
| 93 | + |
| 94 | + override predicate isSource(DataFlow::Node source) { |
| 95 | + source instanceof RemoteFlowSource or |
| 96 | + source.asExpr() instanceof LDAPFullHost or |
| 97 | + source.asExpr() instanceof LDAPBothStrings or |
| 98 | + source.asExpr() instanceof LDAPBothVar or |
| 99 | + source.asExpr() instanceof LDAPVarString or |
| 100 | + source.asExpr() instanceof LDAPStringVar |
| 101 | + } |
| 102 | + |
| 103 | + override predicate isSink(DataFlow::Node sink) { |
| 104 | + exists(LDAPBind ldapBind | not ldapBind.useSSL() and sink = ldapBind.getHost()) |
| 105 | + } |
| 106 | +} |
0 commit comments