Apache Log4j2是一款优秀的Java日志框架,最近爆出了一个jndi注入的漏洞,影响面非常广,各大厂商都被波及。Log4j2作为日志记录的第三方库,被广泛得到使用,这次主要分享一下,最近的一些调试记录。
JNDI简介
JNDI 全称为 Java Naming and Directory Interface,即 Java 名称与目录接口。本质上就是一个接口,ND代表的Naming 和 Directory,分别代表Naming Service(名称服务)和Directory Service(目录服务)。参考JNDI 注入漏洞的前世今生
名称服务就是通过名称查找实际对象的服务,例如:通过域名寻找ip地址即DNS服务、文件系统、以及LDAP(Lightweight Directory Access Protocol)即轻量级目录访问协议都是名称服务,不同的是LDAP(RFC2251(RF**511) )是一个协议,是和HTTP一样是通用的,而不止局限于JAVA.目录服务是名称服务的一种拓展,除了名称服务中已有的名称到对象的关联信息外,还允许对象拥有属性(attributes)信息。由此,我们不仅可以根据名称去查找(lookup)对象(并获取其对应属性),还可以根据属性值去搜索(search)对象。目录服务也是一种特殊的名称服务,关键区别是在目录服务中通常使用搜索(search)操作去定位对象,而不是简单的根据名称查找(lookup)去定位。
JNDI 架构上主要包含两个部分,即 Java 的应用层接口和 SPI,SPI 全称为 Service Provider Interface,即服务供应接口,主要作用是为底层的具体目录服务提供统一接口,从而实现目录服务的可插拔式安装,如下图所示:
如上JNDI为不同的目录服务提供统一的操作接口
JDK 中包含了下述内置的目录服务:
- RMI: Java Remote Method Invocation,Java 远程方法调用;
- LDAP: 轻量级目录访问协议;
- CORBA: Common Object Request Broker Architecture,通用对象请求代理架构,用于 COS 名称服务(Common Object Services);
RMI
RMI(Remote Method Invocation)即java的远程方法调用,Java RMI是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法并获取执行结果,即JAVA的RPC机制。关于RMI需要注意以下两点:
更多可以参考:https://paper.seebug.org/1091/#java-rmi_1
LDAP
LDAP即是JNDI SPI支持的Service Provider之一,但同时也是协议。是早期 X.500 DAP (目录访问协议) 的一个子集,因此有时也被称为 X.500-lite。LDAP目录服务是由目录数据库和一套访问协议组成的系统,目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,能进行查询、浏览和搜索,以树状结构组织数据。LDAP目录服务基于客户端-服务器模型,它的功能用于对一个存在目录数据库的访问。LDAP目录和RMI注册表的区别在于是前者是目录服务,并允许分配存储对象的属性。
LDAP 的目录信息是以树形结构进行存储的,在树根一般定义国家(c=CN)或者域名(dc=com),其次往往定义一个或多个组织(organization,o)或组织单元(organization unit,ou)。一个组织单元可以包含员工、设备信息(计算机/打印机等)相关信息。
一些定义:
漏洞环境
pom.xml 4.0.0 org.example log4j-test 1.0-SNAPSHOT org.apache.logging.log4j log4j-api 2.9.0 org.apache.logging.log4j log4j-core 2.9.0 log4jTest.java import org.apache.logging.log4j.LogManager; public class log4jTest { //获取日志记录器Logger,名字为本类类名 private static final Logger logger = LogManager.getLogger(); public static void main(String[] args) { for(int i=0;i payload
放在mysql服务器的py文件同级目录,并且运行mysq服务器。
流程分析
log4j三大组件为Logger、Appender、Layout。Logger负责收集处理日志记录,Layout负责日志输出的形式,而Appender负责配置日志的输出位置和方式。
其中Appender可以配置的一种方式为数据库输出(JDBCAppender),通过JDBC链接把日志输出到数据库中,配置时需要配置JDBC驱动,连接字符串,用户名,密码以及SQL语句。
我们直接把断点打在JDBCAppender.java的getConnection()处,因为这也是MYSQL JDBC反序列化的执行点。
调用链如下:
成功执行:
但是正常情况下我们是无法控制log4j的配置文件的,所以是比较鸡肋的,但是一些可以动态配置服务的,例如nacos,也许可以找到利用方式。
但是不知道是否支持log4j1.x,:)。
参考:
https://paper.seebug.org/1091/
https://paper.seebug.org/942/
https://githubmemory.com/repo/Ea3i0n/JNDIExploit
https://www.docs4dev.com/docs/zh/log4j2/2.x/all/manual-lookups.html
https://evilpan.com/2021/12/13/jndi-injection/
https://mp.weixin.qq.com/s/vAE89A5wKrc-YnvTr0qaNg
本文作者:合天网安实验室