CVE-2025-1974 - Auth0 Wordpress Plugin - Insecure Deserialization
Abstract
The Auth0 Wordpress plugin contains a critical vulnerability due to insecure deserialization of cookie data. If exploited, since SDKs process cookie content without prior authentication, a threat actor could send a specially crafted cookie containing malicious serialized data.
Analysis
The vulnerability stems from the CookieStore.php
within the Auth0 SDK, which is used by the Wordpress plugin (also laravel ...). The SDK processes cookie content without prior checks and pass it to serialize() .
The core issue lies in the decrypt
function, which deserializes arbitrary data retrieved from cookies without proper protections. This allows us to inject arbitrary PHP objects into the application's scope
Vulnerability Details
The CookieStore
class is responsible for managing the storage of data in cookies. The getState
method retrieves data from cookies, decrypts it (if encryption is enabled), and unserializes it. basically the lack of proper validation on the unserialized data is the root cause of this vulnerability.
public function getState(
?array $state = null
): array {
// Overwrite our internal state with one passed (presumably during unit tests.)
if ($state !== null) {
if ($this->store !== $state) {
$this->dirty = true;
}
return $this->store = $state;
}
$request = Application::Instance()->HttpRequest();
$data = $request->cookie($this->namespace);
if (is_array($data) || $data === null) {
return $this->store = [];
}
// If no cookies were found, set an empty state and continue.
if ($data === '') {
return $this->store = [];
}
$data = $this->decrypt($data);
if ($data === null) {
return $this->store = [];
}
if (is_array($data)) {
return $this->store = $data;
}
return $this->store = [];
}
The decrypt
function is responsible for decrypting the cookie data. If encryption is disabled, it simply decodes the data using rawurldecode
and json_decode
. If encryption is enabled, it performs the decryption using openssl_decrypt
and then decodes the data using json_decode
. In both cases, the data is not validated before being used.
public function decrypt(
string $data
) {
if (! $this->encrypt) {
$decoded = rawurldecode($data);
$decoded = json_decode($decoded, true);
if (is_array($decoded)) {
return $decoded;
}
return [];
}
[$data] = Toolkit::filter([$data])->string()->trim();
$secret = $this->configuration->getCookieSecret();
if ($secret === null) {
throw \Auth0\SDK\Exception\ConfigurationException::requiresCookieSecret();
}
$decoded = rawurldecode((string) $data);
$stripped = stripslashes($decoded);
$data = json_decode($stripped, true, 512);
/** @var array{iv?: int|string|null, tag?: int|string|null, data: string} $data */
if (! isset($data['iv']) || ! isset($data['tag']) || ! is_string($data['iv']) || ! is_string($data['tag'])) {
return null;
}
$iv = base64_decode($data['iv'], true);
$tag = base64_decode($data['tag'], true);
if (! is_string($iv) || ! is_string($tag)) {
return null;
}
$data = openssl_decrypt($data['data'], self::VAL_CRYPTO_ALGO, $secret, 0, $iv, $tag);
if (! is_string($data)) {
return null;
}
$data = json_decode($data, true);
/** @var array<mixed> $data */
return $data;
}
Patch Review
The patch addresses the vulnerability by introducing a new setEncrypted
method to enable or disable cookie encryption.
--- a/src/Store/CookieStore.php
+++ b/src/Store/CookieStore.php
@@ -50,6 +50,16 @@
*/
private bool $dirty = false;
+ /**
+ * Determine if changes have been made since the last setState.
+ */
+ private bool $encrypt = true;
+
+ /**
+ * Returns the current encryption state
+ */
+ public function getEncrypted(): bool
+ {
+ return $this->encrypt;
+ }
+ /**
+ * Toggle the encryption state
+ *
+ * @param bool $encrypt Enable or disable cookie encryption.
+ */
+ public function setEncrypted(bool $encrypt = true): self
+ {
+ $this->encrypt = $encrypt;
+ return $this;
+ }
The patch modifies the encrypt
and decrypt
methods to use rawurlencode
and rawurldecode
when encryption is disabled. This prevents the unserialization of data.
--- a/src/Store/CookieStore.php
+++ b/src/Store/CookieStore.php
@@ -371,12 +371,12 @@
public function encrypt(
array $data,
array $options = []
): string {
if (! $this->encrypt) {
$data = $options['encoded1'] ?? json_encode($data);
if (! is_string($data)) {
return '';
}
return rawurlencode($data);
}
--- a/src/Store/CookieStore.php
+++ b/src/Store/CookieStore.php
@@ -434,7 +434,7 @@
if (! $this->encrypt) {
$decoded = rawurldecode($data);
$decoded = json_decode($decoded, true);
Exploitation Path
To exploit this vulnerability, an attacker would:
- Craft a PHP object (Too lazy to look for a pop chain in this codebase)
- Serialize this object.
- Base64 encode the serialized data.
- Set a cookie with the name matching the
$namespace
used by theCookieStore
instance. The value of the cookie would be the base64 encoded serialized data. - When the application processes the request with the cookie, the
getState
method will retrieve the cookie data, thedecrypt
method will decode the data, and theunserialize
function will execute the methods that exists in our serialized object