From 4ec78d04f8cc8f869af959556e9537bf651fd7e5 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 21 Dec 2020 00:15:15 +0000 Subject: [PATCH 1/9] Insecure LDAP authentication --- .../CWE/CWE-522/InsecureLdapAuth.java | 24 +++ .../CWE/CWE-522/InsecureLdapAuth.qhelp | 32 ++++ .../Security/CWE/CWE-522/InsecureLdapAuth.ql | 153 ++++++++++++++++++ .../CWE-522/InsecureLdapAuth.expected | 15 ++ .../security/CWE-522/InsecureLdapAuth.java | 92 +++++++++++ .../security/CWE-522/InsecureLdapAuth.qlref | 1 + 6 files changed, 317 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql create mode 100644 java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.qlref diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java new file mode 100644 index 000000000000..33764506a1b5 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java @@ -0,0 +1,24 @@ +public class InsecureLdapAuth { + /** LDAP authentication */ + public DirContext ldapAuth(String ldapUserName, String password) { + { + // BAD: LDAP authentication in cleartext + String ldapUrl = "ldap://ad.your-server.com:389"; + } + + { + // GOOD: LDAPS authentication over SSL + String ldapUrl = "ldaps://ad.your-server.com:636"; + } + + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + DirContext dirContext = new InitialDirContext(environment); + return dirContext; + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp new file mode 100644 index 000000000000..9b7193bf52f9 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp @@ -0,0 +1,32 @@ + + + + +

When using the Java LDAP API to perform LDAPv3-style extended operations and controls, a context with connection properties including user credentials is started. Transmission of LDAP credentials in cleartext allows remote attackers to obtain sensitive information by sniffing the network.

+
+ + +

Use LDAPS to send credentials through SSL or use SASL authentication.

+
+ + +

The following example shows two ways of using LDAP authentication. In the 'BAD' case, the credentials are transmitted in cleartext. In the 'GOOD' case, the credentials are transmitted over SSL.

+ +
+ + +
  • + CWE: + CWE-522: Insufficiently Protected Credentials + CWE-319: Cleartext Transmission of Sensitive Information +
  • +
  • + Oracle: + LDAP and LDAPS URLs +
  • +
  • + Oracle: + Simple authentication consists of sending the LDAP server the fully qualified DN of the client (user) and the client's clear-text password +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql new file mode 100644 index 000000000000..663f41105242 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -0,0 +1,153 @@ +/** + * @name Insecure LDAP authentication + * @description LDAP authentication with credentials sent in cleartext. + * @kind path-problem + * @id java/insecure-ldap-auth + * @tags security + * external/cwe-522 + * external/cwe-319 + */ + +import java +import semmle.code.java.frameworks.Jndi +import semmle.code.java.dataflow.TaintTracking +import DataFlow::PathGraph + +/** + * Gets a regular expression for matching private hosts, which only matches the host portion therefore checking for port is not necessary. + */ +private string getPrivateHostRegex() { + result = + "(?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\\]?(?:[:/?#].*)?" +} + +/** + * String of LDAP connections not in private domains. + */ +class LdapStringLiteral extends StringLiteral { + LdapStringLiteral() { + // Match connection strings with the LDAP protocol and without private IP addresses to reduce false positives. + exists(string s | this.getRepresentedString() = s | + s.regexpMatch("(?i)ldap://[\\[a-zA-Z0-9].*") and + not s.substring(7, s.length()).regexpMatch(getPrivateHostRegex()) + ) + } +} + +/** The interface `javax.naming.Context`. */ +class TypeNamingContext extends Interface { + TypeNamingContext() { this.hasQualifiedName("javax.naming", "Context") } +} + +/** The class `java.util.Hashtable`. */ +class TypeHashtable extends Class { + TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") } +} + +/** + * Holds if a non-private LDAP string is concatenated from both protocol and host. + */ +predicate concatLdapString(Expr protocol, Expr host) { + ( + protocol.(CompileTimeConstantExpr).getStringValue().regexpMatch("(?i)ldap(://)?") or + protocol + .(VarAccess) + .getVariable() + .getAnAssignedValue() + .(CompileTimeConstantExpr) + .getStringValue() + .regexpMatch("(?i)ldap(://)?") + ) and + not exists(string hostString | + hostString = host.(CompileTimeConstantExpr).getStringValue() or + hostString = + host.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue() + | + hostString.length() = 0 or // Empty host is loopback address + hostString.regexpMatch(getPrivateHostRegex()) + ) +} + +/** Gets the leftmost operand in a concatenated string */ +Expr getLeftmostConcatOperand(Expr expr) { + if expr instanceof AddExpr + then result = getLeftmostConcatOperand(expr.(AddExpr).getLeftOperand()) + else result = expr +} + +/** + * String concatenated with `LdapStringLiteral`. + */ +class LdapString extends Expr { + LdapString() { + this instanceof LdapStringLiteral + or + concatLdapString(this.(AddExpr).getLeftOperand(), + getLeftmostConcatOperand(this.(AddExpr).getRightOperand())) + } +} + +/** + * Tainted value passed to env `Hashtable` as the provider URL, i.e. + * `env.put(Context.PROVIDER_URL, tainted)` or `env.setProperty(Context.PROVIDER_URL, tainted)`. + */ +predicate isProviderUrlEnv(MethodAccess ma) { + ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and + (ma.getMethod().hasName("put") or ma.getMethod().hasName("setProperty")) and + ( + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url" + or + exists(Field f | + ma.getArgument(0) = f.getAnAccess() and + f.hasName("PROVIDER_URL") and + f.getDeclaringType() instanceof TypeNamingContext + ) + ) +} + +/** + * Holds if the value "simple" is passed to env `Hashtable` as the authentication mechanism, i.e. + * `env.put(Context.SECURITY_AUTHENTICATION, "simple")` or `env.setProperty(Context.SECURITY_AUTHENTICATION, "simple")`. + */ +predicate isSimpleAuthEnv(MethodAccess ma) { + ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and + (ma.getMethod().hasName("put") or ma.getMethod().hasName("setProperty")) and + ( + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = + "java.naming.security.authentication" + or + exists(Field f | + ma.getArgument(0) = f.getAnAccess() and + f.hasName("SECURITY_AUTHENTICATION") and + f.getDeclaringType() instanceof TypeNamingContext + ) + ) and + ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "simple" +} + +/** + * A taint-tracking configuration for cleartext credentials in LDAP authentication. + */ +class LdapAuthFlowConfig extends TaintTracking::Configuration { + LdapAuthFlowConfig() { this = "InsecureLdapAuth:LdapAuthFlowConfig" } + + /** Source of non-private LDAP connection string */ + override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof LdapString } + + /** Sink of provider URL with simple authentication */ + override predicate isSink(DataFlow::Node sink) { + exists(MethodAccess pma | + sink.asExpr() = pma.getArgument(1) and + isProviderUrlEnv(pma) and + exists(MethodAccess sma | + sma.getQualifier() = pma.getQualifier().(VarAccess).getVariable().getAnAccess() and + isSimpleAuthEnv(sma) + ) + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, LdapAuthFlowConfig config +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Insecure LDAP authentication from $@.", source.getNode(), + "LDAP connection string" diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected new file mode 100644 index 000000000000..65abcef0a9b1 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected @@ -0,0 +1,15 @@ +edges +| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | +| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | +| InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | +nodes +| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | +| InsecureLdapAuth.java:15:41:15:47 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | semmle.label | ... + ... : String | +| InsecureLdapAuth.java:29:41:29:47 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | +| InsecureLdapAuth.java:85:41:85:47 | ldapUrl | semmle.label | ldapUrl | +#select +| InsecureLdapAuth.java:15:41:15:47 | ldapUrl | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:29:41:29:47 | ldapUrl | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:25:20:25:39 | ... + ... | LDAP connection string | +| InsecureLdapAuth.java:85:41:85:47 | ldapUrl | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" | LDAP connection string | diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java new file mode 100644 index 000000000000..236880a1e8ec --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java @@ -0,0 +1,92 @@ +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.ldap.InitialLdapContext; + +public class InsecureLdapAuth { + // BAD - Test LDAP authentication in cleartext using `DirContext`. + public void testCleartextLdapAuth(String ldapUserName, String password) { + String ldapUrl = "ldap://ad.your-server.com:389"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + DirContext dirContext = new InitialDirContext(environment); + } + + // BAD - Test LDAP authentication in cleartext using `DirContext`. + public void testCleartextLdapAuth(String ldapUserName, String password, String serverName) { + String ldapUrl = "ldap://"+serverName+":389"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + DirContext dirContext = new InitialDirContext(environment); + } + + // GOOD - Test LDAP authentication over SSL. + public void testSslLdapAuth(String ldapUserName, String password) { + String ldapUrl = "ldaps://ad.your-server.com:636"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + DirContext dirContext = new InitialDirContext(environment); + } + + // GOOD - Test LDAP authentication with SASL authentication. + public void testSaslLdapAuth(String ldapUserName, String password) { + String ldapUrl = "ldap://ad.your-server.com:389"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + environment.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5 GSSAPI"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + DirContext dirContext = new InitialDirContext(environment); + } + + // GOOD - Test LDAP authentication in cleartext connecting to local LDAP server. + public void testCleartextLdapAuth2(String ldapUserName, String password) { + String ldapUrl = "ldap://localhost:389"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + DirContext dirContext = new InitialDirContext(environment); + } + + // BAD - Test LDAP authentication in cleartext using `InitialLdapContext`. + public void testCleartextLdapAuth3(String ldapUserName, String password) { + String ldapUrl = "ldap://ad.your-server.com:389"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + InitialLdapContext ldapContext = new InitialLdapContext(environment, null); + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.qlref b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.qlref new file mode 100644 index 000000000000..c2baa9841779 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql From c069a5b4c654e06ea669e7cdef0793d5179c9944 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 4 Jan 2021 14:51:32 +0000 Subject: [PATCH 2/9] Factor private host regex into the networking library and enhance the query --- .../Security/CWE/CWE-522/InsecureBasicAuth.ql | 12 +---- .../CWE/CWE-522/InsecureLdapAuth.qhelp | 2 +- .../Security/CWE/CWE-522/InsecureLdapAuth.ql | 54 +++++++------------ .../code/java/frameworks/Networking.qll | 10 ++++ .../CWE-522/InsecureLdapAuth.expected | 4 ++ .../security/CWE-522/InsecureLdapAuth.java | 15 ++++++ 6 files changed, 50 insertions(+), 47 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureBasicAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureBasicAuth.ql index 1fadb5bda692..3ec836a01175 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureBasicAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureBasicAuth.ql @@ -14,14 +14,6 @@ import semmle.code.java.frameworks.ApacheHttp import semmle.code.java.dataflow.TaintTracking import DataFlow::PathGraph -/** - * Gets a regular expression for matching private hosts, which only matches the host portion therefore checking for port is not necessary. - */ -private string getPrivateHostRegex() { - result = - "(?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\\]?(?:[:/?#].*)?" -} - /** * Class of Java URL constructor. */ @@ -76,7 +68,7 @@ class HttpStringLiteral extends StringLiteral { // Match URLs with the HTTP protocol and without private IP addresses to reduce false positives. exists(string s | this.getRepresentedString() = s | s.regexpMatch("(?i)http://[\\[a-zA-Z0-9].*") and - not s.substring(7, s.length()).regexpMatch(getPrivateHostRegex()) + not s.substring(7, s.length()) instanceof PrivateHostName ) } } @@ -101,7 +93,7 @@ predicate concatHttpString(Expr protocol, Expr host) { host.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue() | hostString.length() = 0 or // Empty host is loopback address - hostString.regexpMatch(getPrivateHostRegex()) + hostString instanceof PrivateHostName ) } diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp index 9b7193bf52f9..9241b52cf19d 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp @@ -11,7 +11,7 @@

    The following example shows two ways of using LDAP authentication. In the 'BAD' case, the credentials are transmitted in cleartext. In the 'GOOD' case, the credentials are transmitted over SSL.

    - +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql index 663f41105242..37b3057fb2f6 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -10,26 +10,19 @@ import java import semmle.code.java.frameworks.Jndi +import semmle.code.java.frameworks.Networking import semmle.code.java.dataflow.TaintTracking import DataFlow::PathGraph /** - * Gets a regular expression for matching private hosts, which only matches the host portion therefore checking for port is not necessary. + * Insecure (non-SSL, non-private) LDAP URL string literal. */ -private string getPrivateHostRegex() { - result = - "(?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\\]?(?:[:/?#].*)?" -} - -/** - * String of LDAP connections not in private domains. - */ -class LdapStringLiteral extends StringLiteral { - LdapStringLiteral() { +class InsecureLdapUrlLiteral extends StringLiteral { + InsecureLdapUrlLiteral() { // Match connection strings with the LDAP protocol and without private IP addresses to reduce false positives. exists(string s | this.getRepresentedString() = s | s.regexpMatch("(?i)ldap://[\\[a-zA-Z0-9].*") and - not s.substring(7, s.length()).regexpMatch(getPrivateHostRegex()) + not s.substring(7, s.length()) instanceof PrivateHostName ) } } @@ -47,24 +40,15 @@ class TypeHashtable extends Class { /** * Holds if a non-private LDAP string is concatenated from both protocol and host. */ -predicate concatLdapString(Expr protocol, Expr host) { - ( - protocol.(CompileTimeConstantExpr).getStringValue().regexpMatch("(?i)ldap(://)?") or - protocol - .(VarAccess) - .getVariable() - .getAnAssignedValue() - .(CompileTimeConstantExpr) - .getStringValue() - .regexpMatch("(?i)ldap(://)?") - ) and +predicate concatInsecureLdapString(Expr protocol, Expr host) { + protocol.(CompileTimeConstantExpr).getStringValue() = "ldap://" and not exists(string hostString | hostString = host.(CompileTimeConstantExpr).getStringValue() or hostString = host.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue() | hostString.length() = 0 or // Empty host is loopback address - hostString.regexpMatch(getPrivateHostRegex()) + hostString instanceof PrivateHostName ) } @@ -76,22 +60,21 @@ Expr getLeftmostConcatOperand(Expr expr) { } /** - * String concatenated with `LdapStringLiteral`. + * String concatenated with `InsecureLdapUrlLiteral`. */ -class LdapString extends Expr { - LdapString() { - this instanceof LdapStringLiteral +class InsecureLdapUrl extends Expr { + InsecureLdapUrl() { + this instanceof InsecureLdapUrlLiteral or - concatLdapString(this.(AddExpr).getLeftOperand(), + concatInsecureLdapString(this.(AddExpr).getLeftOperand(), getLeftmostConcatOperand(this.(AddExpr).getRightOperand())) } } /** - * Tainted value passed to env `Hashtable` as the provider URL, i.e. - * `env.put(Context.PROVIDER_URL, tainted)` or `env.setProperty(Context.PROVIDER_URL, tainted)`. + * Holds if `ma` writes the `java.naming.provider.url` (also known as `Context.PROVIDER_URL`) key of a `Hashtable`. */ -predicate isProviderUrlEnv(MethodAccess ma) { +predicate isProviderUrlSetter(MethodAccess ma) { ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and (ma.getMethod().hasName("put") or ma.getMethod().hasName("setProperty")) and ( @@ -106,8 +89,7 @@ predicate isProviderUrlEnv(MethodAccess ma) { } /** - * Holds if the value "simple" is passed to env `Hashtable` as the authentication mechanism, i.e. - * `env.put(Context.SECURITY_AUTHENTICATION, "simple")` or `env.setProperty(Context.SECURITY_AUTHENTICATION, "simple")`. + * Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`. */ predicate isSimpleAuthEnv(MethodAccess ma) { ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and @@ -132,13 +114,13 @@ class LdapAuthFlowConfig extends TaintTracking::Configuration { LdapAuthFlowConfig() { this = "InsecureLdapAuth:LdapAuthFlowConfig" } /** Source of non-private LDAP connection string */ - override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof LdapString } + override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof InsecureLdapUrl } /** Sink of provider URL with simple authentication */ override predicate isSink(DataFlow::Node sink) { exists(MethodAccess pma | sink.asExpr() = pma.getArgument(1) and - isProviderUrlEnv(pma) and + isProviderUrlSetter(pma) and exists(MethodAccess sma | sma.getQualifier() = pma.getQualifier().(VarAccess).getVariable().getAnAccess() and isSimpleAuthEnv(sma) diff --git a/java/ql/src/semmle/code/java/frameworks/Networking.qll b/java/ql/src/semmle/code/java/frameworks/Networking.qll index 988510dd2e90..997b80754036 100644 --- a/java/ql/src/semmle/code/java/frameworks/Networking.qll +++ b/java/ql/src/semmle/code/java/frameworks/Networking.qll @@ -129,3 +129,13 @@ class UrlOpenConnectionMethod extends Method { this.getName() = "openConnection" } } + +/** + * A string matching private host names of IPv4 and IPv6, which only matches the host portion therefore checking for port is not necessary. + */ +class PrivateHostName extends string { + bindingset[this] + PrivateHostName() { + this.regexpMatch("(?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\\]?(?:[:/?#].*)?") + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected index 65abcef0a9b1..c34c8966e0df 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected @@ -2,6 +2,7 @@ edges | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | +| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:47:100:53 | ldapUrl | nodes | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | semmle.label | ldapUrl | @@ -9,7 +10,10 @@ nodes | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | semmle.label | ldapUrl | | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | +| InsecureLdapAuth.java:100:47:100:53 | ldapUrl | semmle.label | ldapUrl | #select | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" | LDAP connection string | | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:25:20:25:39 | ... + ... | LDAP connection string | | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:100:47:100:53 | ldapUrl | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:47:100:53 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" | LDAP connection string | diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java index 236880a1e8ec..cc16047ebf2e 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java @@ -89,4 +89,19 @@ public void testCleartextLdapAuth3(String ldapUserName, String password) { environment.put(Context.SECURITY_CREDENTIALS, password); InitialLdapContext ldapContext = new InitialLdapContext(environment, null); } + + + // BAD - Test LDAP authentication in cleartext using `DirContext` and string literals. + public void testCleartextLdapAuth4(String ldapUserName, String password) { + String ldapUrl = "ldap://ad.your-server.com:389"; + Hashtable environment = new Hashtable(); + environment.put("java.naming.factory.initial", + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put("java.naming.provider.url", ldapUrl); + environment.put("java.naming.referral", "follow"); + environment.put("java.naming.security.authentication", "simple"); + environment.put("java.naming.security.principal", ldapUserName); + environment.put("java.naming.security.credentials", password); + DirContext dirContext = new InitialDirContext(environment); + } } From babe744a3011a4f7e2216ff5bd95bed241dd4984 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 13 Jan 2021 03:49:08 +0000 Subject: [PATCH 3/9] Add SECURITY_PROTOCOL check --- .../Security/CWE/CWE-522/InsecureLdapAuth.ql | 30 +++++++++++++++---- .../CWE-522/InsecureLdapAuth.expected | 14 ++++----- .../security/CWE-522/InsecureLdapAuth.java | 15 ++++++++++ 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql index 37b3057fb2f6..1cebfe8bad30 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -89,22 +89,36 @@ predicate isProviderUrlSetter(MethodAccess ma) { } /** - * Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`. + * Holds if `ma` sets `fieldValue` with attribute name `fieldName` to `envValue` in some `Hashtable`. */ -predicate isSimpleAuthEnv(MethodAccess ma) { +bindingset[fieldName, fieldValue, envValue] +predicate hasEnvWithValue(MethodAccess ma, string fieldName, string fieldValue, string envValue) { ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and (ma.getMethod().hasName("put") or ma.getMethod().hasName("setProperty")) and ( - ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = - "java.naming.security.authentication" + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue or exists(Field f | ma.getArgument(0) = f.getAnAccess() and - f.hasName("SECURITY_AUTHENTICATION") and + f.hasName(fieldName) and f.getDeclaringType() instanceof TypeNamingContext ) ) and - ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "simple" + ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue +} + +/** + * Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`. + */ +predicate isSimpleAuthEnv(MethodAccess ma) { + hasEnvWithValue(ma, "SECURITY_AUTHENTICATION", "java.naming.security.authentication", "simple") +} + +/** + * Holds if `ma` sets `java.naming.security.protocol` (also known as `Context.SECURITY_PROTOCOL`) to `ssl` in some `Hashtable`. + */ +predicate isSSLEnv(MethodAccess ma) { + hasEnvWithValue(ma, "SECURITY_PROTOCOL", "java.naming.security.protocol", "ssl") } /** @@ -124,6 +138,10 @@ class LdapAuthFlowConfig extends TaintTracking::Configuration { exists(MethodAccess sma | sma.getQualifier() = pma.getQualifier().(VarAccess).getVariable().getAnAccess() and isSimpleAuthEnv(sma) + ) and + not exists(MethodAccess sma | + sma.getQualifier() = pma.getQualifier().(VarAccess).getVariable().getAnAccess() and + isSSLEnv(sma) ) ) } diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected index c34c8966e0df..745fd9c1b75e 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected @@ -1,19 +1,19 @@ edges | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | -| InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | -| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:47:100:53 | ldapUrl | +| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:41:100:47 | ldapUrl | +| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:115:47:115:53 | ldapUrl | nodes | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | semmle.label | ldapUrl | | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | semmle.label | ... + ... : String | | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | semmle.label | ldapUrl | -| InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:85:41:85:47 | ldapUrl | semmle.label | ldapUrl | | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:100:47:100:53 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:100:41:100:47 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | +| InsecureLdapAuth.java:115:47:115:53 | ldapUrl | semmle.label | ldapUrl | #select | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" | LDAP connection string | | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:25:20:25:39 | ... + ... | LDAP connection string | -| InsecureLdapAuth.java:85:41:85:47 | ldapUrl | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:85:41:85:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:81:20:81:50 | "ldap://ad.your-server.com:389" | LDAP connection string | -| InsecureLdapAuth.java:100:47:100:53 | ldapUrl | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:47:100:53 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:100:41:100:47 | ldapUrl | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:41:100:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:115:47:115:53 | ldapUrl | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:115:47:115:53 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" | LDAP connection string | diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java index cc16047ebf2e..4052557d8b04 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java @@ -48,6 +48,21 @@ public void testSslLdapAuth(String ldapUserName, String password) { DirContext dirContext = new InitialDirContext(environment); } + // GOOD - Test LDAP authentication over SSL. + public void testSslLdapAuth2(String ldapUserName, String password) { + String ldapUrl = "ldap://ad.your-server.com:636"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + environment.put(Context.REFERRAL, "follow"); + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); + environment.put(Context.SECURITY_CREDENTIALS, password); + environment.put(Context.SECURITY_PROTOCOL, "ssl"); + DirContext dirContext = new InitialDirContext(environment); + } + // GOOD - Test LDAP authentication with SASL authentication. public void testSaslLdapAuth(String ldapUserName, String password) { String ldapUrl = "ldap://ad.your-server.com:389"; From e5a703e49c3fe53aeda8e6ecb9d3766df7683124 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 15 Jan 2021 04:05:11 +0000 Subject: [PATCH 4/9] Revamp the query --- .../Security/CWE/CWE-522/InsecureLdapAuth.ql | 109 +++++++++++++++--- .../CWE-522/InsecureLdapAuth.expected | 79 +++++++++++-- .../security/CWE-522/InsecureLdapAuth.java | 33 ++++++ 3 files changed, 190 insertions(+), 31 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql index 1cebfe8bad30..1a38f74092b9 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -110,7 +110,7 @@ predicate hasEnvWithValue(MethodAccess ma, string fieldName, string fieldValue, /** * Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`. */ -predicate isSimpleAuthEnv(MethodAccess ma) { +predicate isBasicAuthEnv(MethodAccess ma) { hasEnvWithValue(ma, "SECURITY_AUTHENTICATION", "java.naming.security.authentication", "simple") } @@ -122,32 +122,103 @@ predicate isSSLEnv(MethodAccess ma) { } /** - * A taint-tracking configuration for cleartext credentials in LDAP authentication. + * A taint-tracking configuration for `ldap://` URL in LDAP authentication. */ -class LdapAuthFlowConfig extends TaintTracking::Configuration { - LdapAuthFlowConfig() { this = "InsecureLdapAuth:LdapAuthFlowConfig" } +class InsecureUrlFlowConfig extends TaintTracking::Configuration { + InsecureUrlFlowConfig() { this = "InsecureLdapAuth:InsecureUrlFlowConfig" } - /** Source of non-private LDAP connection string */ + /** Source of `ldap://` connection string. */ override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof InsecureLdapUrl } - /** Sink of provider URL with simple authentication */ + /** Sink of directory context creation. */ override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess pma | - sink.asExpr() = pma.getArgument(1) and - isProviderUrlSetter(pma) and - exists(MethodAccess sma | - sma.getQualifier() = pma.getQualifier().(VarAccess).getVariable().getAnAccess() and - isSimpleAuthEnv(sma) - ) and - not exists(MethodAccess sma | - sma.getQualifier() = pma.getQualifier().(VarAccess).getVariable().getAnAccess() and - isSSLEnv(sma) - ) + exists(ConstructorCall cc | + cc.getConstructedType().getASupertype*() instanceof TypeDirContext and + sink.asExpr() = cc.getArgument(0) + ) + } + + /** Method call of `env.put()`. */ + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(MethodAccess ma | + pred.asExpr() = ma.getArgument(1) and + isProviderUrlSetter(ma) and + succ.asExpr() = ma.getQualifier() + ) + } +} + +/** + * A taint-tracking configuration for `simple` basic-authentication in LDAP configuration. + */ +class BasicAuthFlowConfig extends TaintTracking::Configuration { + BasicAuthFlowConfig() { this = "InsecureLdapAuth:BasicAuthFlowConfig" } + + /** Source of `simple` configuration. */ + override predicate isSource(DataFlow::Node src) { + src.asExpr().(CompileTimeConstantExpr).getStringValue() = "simple" + } + + /** Sink of directory context creation. */ + override predicate isSink(DataFlow::Node sink) { + exists(ConstructorCall cc | + cc.getConstructedType().getASupertype*() instanceof TypeDirContext and + sink.asExpr() = cc.getArgument(0) + ) + } + + /** Method call of `env.put()`. */ + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(MethodAccess ma | + pred.asExpr() = ma.getArgument(1) and + isBasicAuthEnv(ma) and + succ.asExpr() = ma.getQualifier() ) } } -from DataFlow::PathNode source, DataFlow::PathNode sink, LdapAuthFlowConfig config -where config.hasFlowPath(source, sink) +/** + * A taint-tracking configuration for `ssl` configuration in LDAP authentication. + */ +class SSLFlowConfig extends TaintTracking::Configuration { + SSLFlowConfig() { this = "InsecureLdapAuth:SSLFlowConfig" } + + /** Source of `ssl` configuration. */ + override predicate isSource(DataFlow::Node src) { + src.asExpr().(CompileTimeConstantExpr).getStringValue() = "ssl" + } + + /** Sink of directory context creation. */ + override predicate isSink(DataFlow::Node sink) { + exists(ConstructorCall cc | + cc.getConstructedType().getASupertype*() instanceof TypeDirContext and + sink.asExpr() = cc.getArgument(0) + ) + } + + /** Method call of `env.put()`. */ + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(MethodAccess ma | + pred.asExpr() = ma.getArgument(1) and + isSSLEnv(ma) and + succ.asExpr() = ma.getQualifier() + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureUrlFlowConfig config, VarAccess va +where + config.hasFlowPath(source, sink) and + sink.getNode().asExpr() = va and + exists(BasicAuthFlowConfig bc, DataFlow::PathNode source2, DataFlow::PathNode sink2 | + bc.hasFlowPath(source2, sink2) and + source2.getNode().asExpr().(CompileTimeConstantExpr).getStringValue() = "simple" and + sink2.getNode().asExpr() = va + ) and + not exists(SSLFlowConfig sc, DataFlow::PathNode source3, DataFlow::PathNode sink3 | + sc.hasFlowPath(source3, sink3) and + source3.getNode().asExpr().(CompileTimeConstantExpr).getStringValue() = "ssl" and + sink3.getNode().asExpr() = va.getVariable().getAnAccess() + ) select sink.getNode(), source, sink, "Insecure LDAP authentication from $@.", source.getNode(), "LDAP connection string" diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected index 745fd9c1b75e..1af36e67d05a 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected @@ -1,19 +1,74 @@ edges -| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | -| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | -| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:41:100:47 | ldapUrl | -| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:115:47:115:53 | ldapUrl | +| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:20:49:20:59 | environment | +| InsecureLdapAuth.java:17:52:17:59 | "simple" : String | InsecureLdapAuth.java:20:49:20:59 | environment | +| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:34:49:34:59 | environment | +| InsecureLdapAuth.java:31:52:31:59 | "simple" : String | InsecureLdapAuth.java:34:49:34:59 | environment | +| InsecureLdapAuth.java:45:52:45:59 | "simple" : String | InsecureLdapAuth.java:48:49:48:59 | environment | +| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | InsecureLdapAuth.java:63:49:63:59 | environment | +| InsecureLdapAuth.java:59:52:59:59 | "simple" : String | InsecureLdapAuth.java:63:49:63:59 | environment | +| InsecureLdapAuth.java:62:46:62:50 | "ssl" : String | InsecureLdapAuth.java:63:49:63:59 | environment | +| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:77:49:77:59 | environment | +| InsecureLdapAuth.java:88:52:88:59 | "simple" : String | InsecureLdapAuth.java:91:49:91:59 | environment | +| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:105:59:105:69 | environment | +| InsecureLdapAuth.java:102:52:102:59 | "simple" : String | InsecureLdapAuth.java:105:59:105:69 | environment | +| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:120:49:120:59 | environment | +| InsecureLdapAuth.java:117:58:117:65 | "simple" : String | InsecureLdapAuth.java:120:49:120:59 | environment | +| InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | +| InsecureLdapAuth.java:124:38:124:42 | "ssl" : String | InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | +| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | +| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | +| InsecureLdapAuth.java:128:44:128:51 | "simple" : String | InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | +| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | InsecureLdapAuth.java:142:50:142:60 | environment | +| InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment | +| InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment | +| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | InsecureLdapAuth.java:153:50:153:60 | environment | +| InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:153:50:153:60 | environment | nodes | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:15:41:15:47 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:17:52:17:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | semmle.label | ... + ... : String | -| InsecureLdapAuth.java:29:41:29:47 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:31:52:31:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:45:52:45:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:48:49:48:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | semmle.label | "ldap://ad.your-server.com:636" : String | +| InsecureLdapAuth.java:59:52:59:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:62:46:62:50 | "ssl" : String | semmle.label | "ssl" : String | +| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | +| InsecureLdapAuth.java:77:49:77:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:88:52:88:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:91:49:91:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:100:41:100:47 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:102:52:102:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment | +| InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment | | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:115:47:115:53 | ldapUrl | semmle.label | ldapUrl | +| InsecureLdapAuth.java:117:58:117:65 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment | +| InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | semmle.label | env [post update] : Hashtable | +| InsecureLdapAuth.java:124:38:124:42 | "ssl" : String | semmle.label | "ssl" : String | +| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | semmle.label | env [post update] : Hashtable | +| InsecureLdapAuth.java:128:44:128:51 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | semmle.label | ... + ... : String | +| InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | +| InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | +| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment | +| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment | +| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment | +| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | semmle.label | ... + ... : String | +| InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | +| InsecureLdapAuth.java:153:50:153:60 | environment | semmle.label | environment | +| InsecureLdapAuth.java:153:50:153:60 | environment | semmle.label | environment | #select -| InsecureLdapAuth.java:15:41:15:47 | ldapUrl | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" | LDAP connection string | -| InsecureLdapAuth.java:29:41:29:47 | ldapUrl | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:25:20:25:39 | ... + ... | LDAP connection string | -| InsecureLdapAuth.java:100:41:100:47 | ldapUrl | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:41:100:47 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" | LDAP connection string | -| InsecureLdapAuth.java:115:47:115:53 | ldapUrl | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:115:47:115:53 | ldapUrl | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:20:49:20:59 | environment | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:20:49:20:59 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:34:49:34:59 | environment | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:34:49:34:59 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:25:20:25:39 | ... + ... | LDAP connection string | +| InsecureLdapAuth.java:105:59:105:69 | environment | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:105:59:105:69 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:120:49:120:59 | environment | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:120:49:120:59 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" | LDAP connection string | +| InsecureLdapAuth.java:153:50:153:60 | environment | InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | InsecureLdapAuth.java:153:50:153:60 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:147:20:147:39 | ... + ... | LDAP connection string | diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java index 4052557d8b04..14142d31b219 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.java @@ -119,4 +119,37 @@ public void testCleartextLdapAuth4(String ldapUserName, String password) { environment.put("java.naming.security.credentials", password); DirContext dirContext = new InitialDirContext(environment); } + + private void setSSL(Hashtable env) { + env.put(Context.SECURITY_PROTOCOL, "ssl"); + } + + private void setBasicAuth(Hashtable env, String ldapUserName, String password) { + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, ldapUserName); + env.put(Context.SECURITY_CREDENTIALS, password); + } + + // GOOD - Test LDAP authentication with `ssl` configuration and basic authentication. + public void testCleartextLdapAuth5(String ldapUserName, String password, String serverName) { + String ldapUrl = "ldap://"+serverName+":389"; + Hashtable environment = new Hashtable(); + setSSL(environment); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + setBasicAuth(environment, ldapUserName, password); + DirContext dirContext = new InitialLdapContext(environment, null); + } + + // BAD - Test LDAP authentication with basic authentication. + public void testCleartextLdapAuth6(String ldapUserName, String password, String serverName) { + String ldapUrl = "ldap://"+serverName+":389"; + Hashtable environment = new Hashtable(); + environment.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + environment.put(Context.PROVIDER_URL, ldapUrl); + setBasicAuth(environment, ldapUserName, password); + DirContext dirContext = new InitialLdapContext(environment, null); + } } From 32c54628f8afc010e4000368d9abdecea6b50f7f Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 15 Jan 2021 12:32:13 +0000 Subject: [PATCH 5/9] Drop fieldName from the function for runtime evaluation --- .../Security/CWE/CWE-522/InsecureLdapAuth.ql | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql index 1a38f74092b9..602dae21ad7a 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -91,19 +91,11 @@ predicate isProviderUrlSetter(MethodAccess ma) { /** * Holds if `ma` sets `fieldValue` with attribute name `fieldName` to `envValue` in some `Hashtable`. */ -bindingset[fieldName, fieldValue, envValue] -predicate hasEnvWithValue(MethodAccess ma, string fieldName, string fieldValue, string envValue) { +bindingset[fieldValue, envValue] +predicate hasEnvWithValue(MethodAccess ma, string fieldValue, string envValue) { ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and (ma.getMethod().hasName("put") or ma.getMethod().hasName("setProperty")) and - ( - ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue - or - exists(Field f | - ma.getArgument(0) = f.getAnAccess() and - f.hasName(fieldName) and - f.getDeclaringType() instanceof TypeNamingContext - ) - ) and + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue and ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue } @@ -111,15 +103,13 @@ predicate hasEnvWithValue(MethodAccess ma, string fieldName, string fieldValue, * Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`. */ predicate isBasicAuthEnv(MethodAccess ma) { - hasEnvWithValue(ma, "SECURITY_AUTHENTICATION", "java.naming.security.authentication", "simple") + hasEnvWithValue(ma, "java.naming.security.authentication", "simple") } /** * Holds if `ma` sets `java.naming.security.protocol` (also known as `Context.SECURITY_PROTOCOL`) to `ssl` in some `Hashtable`. */ -predicate isSSLEnv(MethodAccess ma) { - hasEnvWithValue(ma, "SECURITY_PROTOCOL", "java.naming.security.protocol", "ssl") -} +predicate isSSLEnv(MethodAccess ma) { hasEnvWithValue(ma, "java.naming.security.protocol", "ssl") } /** * A taint-tracking configuration for `ldap://` URL in LDAP authentication. From 6a93099b645e32e7598999ddbcf0303f65e657fa Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 28 Jan 2021 03:02:53 +0000 Subject: [PATCH 6/9] Simplify the query and update qldoc --- .../experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp | 2 +- .../src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp index 9241b52cf19d..ee4afabbed62 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp @@ -26,7 +26,7 @@
  • Oracle: - Simple authentication consists of sending the LDAP server the fully qualified DN of the client (user) and the client's clear-text password + Simple authentication
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql index 602dae21ad7a..9563c7554107 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -202,13 +202,11 @@ where sink.getNode().asExpr() = va and exists(BasicAuthFlowConfig bc, DataFlow::PathNode source2, DataFlow::PathNode sink2 | bc.hasFlowPath(source2, sink2) and - source2.getNode().asExpr().(CompileTimeConstantExpr).getStringValue() = "simple" and sink2.getNode().asExpr() = va ) and not exists(SSLFlowConfig sc, DataFlow::PathNode source3, DataFlow::PathNode sink3 | sc.hasFlowPath(source3, sink3) and - source3.getNode().asExpr().(CompileTimeConstantExpr).getStringValue() = "ssl" and - sink3.getNode().asExpr() = va.getVariable().getAnAccess() + sink3.getNode().asExpr() = va ) select sink.getNode(), source, sink, "Insecure LDAP authentication from $@.", source.getNode(), "LDAP connection string" From 3151aeff48c41c067e1daea026edbe3d759b702b Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 2 Feb 2021 18:26:29 +0000 Subject: [PATCH 7/9] Enhance the query --- .../CWE/CWE-522/InsecureLdapAuth.java | 2 +- .../Security/CWE/CWE-522/InsecureLdapAuth.ql | 72 +++++++++---------- .../code/java/frameworks/Networking.qll | 1 + 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java index 33764506a1b5..3c5f65551004 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.java @@ -15,7 +15,7 @@ public DirContext ldapAuth(String ldapUserName, String password) { environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); environment.put(Context.PROVIDER_URL, ldapUrl); environment.put(Context.REFERRAL, "follow"); - env.put(Context.SECURITY_AUTHENTICATION, "simple"); + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); environment.put(Context.SECURITY_PRINCIPAL, ldapUserName); environment.put(Context.SECURITY_CREDENTIALS, password); DirContext dirContext = new InitialDirContext(environment); diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql index 9563c7554107..b6e7b5ed702a 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -76,7 +76,7 @@ class InsecureLdapUrl extends Expr { */ predicate isProviderUrlSetter(MethodAccess ma) { ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and - (ma.getMethod().hasName("put") or ma.getMethod().hasName("setProperty")) and + ma.getMethod().hasName(["put", "setProperty"]) and ( ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url" or @@ -89,27 +89,48 @@ predicate isProviderUrlSetter(MethodAccess ma) { } /** - * Holds if `ma` sets `fieldValue` with attribute name `fieldName` to `envValue` in some `Hashtable`. + * Holds if `ma` sets `fieldValue` to `envValue` in some `Hashtable`. */ bindingset[fieldValue, envValue] -predicate hasEnvWithValue(MethodAccess ma, string fieldValue, string envValue) { +predicate hasFieldValueEnv(MethodAccess ma, string fieldValue, string envValue) { + // environment.put("java.naming.security.authentication", "simple") ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and - (ma.getMethod().hasName("put") or ma.getMethod().hasName("setProperty")) and + ma.getMethod().hasName(["put", "setProperty"]) and ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue and ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue } +/** + * Holds if `ma` sets attribute name `fieldName` to `envValue` in some `Hashtable`. + */ +bindingset[fieldName, envValue] +predicate hasFieldNameEnv(MethodAccess ma, string fieldName, string envValue) { + // environment.put(Context.SECURITY_AUTHENTICATION, "simple") + ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and + ma.getMethod().hasName(["put", "setProperty"]) and + exists(Field f | + ma.getArgument(0) = f.getAnAccess() and + f.hasName(fieldName) and + f.getDeclaringType() instanceof TypeNamingContext + ) and + ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue +} + /** * Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`. */ predicate isBasicAuthEnv(MethodAccess ma) { - hasEnvWithValue(ma, "java.naming.security.authentication", "simple") + hasFieldValueEnv(ma, "java.naming.security.authentication", "simple") or + hasFieldNameEnv(ma, "SECURITY_AUTHENTICATION", "simple") } /** * Holds if `ma` sets `java.naming.security.protocol` (also known as `Context.SECURITY_PROTOCOL`) to `ssl` in some `Hashtable`. */ -predicate isSSLEnv(MethodAccess ma) { hasEnvWithValue(ma, "java.naming.security.protocol", "ssl") } +predicate isSSLEnv(MethodAccess ma) { + hasFieldValueEnv(ma, "java.naming.security.protocol", "ssl") or + hasFieldNameEnv(ma, "SECURITY_PROTOCOL", "ssl") +} /** * A taint-tracking configuration for `ldap://` URL in LDAP authentication. @@ -141,12 +162,12 @@ class InsecureUrlFlowConfig extends TaintTracking::Configuration { /** * A taint-tracking configuration for `simple` basic-authentication in LDAP configuration. */ -class BasicAuthFlowConfig extends TaintTracking::Configuration { +class BasicAuthFlowConfig extends DataFlow::Configuration { BasicAuthFlowConfig() { this = "InsecureLdapAuth:BasicAuthFlowConfig" } /** Source of `simple` configuration. */ override predicate isSource(DataFlow::Node src) { - src.asExpr().(CompileTimeConstantExpr).getStringValue() = "simple" + exists(MethodAccess ma | isBasicAuthEnv(ma) and ma.getQualifier() = src.asExpr()) } /** Sink of directory context creation. */ @@ -156,26 +177,17 @@ class BasicAuthFlowConfig extends TaintTracking::Configuration { sink.asExpr() = cc.getArgument(0) ) } - - /** Method call of `env.put()`. */ - override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - exists(MethodAccess ma | - pred.asExpr() = ma.getArgument(1) and - isBasicAuthEnv(ma) and - succ.asExpr() = ma.getQualifier() - ) - } } /** * A taint-tracking configuration for `ssl` configuration in LDAP authentication. */ -class SSLFlowConfig extends TaintTracking::Configuration { +class SSLFlowConfig extends DataFlow::Configuration { SSLFlowConfig() { this = "InsecureLdapAuth:SSLFlowConfig" } /** Source of `ssl` configuration. */ override predicate isSource(DataFlow::Node src) { - src.asExpr().(CompileTimeConstantExpr).getStringValue() = "ssl" + exists(MethodAccess ma | isSSLEnv(ma) and ma.getQualifier() = src.asExpr()) } /** Sink of directory context creation. */ @@ -185,28 +197,12 @@ class SSLFlowConfig extends TaintTracking::Configuration { sink.asExpr() = cc.getArgument(0) ) } - - /** Method call of `env.put()`. */ - override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - exists(MethodAccess ma | - pred.asExpr() = ma.getArgument(1) and - isSSLEnv(ma) and - succ.asExpr() = ma.getQualifier() - ) - } } -from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureUrlFlowConfig config, VarAccess va +from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureUrlFlowConfig config where config.hasFlowPath(source, sink) and - sink.getNode().asExpr() = va and - exists(BasicAuthFlowConfig bc, DataFlow::PathNode source2, DataFlow::PathNode sink2 | - bc.hasFlowPath(source2, sink2) and - sink2.getNode().asExpr() = va - ) and - not exists(SSLFlowConfig sc, DataFlow::PathNode source3, DataFlow::PathNode sink3 | - sc.hasFlowPath(source3, sink3) and - sink3.getNode().asExpr() = va - ) + exists(BasicAuthFlowConfig bc | bc.hasFlowTo(sink.getNode())) and + not exists(SSLFlowConfig sc | sc.hasFlowTo(sink.getNode())) select sink.getNode(), source, sink, "Insecure LDAP authentication from $@.", source.getNode(), "LDAP connection string" diff --git a/java/ql/src/semmle/code/java/frameworks/Networking.qll b/java/ql/src/semmle/code/java/frameworks/Networking.qll index 997b80754036..cad948ed2f46 100644 --- a/java/ql/src/semmle/code/java/frameworks/Networking.qll +++ b/java/ql/src/semmle/code/java/frameworks/Networking.qll @@ -132,6 +132,7 @@ class UrlOpenConnectionMethod extends Method { /** * A string matching private host names of IPv4 and IPv6, which only matches the host portion therefore checking for port is not necessary. + * Several examples are localhost, reserved IPv4 IP addresses including 127.0.0.1, 10.x.x.x, 172.16.x,x, 192.168.x,x, and reserved IPv6 addresses including [0:0:0:0:0:0:0:1] and [::1] */ class PrivateHostName extends string { bindingset[this] From 2ace10fcdfb54ee3fe7f93a5cdddfd99aedfa534 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 3 Feb 2021 12:21:31 +0000 Subject: [PATCH 8/9] Use PostUpdateNode for wrapper method calls --- .../Security/CWE/CWE-522/InsecureLdapAuth.ql | 9 +++-- .../CWE-522/InsecureLdapAuth.expected | 36 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql index b6e7b5ed702a..8411a128c9c4 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql @@ -9,6 +9,7 @@ */ import java +import DataFlow import semmle.code.java.frameworks.Jndi import semmle.code.java.frameworks.Networking import semmle.code.java.dataflow.TaintTracking @@ -167,7 +168,9 @@ class BasicAuthFlowConfig extends DataFlow::Configuration { /** Source of `simple` configuration. */ override predicate isSource(DataFlow::Node src) { - exists(MethodAccess ma | isBasicAuthEnv(ma) and ma.getQualifier() = src.asExpr()) + exists(MethodAccess ma | + isBasicAuthEnv(ma) and ma.getQualifier() = src.(PostUpdateNode).getPreUpdateNode().asExpr() + ) } /** Sink of directory context creation. */ @@ -187,7 +190,9 @@ class SSLFlowConfig extends DataFlow::Configuration { /** Source of `ssl` configuration. */ override predicate isSource(DataFlow::Node src) { - exists(MethodAccess ma | isSSLEnv(ma) and ma.getQualifier() = src.asExpr()) + exists(MethodAccess ma | + isSSLEnv(ma) and ma.getQualifier() = src.(PostUpdateNode).getPreUpdateNode().asExpr() + ) } /** Sink of directory context creation. */ diff --git a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected index 1af36e67d05a..863e8e55dcf4 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-522/InsecureLdapAuth.expected @@ -1,23 +1,21 @@ edges | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:20:49:20:59 | environment | -| InsecureLdapAuth.java:17:52:17:59 | "simple" : String | InsecureLdapAuth.java:20:49:20:59 | environment | +| InsecureLdapAuth.java:17:3:17:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:20:49:20:59 | environment | | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:34:49:34:59 | environment | -| InsecureLdapAuth.java:31:52:31:59 | "simple" : String | InsecureLdapAuth.java:34:49:34:59 | environment | -| InsecureLdapAuth.java:45:52:45:59 | "simple" : String | InsecureLdapAuth.java:48:49:48:59 | environment | +| InsecureLdapAuth.java:31:3:31:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:34:49:34:59 | environment | +| InsecureLdapAuth.java:45:3:45:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:48:49:48:59 | environment | | InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | InsecureLdapAuth.java:63:49:63:59 | environment | -| InsecureLdapAuth.java:59:52:59:59 | "simple" : String | InsecureLdapAuth.java:63:49:63:59 | environment | -| InsecureLdapAuth.java:62:46:62:50 | "ssl" : String | InsecureLdapAuth.java:63:49:63:59 | environment | +| InsecureLdapAuth.java:59:3:59:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:63:49:63:59 | environment | +| InsecureLdapAuth.java:62:3:62:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:63:49:63:59 | environment | | InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:77:49:77:59 | environment | -| InsecureLdapAuth.java:88:52:88:59 | "simple" : String | InsecureLdapAuth.java:91:49:91:59 | environment | +| InsecureLdapAuth.java:88:3:88:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:91:49:91:59 | environment | | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:105:59:105:69 | environment | -| InsecureLdapAuth.java:102:52:102:59 | "simple" : String | InsecureLdapAuth.java:105:59:105:69 | environment | +| InsecureLdapAuth.java:102:3:102:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:105:59:105:69 | environment | | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:120:49:120:59 | environment | -| InsecureLdapAuth.java:117:58:117:65 | "simple" : String | InsecureLdapAuth.java:120:49:120:59 | environment | +| InsecureLdapAuth.java:117:3:117:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:120:49:120:59 | environment | | InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | -| InsecureLdapAuth.java:124:38:124:42 | "ssl" : String | InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | | InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | | InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | -| InsecureLdapAuth.java:128:44:128:51 | "simple" : String | InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | | InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | InsecureLdapAuth.java:142:50:142:60 | environment | | InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment | | InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment | @@ -25,37 +23,35 @@ edges | InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:153:50:153:60 | environment | nodes | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:17:52:17:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:17:3:17:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | semmle.label | ... + ... : String | -| InsecureLdapAuth.java:31:52:31:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:31:3:31:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment | -| InsecureLdapAuth.java:45:52:45:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:45:3:45:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:48:49:48:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | semmle.label | "ldap://ad.your-server.com:636" : String | -| InsecureLdapAuth.java:59:52:59:59 | "simple" : String | semmle.label | "simple" : String | -| InsecureLdapAuth.java:62:46:62:50 | "ssl" : String | semmle.label | "ssl" : String | +| InsecureLdapAuth.java:59:3:59:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | +| InsecureLdapAuth.java:62:3:62:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | | InsecureLdapAuth.java:77:49:77:59 | environment | semmle.label | environment | -| InsecureLdapAuth.java:88:52:88:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:88:3:88:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:91:49:91:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:102:52:102:59 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:102:3:102:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment | | InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment | | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String | -| InsecureLdapAuth.java:117:58:117:65 | "simple" : String | semmle.label | "simple" : String | +| InsecureLdapAuth.java:117:3:117:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment | | InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | semmle.label | env [post update] : Hashtable | -| InsecureLdapAuth.java:124:38:124:42 | "ssl" : String | semmle.label | "ssl" : String | | InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | semmle.label | env [post update] : Hashtable | -| InsecureLdapAuth.java:128:44:128:51 | "simple" : String | semmle.label | "simple" : String | | InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | semmle.label | ... + ... : String | | InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | | InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable | From 724c3e00e02be120f2a48630c2bc6ab6dfa9fd32 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 3 Feb 2021 16:45:15 +0000 Subject: [PATCH 9/9] Update help file --- .../experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp index ee4afabbed62..c729759a06e5 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-522/InsecureLdapAuth.qhelp @@ -15,11 +15,6 @@ -
  • - CWE: - CWE-522: Insufficiently Protected Credentials - CWE-319: Cleartext Transmission of Sensitive Information -
  • Oracle: LDAP and LDAPS URLs