深入理解JNDI注入一、关于RMI主要介绍了RMI的调用过程,RMI注册中心的概念和动态加载类。1.1远程方法调用RMI(RemoteMethodInvocation)是为Java环境设计的一种远程方法调用机制。远程服务器实现特定的Java方法并提供接口。本地客户端只需要根据接口类的定义提供相应的参数即可调用远程方法。RMI所依赖的通信协议是JRMP(JavaRemoteProtocol,Java远程消息交换协议)。端到端通信要满足的规范。RMI中的对象通过序列化进行编码和传输。1.2远程对象使用远程方法调用,必然涉及到参数的传递和执行结果的返回。参数或者返回值可以是基本数据类型,当然也可以是对象的引用。因此,这些需要传输的对象必须是可序列化的,这就要求对应的类必须实现Java.io.Serializable接口,并且客户端的serialVersionUID字段必须与服务端保持一致。在JVM之间通信时,RMI远程对象和非远程对象的处理方式不同。它不是直接拷贝一个远程对象给客户端,而是传递一个远程对象Stub,相当于一个远程对象。参考或代理。Stub对开发者是透明的,客户端可以像调用本地方法一样通过它调用远程方法。Stub中包含了远程对象的位置信息,如Socket端口、服务器主机地址等,实现了远程调用过程中具体的底层通信细节,因此RMI远程调用逻辑如下:从逻辑上讲,数据是在Client和Server之间水平流动的,但实际上是从Client到Stub,再从Skeleton到Server的垂直流动。服务器监听一个端口,由JVM随机选择;客户端不知道服务器远程对象的通信地址和端口,但是Stub包含了这些信息并封装了底层的网络操作;客户端可以调用存根上的方法;Stub连接到Server监听的通信端口,并提交参数;在远程Server上执行特定的方法,并将结果返回给Stub;Stub将执行结果返回给Client,从Client看来Stub是在本地执行这个方法的一样;如何获得存根?1.3获取RMI注册表存根的方法有很多种。常见的方法是调用远程服务上的方法从远程服务器获取存根。但是调用远程方法需要已有远程对象的Stub,所以这里存在死循环问题。JDK提供了一个RMI注册中心(RMIRegistry)来解决这个问题。RMIRegistry也是一个远程对象。它默认侦听传说中的端口1099。您可以使用代码启动RMIRegistry或使用RMIRegistry命令。注册远程对象,获取远程对象的RMI调用关系:从客户端的角度来看,服务端应用有两个端口,一个是RMIResgistry端口(默认1099),一个是远程对象的通信端口远程对象(随机分配)1.4RMI的核心特性之一是动态类加载。如果当前JVM中没有某个类的定义,它可以从远程下载这个类的类,动态加载该对象的class文件。这样可以动态扩展远程远程应用的功能,RMI注册中心可以动态加载绑定多个RMI应用。对于客户端来说,服务器的返回值也可能是一些子类的对象实例,但是客户端并没有这些子类的类文件。如果客户端需要正确调用这些子类中重写的方法,就需要有在运行时动态加载额外类的能力。客户端使用与RMI注册表相同的机制。RMI服务器将URL传递给客户端,客户端通过HTTP请求下载这些类。JNDI注入使用了动态加载类的思想。JNDIInjection2.1关于JNDI简单的说,JNDI(JavaNamingAndDirectoryInterface)是一套应用程序编程接口,它为开发者查找和访问各种资源提供了统一的通用接口,可以用来定位用户、网络、各种机器、对象和服务等资源。例如,您可以使用JNDI来定位LAN上的打印机,或者您可以使用JNDI来定位数据库服务或远程Java对象。JDNI底层支持RMI远程对象,通过JNDI接口可以访问和调用RMI注册的服务。JNDI支持多种命名和目录提供程序,RMI注册服务提供程序(RMIRegistryServiceProvider)允许通过JNDI应用程序接口访问在RMI中注册的远程对象。将RMI服务绑定到JNDI的好处之一是它更加透明、统一和松散耦合。RMI客户端通过URL直接定位远程对象,RMI服务可以链接到包含人员、组织、网络资源等信息的企业目录。一起。JNDI接口初始化时,可以将RMIURL作为参数传入,JNDI注入发生在客户端的lookup()函数中。如果lookup()的参数是可控的,就有可能被攻击。2.2JNDI References注入JNDIReferences注入是JNDI的核心部分。在JNDI服务中,RMI服务器除了直接绑定远程对象外,还可以通过References类绑定外部远程对象(当前名称目录系统以外的对象)。绑定References后,服务端会先通过Referenceable.getReference()获取绑定对象的引用,并保存在目录中。当客户端在lookup()中查找远程对象时,客户端可能会获取对应的对象工厂,最后通过工厂类将引用转化为具体的对象实例。整个利用过程如下:目标代码中调用了context.lookup(url),url是用户可控的;攻击者控制url参数为恶意RMI服务地址,如:rmi://hacker_rmi_server/name;攻击者的RMI服务器返回一个Reference对象给目标,在Reference对象中指定一个构造良好的Factory类;目标在lookp()操作过程中会动态加载并实例化Factory类,然后调用Factory.getObjectlnstance()获取外部远程对象实例攻击者可以在Factory类文件的构造方法中写入恶意代码,静态代码block、getObjectInstancc()方法等实现RCE的效果。看起来JNDI注入的方式很简单,但是在jdk8u191之后修复了,使得我们现在的方式失效了。2.38u191后的JNDI注入,看看做了哪些修复。可以看到高版本的jdk默认将trustURLCodebase设置为false,这样客户端就无法动态加载类了。2.4JNDI绕过我们可以让var8.getFactoryClassLocation()为空,跳过这个if,进入else跟进Naming.getObjectInstance(),这里我们会从ref中获取ObjectFactory对象,然后用这个ObjectFactory对象创建一个类if不为空的例子继续跟进factory.getObjectFactoryFromReference(),创建一个ELProcessor实例,然后取出RefAddr中key为“forceString”的context,再进行进一步分析,将=的两边分开,右边的value可以替换setter方法,这里替换为eval(),传入等号左边的value并通过反射将参数放入参数数组,通过method.invoke()执行,最后弹出远程连接窗口,注入成功!
