Coordinated Disclosure Timeline

Summary

pac4j-core prior to version 4 is affected by a Java deserialization vulnerability. The vulnerability affects systems that store externally controlled values in attributes of the UserProfile class from pac4j-core. It can be exploited by providing an attribute that contains a serialized Java object with a special prefix {#sb64} and Base64 encoding.

Product

pac4j-core

Tested Version

v3.8.3

Details

Issue: Deserialization of untrusted data in InternalAttributeHandler (GHSL-2022-085)

The method org.pac4j.core.profile.InternalAttributeHandler#restore deserializes untrusted data:

public Object restore(final Object value) {
      if (value != null && value instanceof String) {
          final String sValue = (String) value;
          if (sValue.startsWith(PREFIX)) {
              if (sValue.startsWith(PREFIX_BOOLEAN)) {
                  return Boolean.parseBoolean(sValue.substring(PREFIX_BOOLEAN.length()));
              } else if (sValue.startsWith(PREFIX_INT)) {
                  return Integer.parseInt(sValue.substring(PREFIX_INT.length()));
              } else if (sValue.startsWith(PREFIX_LONG)) {
                  return Long.parseLong(sValue.substring(PREFIX_LONG.length()));
              } else if (sValue.startsWith(PREFIX_DATE)) {
                  final String d = sValue.substring(PREFIX_DATE.length());
                  try {
                      return newSdf().parse(d);
                  } catch (final ParseException e) {
                      logger.warn("Unable to parse stringified date: {}", d, e);
                  }
              } else if (sValue.startsWith(PREFIX_URI)) {
                  final String uri = sValue.substring(PREFIX_URI.length());
                  try {
                      return new URI(uri);
                  } catch (final URISyntaxException e) {
                      logger.warn("Unable to parse stringified URI: {}", uri, e);
                  }
              } else if (sValue.startsWith(PREFIX_SB64)) {
                  return serializationHelper.unserializeFromBase64(sValue.substring(PREFIX_SB64.length()));
              }
          }
      }
      return value;
  }

The logic inside InternalAttributeHandler#prepare method is designed to support different attribute types, such as String, Boolean, Data or URI. When it comes to storing objects of arbitrary type, it uses Java serialization with ObjectInputStream and add a special prefix {#sb64}. When an attribute is restored, the code inside InternalAttributeHandler#restore checks the prefix and constructs the corresponding type. This logic is flawed for string attributes, as InternalAttributeHandler#prepare does not check if a string value already starts with {#sb64}. If an externally controlled string is stored as an attribute, it’s possible to trick the program to perform a deserialization of an arbitrary Java class. For example, if the username or email are stored as attributes, it’s possible to use the username like {#sb64}rO0ABXN...serizalized_object_in_base64... to trigger the deserialization vulnerability.

Impact

This issue may lead to Remote Code Execution (RCE) in the worst case. Although a RestrictedObjectInputStream is in place, that puts some restriction on what classes can be deserialized, it still allows a broad range of java packages and potentially exploitable with different gadget chains.

Resources

CVE

Credit

This issue was discovered and reported by GHSL team member @artsploit (Michael Stepankin).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2022-085 in any communication regarding this issue.