Learning Man's Blog

CodeQL 学习小记 Log4j

字数统计: 993阅读时长: 5 min
2021/12/30

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。

QL

这里可以借鉴 CWE-074 中的内容,直接使用其定义的 sink,其已经将常见的利用点标记出来

private class DefaultJndiInjectionSinkModel extends SinkModelCsv {
  override predicate row(string row) {
    row =
      [
        "javax.naming;Context;true;lookup;;;Argument[0];jndi-injection",
        "javax.naming;Context;true;lookupLink;;;Argument[0];jndi-injection",
        "javax.naming;Context;true;rename;;;Argument[0];jndi-injection",
        "javax.naming;Context;true;list;;;Argument[0];jndi-injection",
        "javax.naming;Context;true;listBindings;;;Argument[0];jndi-injection",
        "javax.naming;InitialContext;true;doLookup;;;Argument[0];jndi-injection",
        "javax.management.remote;JMXConnector;true;connect;;;Argument[-1];jndi-injection",
        "javax.management.remote;JMXConnectorFactory;false;connect;;;Argument[0];jndi-injection",
        // Spring
        "org.springframework.jndi;JndiTemplate;false;lookup;;;Argument[0];jndi-injection",
        // spring-ldap 1.2.x and newer
        "org.springframework.ldap.core;LdapOperations;true;lookup;;;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;lookupContext;;;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;findByDn;;;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;rename;;;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;list;;;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;listBindings;;;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;search;(Name,String,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;search;(Name,String,int,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;search;(Name,String,int,String[],ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;search;(String,String,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;search;(String,String,int,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;search;(String,String,int,String[],ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;searchForObject;(Name,String,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap.core;LdapOperations;true;searchForObject;(String,String,ContextMapper);;Argument[0];jndi-injection",
        // spring-ldap 1.1.x
        "org.springframework.ldap;LdapOperations;true;lookup;;;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;lookupContext;;;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;findByDn;;;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;rename;;;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;list;;;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;listBindings;;;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;search;(Name,String,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;search;(Name,String,int,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;search;(Name,String,int,String[],ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;search;(String,String,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;search;(String,String,int,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;search;(String,String,int,String[],ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;searchForObject;(Name,String,ContextMapper);;Argument[0];jndi-injection",
        "org.springframework.ldap;LdapOperations;true;searchForObject;(String,String,ContextMapper);;Argument[0];jndi-injection",
        // Shiro
        "org.apache.shiro.jndi;JndiTemplate;false;lookup;;;Argument[0];jndi-injection"
      ]
  }
}

所以我们只要关注到 source 的定义即可,而追溯 source 不管怎么向上,终归应该是有个函数中的某个参数是源头,所以如下定义 source

/**
 * @kind path-problem
 */

import java
import semmle.code.java.security.JndiInjection
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph

class Config extends TaintTracking::Configuration {
    Config() { this = "Config" }

    override predicate isSource(DataFlow::Node source) {
        exists(Argument a | a = source.asExpr())
    }

    override predicate isSink(DataFlow::Node sink) { sink instanceof JndiInjectionSink }

}

from DataFlow::PathNode source, DataFlow::PathNode sink, Config conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "JNDI lookup might include name from $@.", source.getNode(), "this user input"

这个结果太多以至于就不放上来占篇幅了,不过有很多有意思的东西可以自己看看

限定 AbstractLogger

限定为AbstractLogger的主要考虑是为了重现 CVE-2021-44228,由于此类下方法太多,没必要过于细化,我们只要考虑这个类里,某个公开函数的某个参数应是 source 即可

override predicate isSource(DataFlow::Node source) {
    exists(MethodAccess mda |
        mda.getMethod().getDeclaringType().hasQualifiedName("org.apache.logging.log4j.spi", "AbstractLogger") and
        mda.getAnArgument() = source.asExpr() and
        mda.getMethod().isPublic()
    )
}

基本有的没的就全出来了

关于 DataSourceConnectionSource

这个发现不用什么高大上的方法,甚至根本不该称为漏洞(很模糊的界限),注释里面写的就是对 JNDI 形式的支持(我觉得官方现在是草木皆兵

只需要对上述的 isSink 使用CodeQL: Quick Evaluation快速查询即可

如果上面的都能称为漏洞,那么如org.apache.logging.log4j.jmx.gui.ClientGui#main里也明显有 JNDI 注入,只是需要传输指定类名为 jmxrmi

直接利用

import org.apache.logging.log4j.jmx.gui.ClientGui;

public class Test {
    public static void main(String[] args) throws Exception {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        ClientGui.main(new String[]{"127.0.0.1"});
    }
}


终归就是

CATALOG
  1. 1. QL
  2. 2. 限定 AbstractLogger
  3. 3. 关于 DataSourceConnectionSource