Java安全 - Apache Log4j2 JNDI Injection 原理分析
简介
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
原理
由于Apache Log4j2某些功能存在递归解析,攻击者可在未经身份验证的情况下构造发送带有攻击语句的数据请求包,最终造成在目标服务器上执行任意代码
依赖
1 | |
demo
1 | |
在执行上述代码的时候可以发现他对我们的${java.os}进行了解析,那么对此我们进行分析为什么会对${进行解析?
于是前面的直接跳过,直接在这个包下下断点,因为这个类是拿来对日志信息进行打印的
log4j-core-2.14.1.jar!\org\apache\logging\log4j\core\layout\PatternLayout#toSerializable()
两个传进去进行处理的变量,一个是 event,也就是我们 log4j2 需要来进行日志打印的内容;另外一个 buffer,我们会把打印出来的东西写进 buffe
这里其实没有必要跟进这个format方法(有别的师傅踩坑了) 直接让i = 8 来看是什么
发现是走到了log4j-core-2.14.1.jar!\org\apache\logging\log4j\core\pattern\MessagePatternConverter#format()中
当我们进到这个format()方法里面之后,先判断是否是 Log4j2 的 lookups 功能。这里我们是 lookups 功能,所以可以继续往下走然后继续往下走,会遍历 workingBuilder 来进行判断;如果 workingBuilder 中存在${,那么就会取出从 $ 开始知道最后的字符串,这一步
但是这里有个replace方法会不会进行一个替换呢? 跟进
接着调用了substitute方法
这里就是获取payload了,这里存在一个while 循环 会对字符进行逐字匹配${然后进行循环读取,知道读取到} 并获取其坐标,然后将 ${} 中间的内容取出来
将 varName 作为变量传入了 resolveVariable 函数
这里我们看到resolveVariable()方法里面是调用了lookup()方法,这个lookup()方法也就是 jndi 里面原生的方法,在我们让 jndi 去调用 ldap 服务的时候,是调用原生的lookup()方法的
总结一下
- 先判断内容中是否有
${},然后截取${}中的内容,得到我们的恶意payloadjndi:xxx - 后使用
:分割payload,通过前缀来判断使用何种解析器去调用对应协议的lookup方法 ,写了JNDI就会去用JNDI去调ldap - 支持的前缀包括
date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j
最后就造成了JNDI注入
复现
solr的话就是一个接口存在log4j2
1 | |