由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
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"});
}
}
终归就是