Coordinated Disclosure Timeline
- 2023-02-02: Report sent to security@pac4j.org
- 2023-02-14: Acknowledged by the development team, CVE is issued. The fix is available with version 4.0
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
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
- CVE-2023-25581
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.