Skip to content

CodeQL query to detect JNDI injections#3288

Merged
aschackmull merged 8 commits intogithub:masterfrom
ggolawski:jndi-injection
May 19, 2020
Merged

CodeQL query to detect JNDI injections#3288
aschackmull merged 8 commits intogithub:masterfrom
ggolawski:jndi-injection

Conversation

@ggolawski
Copy link
Copy Markdown
Contributor

@ggolawski ggolawski commented Apr 17, 2020

This PR adds a query to detect JNDI injections. It flags the code where user-provided input is used in JNDI lookup:

public void jndiLookup(HttpServletRequest request) throws NamingException {
  String name = request.getParameter("name");

  Hashtable<String, String> env = new Hashtable<String, String>();
  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);
}

It also supports Spring's and Apache Shiro's JndiTemplate.

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 tests and required qlpack.yml in src/experimental and test/experimental are also included.

Comment thread java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll
Comment thread java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll

Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://trusted-server:1099");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we have a sink for user-controlled URLs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I'll add it.


/** The class `javax.naming.CompositeName`. */
class TypeCompositeName extends Class {
TypeCompositeName() { this.hasQualifiedName("javax.naming", "CompositeName") }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should account for javax.naming.Name interface instead? Not sure if CompositeName is the only one that can hold a JNDI inj payload or if it can be hold in a Name object at all (maybe only String params to lookup are valid for injection purposes)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name interface has 3 implementations in JDK: CompositeName, CompoundName and LdapName. CompositeName and CompoundName can hold JNDI payload:

ctx.lookup(new CompositeName("rmi:\\/\\/127.0.0.1:12345/abc"));
ctx.lookup(new CompoundName("rmi://127.0.0.1:12345/abc", new Properties()));

LdapName, most likely, can't.

I'll add support for CompoundName in the query, but if you think that handling all javax.naming.Name implementations is better, I can change it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If LdapName cant, I think its enough with adding CompoundName, thanks!

* method from Spring's `LdapTemplate`.
*/
predicate springLdapTemplateSinkMethod(Method m, int index) {
m.getDeclaringType() instanceof TypeSpringLdapTemplate and
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cant find TypeSpringLdapTempalte definition. Make sure it also accounts for LdapOperations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In older versions of the API, these clases were in org.springframework.ldap namespace (without core), see https://docs.spring.io/spring-ldap/docs/1.1.2/api/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeSpringLdapTemplate is defined in java/ql/src/semmle/code/java/frameworks/SpringLdap.qll:

class TypeSpringLdapTemplate extends Class {
  TypeSpringLdapTemplate() {
    this.hasQualifiedName("org.springframework.ldap.core", "LdapTemplate")
  }
}

But it makes sense to account for any implementation of LdapOperations interface - I'll change it. I'll also add support for org.springframework.ldap package (without core).

*/
predicate springLdapTemplateSinkMethod(Method m, int index) {
m.getDeclaringType() instanceof TypeSpringLdapTemplate and
(m.hasName("lookup") or m.hasName("lookupContext")) and
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out. I'll add findByDn, rename, list, listBindings, unbind (if recursive is true).

It looks like all search and searchForObject methods where the last parameter is ContextMapper and there's no SearchControls parameter are vulnerable as well (in all these cases, search with RETURN_OBJ_FLAG is called eventually). I'll add them all.

@ggolawski ggolawski requested a review from a team as a code owner May 7, 2020 21:19
@ggolawski
Copy link
Copy Markdown
Contributor Author

@pwntester, I updated the query according to your comments (I hope that I didn't miss anything).
Please let me know if it's OK now. You can check the test file (java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.java) to see all the supported cases (methods with Bad in method name).

Comment thread java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll Outdated
Copy link
Copy Markdown
Contributor

@aschackmull aschackmull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the QL code looks solid, but I've left a few inline comments.

Comment thread java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll Outdated
Comment thread java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll Outdated
Comment thread java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll Outdated
Comment thread java/ql/src/experimental/Security/CWE/CWE-074/JndiInjectionLib.qll
ggolawski and others added 2 commits May 18, 2020 23:18
….qll

Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
@ggolawski
Copy link
Copy Markdown
Contributor Author

@aschackmull Thanks for reviewing the query. I've updated it according to your comments. Please check if everything is fine now.

@aschackmull aschackmull merged commit c36e621 into github:master May 19, 2020
@ggolawski ggolawski deleted the jndi-injection branch June 3, 2020 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants