diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.java b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.java new file mode 100644 index 000000000000..ce75fdab6be9 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.java @@ -0,0 +1,21 @@ +import javax.naming.Context; +import javax.naming.InitialContext; + +public void jndiLookup(HttpServletRequest request) throws NamingException { + String name = request.getParameter("name"); + + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + env.put(Context.PROVIDER_URL, "rmi://trusted-server:1099"); + InitialContext ctx = new InitialContext(env); + + // BAD: User input used in lookup + ctx.lookup(name); + + // GOOD: The name is validated before being used in lookup + if (isValid(name)) { + ctx.lookup(name); + } else { + // Reject the request + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.qhelp new file mode 100644 index 000000000000..d1d7b2ba51fd --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.qhelp @@ -0,0 +1,36 @@ + + + +

The Java Naming and Directory Interface (JNDI) is a Java API for a directory service that allows +Java software clients to discover and look up data and resources (in the form of Java objects) via +a name. If the name being used to look up the data is controlled by the user, it can point to a +malicious server, which can return an arbitrary object. In the worst case, this can allow remote +code execution.

+
+ + +

The general recommendation is to not pass untrusted data to the InitialContext.lookup + method. If the name being used to look up the object must be provided by the user, make +sure that it's not in the form of an absolute URL or that it's the URL pointing to a trused server. +

+
+ + +

In the following examples, the code accepts a name from the user, which it uses to look up an +object.

+ +

In the first example, the user provided name is used to look up an object.

+ +

The second example validates the name before using it to look up an object.

+ + +
+ + +
  • Oracle: Java Naming and Directory Interface (JNDI).
  • +
  • Black Hat materials: A Journey from JNDI/LDAP Manipulation to Remote Code Execution Dream Land.
  • +
  • Veracode: Exploiting JNDI Injections in Java.
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.ql new file mode 100644 index 000000000000..2b1af37dcae0 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjection.ql @@ -0,0 +1,21 @@ +/** + * @name JNDI lookup with user-controlled name + * @description Doing a JNDI lookup with user-controlled name can lead to download an untrusted + * object and to execution of arbitrary code. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/jndi-injection + * @tags security + * external/cwe/cwe-074 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import JndiInjectionLib +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, JndiInjectionFlowConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "JNDI lookup might include name from $@.", source.getNode(), + "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll new file mode 100644 index 000000000000..6cca28872a34 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll @@ -0,0 +1,261 @@ +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow +import experimental.semmle.code.java.frameworks.Jndi +import experimental.semmle.code.java.frameworks.spring.SpringJndi +import semmle.code.java.frameworks.SpringLdap +import experimental.semmle.code.java.frameworks.Shiro + +/** + * A taint-tracking configuration for unvalidated user input that is used in JNDI lookup. + */ +class JndiInjectionFlowConfig extends TaintTracking::Configuration { + JndiInjectionFlowConfig() { this = "JndiInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof JndiInjectionSink } + + override predicate isSanitizer(DataFlow::Node node) { + node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + nameStep(node1, node2) or + jmxServiceUrlStep(node1, node2) or + jmxConnectorStep(node1, node2) or + rmiConnectorStep(node1, node2) + } +} + +/** The class `java.util.Hashtable`. */ +class TypeHashtable extends Class { + TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") } +} + +/** The class `javax.naming.directory.SearchControls`. */ +class TypeSearchControls extends Class { + TypeSearchControls() { this.hasQualifiedName("javax.naming.directory", "SearchControls") } +} + +/** + * The interface `org.springframework.ldap.core.LdapOperations` (spring-ldap 1.2.x and newer) or + * `org.springframework.ldap.LdapOperations` (spring-ldap 1.1.x). + */ +class TypeSpringLdapOperations extends Interface { + TypeSpringLdapOperations() { + this.hasQualifiedName("org.springframework.ldap.core", "LdapOperations") or + this.hasQualifiedName("org.springframework.ldap", "LdapOperations") + } +} + +/** + * The interface `org.springframework.ldap.core.ContextMapper` (spring-ldap 1.2.x and newer) or + * `org.springframework.ldap.ContextMapper` (spring-ldap 1.1.x). + */ +class TypeSpringContextMapper extends Interface { + TypeSpringContextMapper() { + this.getSourceDeclaration().hasQualifiedName("org.springframework.ldap.core", "ContextMapper") or + this.getSourceDeclaration().hasQualifiedName("org.springframework.ldap", "ContextMapper") + } +} + +/** The interface `javax.management.remote.JMXConnector`. */ +class TypeJMXConnector extends Interface { + TypeJMXConnector() { this.hasQualifiedName("javax.management.remote", "JMXConnector") } +} + +/** The class `javax.management.remote.rmi.RMIConnector`. */ +class TypeRMIConnector extends Class { + TypeRMIConnector() { this.hasQualifiedName("javax.management.remote.rmi", "RMIConnector") } +} + +/** The class `javax.management.remote.JMXConnectorFactory`. */ +class TypeJMXConnectorFactory extends Class { + TypeJMXConnectorFactory() { + this.hasQualifiedName("javax.management.remote", "JMXConnectorFactory") + } +} + +/** The class `javax.management.remote.JMXServiceURL`. */ +class TypeJMXServiceURL extends Class { + TypeJMXServiceURL() { this.hasQualifiedName("javax.management.remote", "JMXServiceURL") } +} + +/** The interface `javax.naming.Context`. */ +class TypeNamingContext extends Interface { + TypeNamingContext() { this.hasQualifiedName("javax.naming", "Context") } +} + +/** + * JNDI sink for JNDI injection vulnerabilities, i.e. 1st argument to `lookup`, `lookupLink`, + * `doLookup`, `rename`, `list` or `listBindings` method from `InitialContext`. + */ +predicate jndiSinkMethod(Method m, int index) { + m.getDeclaringType().getAnAncestor() instanceof TypeInitialContext and + ( + m.hasName("lookup") or + m.hasName("lookupLink") or + m.hasName("doLookup") or + m.hasName("rename") or + m.hasName("list") or + m.hasName("listBindings") + ) and + index = 0 +} + +/** + * Spring sink for JNDI injection vulnerabilities, i.e. 1st argument to `lookup` method from + * Spring's `JndiTemplate`. + */ +predicate springJndiTemplateSinkMethod(Method m, int index) { + m.getDeclaringType() instanceof TypeSpringJndiTemplate and + m.hasName("lookup") and + index = 0 +} + +/** + * Spring sink for JNDI injection vulnerabilities, i.e. 1st argument to `lookup`, `lookupContext`, + * `findByDn`, `rename`, `list`, `listBindings`, `unbind`, `search` or `searchForObject` method + * from Spring's `LdapOperations`. + */ +predicate springLdapTemplateSinkMethod(MethodAccess ma, Method m, int index) { + m.getDeclaringType().getAnAncestor() instanceof TypeSpringLdapOperations and + ( + m.hasName("lookup") + or + m.hasName("lookupContext") + or + m.hasName("findByDn") + or + m.hasName("rename") + or + m.hasName("list") + or + m.hasName("listBindings") + or + m.hasName("unbind") and ma.getArgument(1).(CompileTimeConstantExpr).getBooleanValue() = true + or + m.getName().matches("search%") and + m.getParameterType(m.getNumberOfParameters() - 1) instanceof TypeSpringContextMapper and + not m.getAParamType() instanceof TypeSearchControls + or + m.hasName("search") and ma.getArgument(3).(CompileTimeConstantExpr).getBooleanValue() = true + ) and + index = 0 +} + +/** + * Apache Shiro sink for JNDI injection vulnerabilities, i.e. 1st argument to `lookup` method from + * Shiro's `JndiTemplate`. + */ +predicate shiroSinkMethod(Method m, int index) { + m.getDeclaringType() instanceof TypeShiroJndiTemplate and + m.hasName("lookup") and + index = 0 +} + +/** + * `JMXConnectorFactory` sink for JNDI injection vulnerabilities, i.e. 1st argument to `connect` + * method from `JMXConnectorFactory`. + */ +predicate jmxConnectorFactorySinkMethod(Method m, int index) { + m.getDeclaringType() instanceof TypeJMXConnectorFactory and + m.hasName("connect") and + index = 0 +} + +/** + * 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 providerUrlEnv(MethodAccess ma, Method m, int index) { + m.getDeclaringType().getAnAncestor() instanceof TypeHashtable and + (m.hasName("put") or m.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 + ) + ) and + index = 1 +} + +/** Holds if parameter at index `index` in method `m` is JNDI injection sink. */ +predicate jndiInjectionSinkMethod(MethodAccess ma, Method m, int index) { + jndiSinkMethod(m, index) or + springJndiTemplateSinkMethod(m, index) or + springLdapTemplateSinkMethod(ma, m, index) or + shiroSinkMethod(m, index) or + jmxConnectorFactorySinkMethod(m, index) or + providerUrlEnv(ma, m, index) +} + +/** A data flow sink for unvalidated user input that is used in JNDI lookup. */ +class JndiInjectionSink extends DataFlow::ExprNode { + JndiInjectionSink() { + exists(MethodAccess ma, Method m, int index | + ma.getMethod() = m and + ma.getArgument(index) = this.getExpr() and + jndiInjectionSinkMethod(ma, m, index) + ) + or + exists(MethodAccess ma, Method m | + ma.getMethod() = m and + ma.getQualifier() = this.getExpr() and + m.getDeclaringType().getAnAncestor() instanceof TypeJMXConnector and + m.hasName("connect") + ) + } +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `String` and `CompositeName` or + * `CompoundName`, i.e. `new CompositeName(tainted)` or `new CompoundName(tainted)`. + */ +predicate nameStep(ExprNode n1, ExprNode n2) { + exists(ConstructorCall cc | + cc.getConstructedType() instanceof TypeCompositeName or + cc.getConstructedType() instanceof TypeCompoundName + | + n1.asExpr() = cc.getAnArgument() and + n2.asExpr() = cc + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `String` and `JMXServiceURL`, + * i.e. `new JMXServiceURL(tainted)`. + */ +predicate jmxServiceUrlStep(ExprNode n1, ExprNode n2) { + exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeJMXServiceURL | + n1.asExpr() = cc.getAnArgument() and + n2.asExpr() = cc + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `JMXServiceURL` and + * `JMXConnector`, i.e. `JMXConnectorFactory.newJMXConnector(tainted)`. + */ +predicate jmxConnectorStep(ExprNode n1, ExprNode n2) { + exists(MethodAccess ma, Method m | n1.asExpr() = ma.getArgument(0) and n2.asExpr() = ma | + ma.getMethod() = m and + m.getDeclaringType() instanceof TypeJMXConnectorFactory and + m.hasName("newJMXConnector") + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `JMXServiceURL` and + * `RMIConnector`, i.e. `new RMIConnector(tainted)`. + */ +predicate rmiConnectorStep(ExprNode n1, ExprNode n2) { + exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeRMIConnector | + n1.asExpr() = cc.getAnArgument() and + n2.asExpr() = cc + ) +} diff --git a/java/ql/src/experimental/semmle/code/java/frameworks/Jndi.qll b/java/ql/src/experimental/semmle/code/java/frameworks/Jndi.qll new file mode 100644 index 000000000000..73b5e1a4f9fc --- /dev/null +++ b/java/ql/src/experimental/semmle/code/java/frameworks/Jndi.qll @@ -0,0 +1,16 @@ +import java + +/** The class `javax.naming.InitialContext`. */ +class TypeInitialContext extends Class { + TypeInitialContext() { this.hasQualifiedName("javax.naming", "InitialContext") } +} + +/** The class `javax.naming.CompositeName`. */ +class TypeCompositeName extends Class { + TypeCompositeName() { this.hasQualifiedName("javax.naming", "CompositeName") } +} + +/** The class `javax.naming.CompoundName`. */ +class TypeCompoundName extends Class { + TypeCompoundName() { this.hasQualifiedName("javax.naming", "CompoundName") } +} diff --git a/java/ql/src/experimental/semmle/code/java/frameworks/Shiro.qll b/java/ql/src/experimental/semmle/code/java/frameworks/Shiro.qll new file mode 100644 index 000000000000..55e42f14fcfa --- /dev/null +++ b/java/ql/src/experimental/semmle/code/java/frameworks/Shiro.qll @@ -0,0 +1,6 @@ +import java + +/** The class `org.apache.shiro.jndi.JndiTemplate`. */ +class TypeShiroJndiTemplate extends Class { + TypeShiroJndiTemplate() { this.hasQualifiedName("org.apache.shiro.jndi", "JndiTemplate") } +} diff --git a/java/ql/src/experimental/semmle/code/java/frameworks/spring/SpringJndi.qll b/java/ql/src/experimental/semmle/code/java/frameworks/spring/SpringJndi.qll new file mode 100644 index 000000000000..6033e359b171 --- /dev/null +++ b/java/ql/src/experimental/semmle/code/java/frameworks/spring/SpringJndi.qll @@ -0,0 +1,6 @@ +import java + +/** The class `org.springframework.jndi.JndiTemplate`. */ +class TypeSpringJndiTemplate extends Class { + TypeSpringJndiTemplate() { this.hasQualifiedName("org.springframework.jndi", "JndiTemplate") } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.expected new file mode 100644 index 000000000000..fe6677ccf575 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.expected @@ -0,0 +1,180 @@ +edges +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:30:16:30:22 | nameStr | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:31:20:31:26 | nameStr | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:32:29:32:35 | nameStr | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:33:16:33:22 | nameStr | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:34:14:34:20 | nameStr | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:35:22:35:28 | nameStr | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:37:16:37:19 | name | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:38:20:38:23 | name | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:39:29:39:32 | name | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:40:16:40:19 | name | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:41:14:41:17 | name | +| JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:42:22:42:25 | name | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:49:16:49:22 | nameStr | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:50:20:50:26 | nameStr | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:51:16:51:22 | nameStr | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:52:14:52:20 | nameStr | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:53:22:53:28 | nameStr | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:55:16:55:19 | name | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:56:20:56:23 | name | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:57:16:57:19 | name | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:58:14:58:17 | name | +| JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:59:22:59:25 | name | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:66:16:66:22 | nameStr | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:67:20:67:26 | nameStr | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:68:16:68:22 | nameStr | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:69:14:69:20 | nameStr | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:70:22:70:28 | nameStr | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:72:16:72:19 | name | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:73:20:73:23 | name | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:74:16:74:19 | name | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:75:14:75:17 | name | +| JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:76:22:76:25 | name | +| JndiInjection.java:79:42:79:69 | nameStr : String | JndiInjection.java:82:16:82:22 | nameStr | +| JndiInjection.java:79:42:79:69 | nameStr : String | JndiInjection.java:83:16:83:22 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:90:16:90:22 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:91:23:91:29 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:92:18:92:21 | name | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:93:16:93:19 | name | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:94:14:94:17 | name | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:95:22:95:25 | name | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:96:16:96:22 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:98:16:98:22 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:99:16:99:22 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:100:16:100:22 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:101:16:101:22 | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:103:25:103:31 | nameStr | +| JndiInjection.java:106:41:106:68 | nameStr : String | JndiInjection.java:109:16:109:22 | nameStr | +| JndiInjection.java:106:41:106:68 | nameStr : String | JndiInjection.java:110:16:110:22 | nameStr | +| JndiInjection.java:113:37:113:63 | urlStr : String | JndiInjection.java:114:33:114:57 | new JMXServiceURL(...) | +| JndiInjection.java:113:37:113:63 | urlStr : String | JndiInjection.java:118:5:118:13 | connector | +| JndiInjection.java:121:27:121:53 | urlStr : String | JndiInjection.java:124:35:124:40 | urlStr | +| JndiInjection.java:128:27:128:53 | urlStr : String | JndiInjection.java:131:41:131:46 | urlStr | +| JndiInjection.java:135:52:135:78 | urlStr : String | JndiInjection.java:138:37:138:42 | urlStr | +| JndiInjection.java:142:52:142:78 | urlStr : String | JndiInjection.java:145:51:145:56 | urlStr | +| JndiInjection.java:149:52:149:78 | urlStr : String | JndiInjection.java:152:51:152:56 | urlStr | +nodes +| JndiInjection.java:26:38:26:65 | nameStr : String | semmle.label | nameStr : String | +| JndiInjection.java:30:16:30:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:31:20:31:26 | nameStr | semmle.label | nameStr | +| JndiInjection.java:32:29:32:35 | nameStr | semmle.label | nameStr | +| JndiInjection.java:33:16:33:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:34:14:34:20 | nameStr | semmle.label | nameStr | +| JndiInjection.java:35:22:35:28 | nameStr | semmle.label | nameStr | +| JndiInjection.java:37:16:37:19 | name | semmle.label | name | +| JndiInjection.java:38:20:38:23 | name | semmle.label | name | +| JndiInjection.java:39:29:39:32 | name | semmle.label | name | +| JndiInjection.java:40:16:40:19 | name | semmle.label | name | +| JndiInjection.java:41:14:41:17 | name | semmle.label | name | +| JndiInjection.java:42:22:42:25 | name | semmle.label | name | +| JndiInjection.java:45:41:45:68 | nameStr : String | semmle.label | nameStr : String | +| JndiInjection.java:49:16:49:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:50:20:50:26 | nameStr | semmle.label | nameStr | +| JndiInjection.java:51:16:51:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:52:14:52:20 | nameStr | semmle.label | nameStr | +| JndiInjection.java:53:22:53:28 | nameStr | semmle.label | nameStr | +| JndiInjection.java:55:16:55:19 | name | semmle.label | name | +| JndiInjection.java:56:20:56:23 | name | semmle.label | name | +| JndiInjection.java:57:16:57:19 | name | semmle.label | name | +| JndiInjection.java:58:14:58:17 | name | semmle.label | name | +| JndiInjection.java:59:22:59:25 | name | semmle.label | name | +| JndiInjection.java:62:42:62:69 | nameStr : String | semmle.label | nameStr : String | +| JndiInjection.java:66:16:66:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:67:20:67:26 | nameStr | semmle.label | nameStr | +| JndiInjection.java:68:16:68:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:69:14:69:20 | nameStr | semmle.label | nameStr | +| JndiInjection.java:70:22:70:28 | nameStr | semmle.label | nameStr | +| JndiInjection.java:72:16:72:19 | name | semmle.label | name | +| JndiInjection.java:73:20:73:23 | name | semmle.label | name | +| JndiInjection.java:74:16:74:19 | name | semmle.label | name | +| JndiInjection.java:75:14:75:17 | name | semmle.label | name | +| JndiInjection.java:76:22:76:25 | name | semmle.label | name | +| JndiInjection.java:79:42:79:69 | nameStr : String | semmle.label | nameStr : String | +| JndiInjection.java:82:16:82:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:83:16:83:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:86:42:86:69 | nameStr : String | semmle.label | nameStr : String | +| JndiInjection.java:90:16:90:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:91:23:91:29 | nameStr | semmle.label | nameStr | +| JndiInjection.java:92:18:92:21 | name | semmle.label | name | +| JndiInjection.java:93:16:93:19 | name | semmle.label | name | +| JndiInjection.java:94:14:94:17 | name | semmle.label | name | +| JndiInjection.java:95:22:95:25 | name | semmle.label | name | +| JndiInjection.java:96:16:96:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:98:16:98:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:99:16:99:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:100:16:100:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:101:16:101:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:103:25:103:31 | nameStr | semmle.label | nameStr | +| JndiInjection.java:106:41:106:68 | nameStr : String | semmle.label | nameStr : String | +| JndiInjection.java:109:16:109:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:110:16:110:22 | nameStr | semmle.label | nameStr | +| JndiInjection.java:113:37:113:63 | urlStr : String | semmle.label | urlStr : String | +| JndiInjection.java:114:33:114:57 | new JMXServiceURL(...) | semmle.label | new JMXServiceURL(...) | +| JndiInjection.java:118:5:118:13 | connector | semmle.label | connector | +| JndiInjection.java:121:27:121:53 | urlStr : String | semmle.label | urlStr : String | +| JndiInjection.java:124:35:124:40 | urlStr | semmle.label | urlStr | +| JndiInjection.java:128:27:128:53 | urlStr : String | semmle.label | urlStr : String | +| JndiInjection.java:131:41:131:46 | urlStr | semmle.label | urlStr | +| JndiInjection.java:135:52:135:78 | urlStr : String | semmle.label | urlStr : String | +| JndiInjection.java:138:37:138:42 | urlStr | semmle.label | urlStr | +| JndiInjection.java:142:52:142:78 | urlStr : String | semmle.label | urlStr : String | +| JndiInjection.java:145:51:145:56 | urlStr | semmle.label | urlStr | +| JndiInjection.java:149:52:149:78 | urlStr : String | semmle.label | urlStr : String | +| JndiInjection.java:152:51:152:56 | urlStr | semmle.label | urlStr | +#select +| JndiInjection.java:30:16:30:22 | nameStr | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:30:16:30:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:31:20:31:26 | nameStr | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:31:20:31:26 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:32:29:32:35 | nameStr | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:32:29:32:35 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:33:16:33:22 | nameStr | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:33:16:33:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:34:14:34:20 | nameStr | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:34:14:34:20 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:35:22:35:28 | nameStr | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:35:22:35:28 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:37:16:37:19 | name | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:37:16:37:19 | name | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:38:20:38:23 | name | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:38:20:38:23 | name | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:39:29:39:32 | name | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:39:29:39:32 | name | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:40:16:40:19 | name | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:40:16:40:19 | name | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:41:14:41:17 | name | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:41:14:41:17 | name | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:42:22:42:25 | name | JndiInjection.java:26:38:26:65 | nameStr : String | JndiInjection.java:42:22:42:25 | name | JNDI lookup might include name from $@. | JndiInjection.java:26:38:26:65 | nameStr | this user input | +| JndiInjection.java:49:16:49:22 | nameStr | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:49:16:49:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:50:20:50:26 | nameStr | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:50:20:50:26 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:51:16:51:22 | nameStr | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:51:16:51:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:52:14:52:20 | nameStr | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:52:14:52:20 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:53:22:53:28 | nameStr | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:53:22:53:28 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:55:16:55:19 | name | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:55:16:55:19 | name | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:56:20:56:23 | name | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:56:20:56:23 | name | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:57:16:57:19 | name | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:57:16:57:19 | name | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:58:14:58:17 | name | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:58:14:58:17 | name | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:59:22:59:25 | name | JndiInjection.java:45:41:45:68 | nameStr : String | JndiInjection.java:59:22:59:25 | name | JNDI lookup might include name from $@. | JndiInjection.java:45:41:45:68 | nameStr | this user input | +| JndiInjection.java:66:16:66:22 | nameStr | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:66:16:66:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:67:20:67:26 | nameStr | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:67:20:67:26 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:68:16:68:22 | nameStr | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:68:16:68:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:69:14:69:20 | nameStr | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:69:14:69:20 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:70:22:70:28 | nameStr | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:70:22:70:28 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:72:16:72:19 | name | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:72:16:72:19 | name | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:73:20:73:23 | name | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:73:20:73:23 | name | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:74:16:74:19 | name | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:74:16:74:19 | name | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:75:14:75:17 | name | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:75:14:75:17 | name | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:76:22:76:25 | name | JndiInjection.java:62:42:62:69 | nameStr : String | JndiInjection.java:76:22:76:25 | name | JNDI lookup might include name from $@. | JndiInjection.java:62:42:62:69 | nameStr | this user input | +| JndiInjection.java:82:16:82:22 | nameStr | JndiInjection.java:79:42:79:69 | nameStr : String | JndiInjection.java:82:16:82:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:79:42:79:69 | nameStr | this user input | +| JndiInjection.java:83:16:83:22 | nameStr | JndiInjection.java:79:42:79:69 | nameStr : String | JndiInjection.java:83:16:83:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:79:42:79:69 | nameStr | this user input | +| JndiInjection.java:90:16:90:22 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:90:16:90:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:91:23:91:29 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:91:23:91:29 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:92:18:92:21 | name | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:92:18:92:21 | name | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:93:16:93:19 | name | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:93:16:93:19 | name | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:94:14:94:17 | name | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:94:14:94:17 | name | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:95:22:95:25 | name | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:95:22:95:25 | name | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:96:16:96:22 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:96:16:96:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:98:16:98:22 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:98:16:98:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:99:16:99:22 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:99:16:99:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:100:16:100:22 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:100:16:100:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:101:16:101:22 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:101:16:101:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:103:25:103:31 | nameStr | JndiInjection.java:86:42:86:69 | nameStr : String | JndiInjection.java:103:25:103:31 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:86:42:86:69 | nameStr | this user input | +| JndiInjection.java:109:16:109:22 | nameStr | JndiInjection.java:106:41:106:68 | nameStr : String | JndiInjection.java:109:16:109:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:106:41:106:68 | nameStr | this user input | +| JndiInjection.java:110:16:110:22 | nameStr | JndiInjection.java:106:41:106:68 | nameStr : String | JndiInjection.java:110:16:110:22 | nameStr | JNDI lookup might include name from $@. | JndiInjection.java:106:41:106:68 | nameStr | this user input | +| JndiInjection.java:114:33:114:57 | new JMXServiceURL(...) | JndiInjection.java:113:37:113:63 | urlStr : String | JndiInjection.java:114:33:114:57 | new JMXServiceURL(...) | JNDI lookup might include name from $@. | JndiInjection.java:113:37:113:63 | urlStr | this user input | +| JndiInjection.java:118:5:118:13 | connector | JndiInjection.java:113:37:113:63 | urlStr : String | JndiInjection.java:118:5:118:13 | connector | JNDI lookup might include name from $@. | JndiInjection.java:113:37:113:63 | urlStr | this user input | +| JndiInjection.java:124:35:124:40 | urlStr | JndiInjection.java:121:27:121:53 | urlStr : String | JndiInjection.java:124:35:124:40 | urlStr | JNDI lookup might include name from $@. | JndiInjection.java:121:27:121:53 | urlStr | this user input | +| JndiInjection.java:131:41:131:46 | urlStr | JndiInjection.java:128:27:128:53 | urlStr : String | JndiInjection.java:131:41:131:46 | urlStr | JNDI lookup might include name from $@. | JndiInjection.java:128:27:128:53 | urlStr | this user input | +| JndiInjection.java:138:37:138:42 | urlStr | JndiInjection.java:135:52:135:78 | urlStr : String | JndiInjection.java:138:37:138:42 | urlStr | JNDI lookup might include name from $@. | JndiInjection.java:135:52:135:78 | urlStr | this user input | +| JndiInjection.java:145:51:145:56 | urlStr | JndiInjection.java:142:52:142:78 | urlStr : String | JndiInjection.java:145:51:145:56 | urlStr | JNDI lookup might include name from $@. | JndiInjection.java:142:52:142:78 | urlStr | this user input | +| JndiInjection.java:152:51:152:56 | urlStr | JndiInjection.java:149:52:149:78 | urlStr : String | JndiInjection.java:152:51:152:56 | urlStr | JNDI lookup might include name from $@. | JndiInjection.java:149:52:149:78 | urlStr | this user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.java b/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.java new file mode 100644 index 000000000000..a169eb4a3ac6 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.java @@ -0,0 +1,191 @@ +import java.io.IOException; +import java.util.Hashtable; +import java.util.Properties; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import javax.naming.CompositeName; +import javax.naming.CompoundName; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.ldap.InitialLdapContext; + +import org.springframework.jndi.JndiTemplate; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.NameClassPairCallbackHandler; +import org.springframework.web.bind.annotation.RequestParam; + +public class JndiInjection { + public void testInitialContextBad1(@RequestParam String nameStr) throws NamingException { + Name name = new CompositeName(nameStr); + InitialContext ctx = new InitialContext(); + + ctx.lookup(nameStr); + ctx.lookupLink(nameStr); + InitialContext.doLookup(nameStr); + ctx.rename(nameStr, ""); + ctx.list(nameStr); + ctx.listBindings(nameStr); + + ctx.lookup(name); + ctx.lookupLink(name); + InitialContext.doLookup(name); + ctx.rename(name, null); + ctx.list(name); + ctx.listBindings(name); + } + + public void testInitialDirContextBad1(@RequestParam String nameStr) throws NamingException { + Name name = new CompoundName(nameStr, new Properties()); + InitialDirContext ctx = new InitialDirContext(); + + ctx.lookup(nameStr); + ctx.lookupLink(nameStr); + ctx.rename(nameStr, ""); + ctx.list(nameStr); + ctx.listBindings(nameStr); + + ctx.lookup(name); + ctx.lookupLink(name); + ctx.rename(name, null); + ctx.list(name); + ctx.listBindings(name); + } + + public void testInitialLdapContextBad1(@RequestParam String nameStr) throws NamingException { + Name name = new CompositeName(nameStr); + InitialLdapContext ctx = new InitialLdapContext(); + + ctx.lookup(nameStr); + ctx.lookupLink(nameStr); + ctx.rename(nameStr, ""); + ctx.list(nameStr); + ctx.listBindings(nameStr); + + ctx.lookup(name); + ctx.lookupLink(name); + ctx.rename(name, null); + ctx.list(name); + ctx.listBindings(name); + } + + public void testSpringJndiTemplateBad1(@RequestParam String nameStr) throws NamingException { + JndiTemplate ctx = new JndiTemplate(); + + ctx.lookup(nameStr); + ctx.lookup(nameStr, null); + } + + public void testSpringLdapTemplateBad1(@RequestParam String nameStr) throws NamingException { + LdapTemplate ctx = new LdapTemplate(); + Name name = new CompositeName(nameStr); + + ctx.lookup(nameStr); + ctx.lookupContext(nameStr); + ctx.findByDn(name, null); + ctx.rename(name, null); + ctx.list(name); + ctx.listBindings(name); + ctx.unbind(nameStr, true); + + ctx.search(nameStr, "", 0, true, null); + ctx.search(nameStr, "", 0, new String[] {}, (ContextMapper) new Object()); + ctx.search(nameStr, "", 0, (ContextMapper) new Object()); + ctx.search(nameStr, "", (ContextMapper) new Object()); + + ctx.searchForObject(nameStr, "", (ContextMapper) new Object()); + } + + public void testShiroJndiTemplateBad1(@RequestParam String nameStr) throws NamingException { + org.apache.shiro.jndi.JndiTemplate ctx = new org.apache.shiro.jndi.JndiTemplate(); + + ctx.lookup(nameStr); + ctx.lookup(nameStr, null); + } + + public void testJMXServiceUrlBad1(@RequestParam String urlStr) throws IOException { + JMXConnectorFactory.connect(new JMXServiceURL(urlStr)); + + JMXServiceURL url = new JMXServiceURL(urlStr); + JMXConnector connector = JMXConnectorFactory.newJMXConnector(url, null); + connector.connect(); + } + + public void testEnvBad1(@RequestParam String urlStr) throws NamingException { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + env.put(Context.PROVIDER_URL, urlStr); + new InitialContext(env); + } + + public void testEnvBad2(@RequestParam String urlStr) throws NamingException { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + env.put("java.naming.provider.url", urlStr); + new InitialDirContext(env); + } + + public void testSpringJndiTemplatePropertiesBad1(@RequestParam String urlStr) throws NamingException { + Properties props = new Properties(); + props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + props.put(Context.PROVIDER_URL, urlStr); + new JndiTemplate(props); + } + + public void testSpringJndiTemplatePropertiesBad2(@RequestParam String urlStr) throws NamingException { + Properties props = new Properties(); + props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + props.setProperty("java.naming.provider.url", urlStr); + new JndiTemplate(props); + } + + public void testSpringJndiTemplatePropertiesBad3(@RequestParam String urlStr) throws NamingException { + Properties props = new Properties(); + props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + props.setProperty("java.naming.provider.url", urlStr); + JndiTemplate template = new JndiTemplate(); + template.setEnvironment(props); + } + + public void testSpringLdapTemplateOk1(@RequestParam String nameStr) throws NamingException { + LdapTemplate ctx = new LdapTemplate(); + + ctx.unbind(nameStr); + ctx.unbind(nameStr, false); + + ctx.search(nameStr, "", 0, false, null); + ctx.search(nameStr, "", new SearchControls(), (NameClassPairCallbackHandler) new Object()); + ctx.search(nameStr, "", new SearchControls(), (NameClassPairCallbackHandler) new Object(), null); + ctx.search(nameStr, "", (NameClassPairCallbackHandler) new Object()); + ctx.search(nameStr, "", 0, new String[] {}, (AttributesMapper) new Object()); + ctx.search(nameStr, "", 0, (AttributesMapper) new Object()); + ctx.search(nameStr, "", (AttributesMapper) new Object()); + ctx.search(nameStr, "", new SearchControls(), (ContextMapper) new Object()); + ctx.search(nameStr, "", new SearchControls(), (AttributesMapper) new Object()); + ctx.search(nameStr, "", new SearchControls(), (ContextMapper) new Object(), null); + ctx.search(nameStr, "", new SearchControls(), (AttributesMapper) new Object(), null); + + ctx.searchForObject(nameStr, "", new SearchControls(), (ContextMapper) new Object()); + } + + public void testEnvOk1(@RequestParam String urlStr) throws NamingException { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + env.put(Context.SECURITY_PRINCIPAL, urlStr); + new InitialContext(env); + } + + public void testEnvOk2(@RequestParam String urlStr) throws NamingException { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); + env.put("java.naming.security.principal", urlStr); + new InitialContext(env); + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.qlref new file mode 100644 index 000000000000..61282fd2de80 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-074/JndiInjection.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/options b/java/ql/test/experimental/query-tests/security/CWE-074/options new file mode 100644 index 000000000000..b9529aa93ce5 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/shiro-core-1.5.2/org/apache/shiro/jndi/JndiTemplate.java b/java/ql/test/experimental/stubs/shiro-core-1.5.2/org/apache/shiro/jndi/JndiTemplate.java new file mode 100644 index 000000000000..936be2abf7c5 --- /dev/null +++ b/java/ql/test/experimental/stubs/shiro-core-1.5.2/org/apache/shiro/jndi/JndiTemplate.java @@ -0,0 +1,13 @@ +package org.apache.shiro.jndi; + +import javax.naming.NamingException; + +public class JndiTemplate { + public Object lookup(final String name) throws NamingException { + return new Object(); + } + + public Object lookup(String name, Class requiredType) throws NamingException { + return new Object(); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java new file mode 100644 index 000000000000..af734cea2372 --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java @@ -0,0 +1,3 @@ +package org.springframework.ldap.core; + +public interface AttributesMapper {} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java new file mode 100644 index 000000000000..951015b637ec --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java @@ -0,0 +1,4 @@ +package org.springframework.ldap.core; + +public interface ContextMapper { +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java new file mode 100644 index 000000000000..682de892a42f --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java @@ -0,0 +1,4 @@ +package org.springframework.ldap.core; + +public interface DirContextOperations { +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java new file mode 100644 index 000000000000..06c2b9aa5446 --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java @@ -0,0 +1,3 @@ +package org.springframework.ldap.core; + +public interface DirContextProcessor {} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java new file mode 100644 index 000000000000..0189611802fc --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java @@ -0,0 +1,3 @@ +package org.springframework.ldap.core; + +public interface LdapOperations {} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java new file mode 100644 index 000000000000..29bee2191d26 --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java @@ -0,0 +1,76 @@ +package org.springframework.ldap.core; + +import org.springframework.beans.factory.InitializingBean; + +import java.util.*; + +import javax.naming.Name; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.filter.Filter; + +import org.springframework.ldap.query.LdapQuery; + +public class LdapTemplate implements LdapOperations, InitializingBean { + public void authenticate(LdapQuery query, String password) { } + + public boolean authenticate(Name base, String filter, String password) { return true; } + + public List find(Name base, Filter filter, SearchControls searchControls, final Class clazz) { return null; } + + public List find(LdapQuery query, Class clazz) { return null; } + + public T findOne(LdapQuery query, Class clazz) { return null; } + + public void search(String base, String filter, int searchScope, boolean returningObjFlag, NameClassPairCallbackHandler handler) { } + + public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler) {} + + public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor) {} + + public void search(String base, String filter, NameClassPairCallbackHandler handler) {} + + public List search(String base, String filter, int searchScope, String[] attrs, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, String[] attrs, ContextMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, ContextMapper mapper) { return null; } + + public List search(String base, String filter, ContextMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, ContextMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, AttributesMapper mapper, DirContextProcessor processor) { return null; } + + public List search(String base, String filter, SearchControls controls, ContextMapper mapper, DirContextProcessor processor) { return null; } + + public DirContextOperations searchForContext(LdapQuery query) { return null; } + + public T searchForObject(Name base, String filter, ContextMapper mapper) { return null; } + + public T searchForObject(String base, String filter, ContextMapper mapper) { return null; } + + public T searchForObject(String base, String filter, SearchControls searchControls, ContextMapper mapper) { return null; } + + public Object lookup(final String dn) { return new Object(); } + + public DirContextOperations lookupContext(String dn) { return null; } + + public T findByDn(Name dn, final Class clazz) { return null; } + + public void rename(final Name oldDn, final Name newDn) {} + + public List list(final Name base) { return null; } + + public List listBindings(final Name base) { return null; } + + public void unbind(final String dn) {} + + public void unbind(final String dn, boolean recursive) {} +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java new file mode 100644 index 000000000000..250e6da0237c --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java @@ -0,0 +1,3 @@ +package org.springframework.ldap.core; + +public interface NameClassPairCallbackHandler { } diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java new file mode 100644 index 000000000000..a5cbbd2a6742 --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java @@ -0,0 +1,5 @@ +package org.springframework.ldap.filter; + +public class EqualsFilter implements Filter { + public EqualsFilter(String attribute, String value) { } +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java new file mode 100644 index 000000000000..b24091e6de0a --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java @@ -0,0 +1,4 @@ +package org.springframework.ldap.filter; + +public interface Filter { +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java new file mode 100644 index 000000000000..bc43dddc6f8c --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java @@ -0,0 +1,7 @@ +package org.springframework.ldap.filter; + +public class HardcodedFilter implements Filter { + public HardcodedFilter(String filter) { } + public StringBuffer encode(StringBuffer buff) { return buff; } + public String toString() { return ""; } +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java new file mode 100644 index 000000000000..80cf59b60401 --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java @@ -0,0 +1,5 @@ +package org.springframework.ldap.query; + +public interface ConditionCriteria { + ContainerCriteria is(String value); +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java new file mode 100644 index 000000000000..7a68b9fbab73 --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java @@ -0,0 +1,4 @@ +package org.springframework.ldap.query; + +public interface ContainerCriteria extends LdapQuery { +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java new file mode 100644 index 000000000000..c94bb75c20ca --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java @@ -0,0 +1,4 @@ +package org.springframework.ldap.query; + +public interface LdapQuery { +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java new file mode 100644 index 000000000000..2e6c76ccc55e --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java @@ -0,0 +1,14 @@ +package org.springframework.ldap.query; + +import javax.naming.Name; +import org.springframework.ldap.filter.Filter; + +public class LdapQueryBuilder { + public static LdapQueryBuilder query() { return null; } + public LdapQuery filter(String hardcodedFilter) { return null; } + public LdapQuery filter(Filter filter) { return null; } + public LdapQuery filter(String filterFormat, Object... params) { return null; } + public LdapQueryBuilder base(String baseDn) { return this; } + public Name base() { return null; } + public ConditionCriteria where(String attribute) { return null; } +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java new file mode 100644 index 000000000000..a85d74192b3d --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java @@ -0,0 +1,5 @@ +package org.springframework.ldap.support; + +public class LdapEncoder { + public static String filterEncode(String value) { return null; } +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java new file mode 100644 index 000000000000..74333407853a --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java @@ -0,0 +1,12 @@ +package org.springframework.ldap.support; + +import javax.naming.ldap.LdapName; + +public class LdapNameBuilder { + public static LdapNameBuilder newInstance() { return null; } + public static LdapNameBuilder newInstance(String name) { return null; } + + public LdapNameBuilder add(String name) { return null; } + public LdapNameBuilder add(String key, Object value) { return null; } + public LdapName build() { return null; } +} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java new file mode 100644 index 000000000000..13fee96e004d --- /dev/null +++ b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java @@ -0,0 +1,7 @@ +package org.springframework.ldap.support; + +import javax.naming.ldap.LdapName; + +public class LdapUtils { + public static LdapName newLdapName(String distinguishedName) { return null; } +} diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java new file mode 100644 index 000000000000..40fc853a45b0 --- /dev/null +++ b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java @@ -0,0 +1,3 @@ +package org.springframework.beans.factory; + +public interface InitializingBean {} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java new file mode 100644 index 000000000000..800071a30d43 --- /dev/null +++ b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java @@ -0,0 +1,21 @@ +package org.springframework.jndi; + +import java.util.Properties; +import javax.naming.NamingException; + +public class JndiTemplate { + public JndiTemplate() {} + + public JndiTemplate(Properties environment) {} + + public Object lookup(final String name) throws NamingException { + return new Object(); + } + + @SuppressWarnings("unchecked") + public T lookup(String name, Class requiredType) throws NamingException { + return (T) new Object(); + } + + public void setEnvironment(Properties environment) {} +} diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java new file mode 100644 index 000000000000..5ae52ad123fa --- /dev/null +++ b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java @@ -0,0 +1,8 @@ +package org.springframework.web.bind.annotation; + +import java.lang.annotation.*; + +@Target(value=ElementType.PARAMETER) +@Retention(value=RetentionPolicy.RUNTIME) +@Documented +public @interface RequestParam { }