[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fHRrV9I1Cv4aOgjxI7v5PkQNU4StwdnjTx5cx5-sFaoI":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-54834","object-cache-4-everyone-information-exposure","Object Cache 4 everyone \u003C= 2.3.2 - Information Exposure","The Object Cache 4 everyone plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 2.3.2. This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.","object-cache-4-everyone",null,"\u003C=2.3.2","2.3.3","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:L\u002FI:N\u002FA:N","Exposure of Sensitive Information to an Unauthorized Actor","2026-06-17 00:00:00","2026-06-25 13:37:54",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F2002b29b-c817-48a9-b65c-ad1bff2c0935?source=api-prod",9,[22,23,24],"object-cache-4-everyone.php","object-cache-disk-template.php","readme.txt","researched",false,3,"This research plan focuses on **CVE-2026-54834**, an information exposure vulnerability in the \"Object Cache 4 everyone\" plugin. The vulnerability arises from storing serialized cache data in web-accessible files with predictable names and locations when using the disk-based caching backend.\n\n---\n\n### 1. Vulnerability Summary\nThe **Object Cache 4 everyone** plugin (\u003C= 2.3.2) provides a disk-based fallback when Memcached is unavailable. This disk backend stores cached objects (which often include sensitive site configuration, user metadata, and session transients) as serialized PHP data. These files are stored within the `wp-content\u002Fcache\u002Fobject\u002F` directory. \n\nBecause the directory structure and filenames are derived from a simple MD5 hash of the cache key, and the files use a `.object.php` extension without proper access controls (like `.htaccess` or placement outside the web root), an unauthenticated attacker can predict the path to sensitive cache entries and download them directly via HTTP.\n\n### 2. Attack Vector Analysis\n*   **Endpoint**: Direct HTTP GET requests to files located under `\u002Fwp-content\u002Fcache\u002Fobject\u002F`.\n*   **Vulnerable File**: `object-cache-disk-template.php` (specifically the `_get_path` and `set` methods).\n*   **Payload**: No payload is required for the request itself; the \"attack\" is the prediction of the file path.\n*   **Authentication**: None (Unauthenticated).\n*   **Preconditions**: \n    1.  The plugin must be active.\n    2.  The disk cache must be in use (Memcached must be unavailable or disabled via `OC4EVERYONE_DISABLE_DISK_CACHE`).\n    3.  The web server must allow access to the `wp-content\u002Fcache\u002F` directory.\n\n### 3. Code Flow\nThe vulnerability can be traced through the following call stack:\n\n1.  **Entry**: A WordPress process calls `wp_cache_set( $key, $value, $group )`.\n2.  **Processing**: The `WP_Object_Cache` class (installed by the plugin) formats the internal key, typically using the pattern `$blog_prefix . $group . ':' . $key`.\n3.  **Sink (Write)**: `ObjectCacheDisk::set($key, $value)` is called in `object-cache-disk-template.php`.\n4.  **Path Generation**: `ObjectCacheDisk::_get_path($key)` (line 157) calculates the file path:\n    ```php\n    $hash = md5($key); \u002F\u002F Returns 32-character hex string\n    $array_hash = str_split($hash, 8); \u002F\u002F Splits into four 8-character segments\n    $path = $this->local_path . implode(DIRECTORY_SEPARATOR, $array_hash); \u002F\u002F wp-content\u002Fcache\u002Fobject\u002FS1\u002FS2\u002FS3\u002FS4\u002F\n    \u002F\u002F ...\n    $path .= DIRECTORY_SEPARATOR . '.object.php'; \u002F\u002F Final filename: .object.php\n    ```\n5.  **Storage**: The serialized data is written to the file via `$wp_filesystem->put_contents($path, @serialize($value))`.\n6.  **Exposure**: The attacker requests the URL corresponding to the generated `$path`.\n\n### 4. Nonce Acquisition Strategy\n**No nonce is required.** This vulnerability involves direct access to static files on the filesystem. WordPress nonces are not checked by the web server when serving `.php` or other files from the `wp-content\u002Fcache` directory.\n\n### 5. Exploitation Strategy\nThe goal is to retrieve the `alloptions` cache entry, which contains a massive amount of site configuration data.\n\n1.  **Identify Target Key**: The standard key for site options is `options:alloptions`. In a default single-site WordPress installation (Blog ID 1), the prefixed key used by the cache engine is `1:options:alloptions`.\n2.  **Calculate Path**:\n    *   Target Key: `1:options:alloptions`\n    *   MD5 Hash: `0043818e100f95470d046c879d72a5a5`\n    *   Split segments: `0043818e`, `100f9547`, `0d046c87`, `9d72a5a5`\n    *   Predicted Path: `\u002Fwp-content\u002Fcache\u002Fobject\u002F0043818e\u002F100f9547\u002F0d046c87\u002F9d72a5a5\u002F.object.php`\n3.  **Execute Request**: Use the `http_request` tool to fetch the file.\n4.  **Parse Data**: The response will be a serialized PHP string. Use a script or `unserialize()` to extract the site configuration.\n\n### 6. Test Data Setup\nTo verify the vulnerability in a test environment:\n1.  **Environment**: A standard WordPress installation with the plugin \"Object Cache 4 everyone\" \u003C= 2.3.2.\n2.  **Trigger Fallback**: Ensure no Memcached server is running on the local host to force the plugin to use the `ObjectCacheDisk` backend.\n3.  **Populate Cache**: Log in as an administrator and browse the dashboard. This ensures the `alloptions` and `users:1` keys are cached to disk.\n4.  **Confirm Storage**: Check that the directory `wp-content\u002Fcache\u002Fobject\u002F` has been created and populated with subdirectories.\n\n### 7. Expected Results\n*   An HTTP GET request to the predicted path returns a `200 OK` status.\n*   The response body starts with serialized PHP markers (e.g., `a:NNN:{...}` or `O:8:\"stdClass\":...`).\n*   The data contains sensitive information such as the `admin_email`, `siteurl`, `active_plugins`, and potentially database schema information or transient secrets.\n\n### 8. Verification Steps\n1.  **Verify File Existence**: Use WP-CLI to confirm the path the plugin *should* be using:\n    `wp eval \"echo WP_CONTENT_DIR . '\u002Fcache\u002Fobject\u002F' . implode('\u002F', str_split(md5('1:options:alloptions'), 8)) . '\u002F.object.php';\"`\n2.  **Check Content**: Compare the output of the HTTP request with the actual option value:\n    `wp option get alloptions --format=json`\n3.  **Check Direct Access**: Ensure that attempting to access the directory itself (`\u002Fwp-content\u002Fcache\u002Fobject\u002F`) returns a 403 or an empty `index.php`, but the specific `.object.php` file remains accessible.\n\n### 9. Alternative Approaches\nIf `1:options:alloptions` does not exist, the plugin or WordPress configuration might not be using a blog prefix. Try these variations:\n\n*   **No Prefix**: `options:alloptions` \n    *   MD5: `53066d9341498b5e9036c57f2023d8c2`\n    *   Path: `\u002Fwp-content\u002Fcache\u002Fobject\u002F53066d93\u002F41498b5e\u002F9036c57f\u002F2023d8c2\u002F.object.php`\n*   **User Data**: `1:users:1` (Admin User Data)\n    *   MD5: `7f26798e8334465439a38f36c5339c0f`\n    *   Path: `\u002Fwp-content\u002Fcache\u002Fobject\u002F7f26798e\u002F83344654\u002F39a38f36\u002Fc5339c0f\u002F.object.php`\n*   **Custom Salt**: If the site defines `WP_CACHE_KEY_SALT` in `wp-config.php`, the key will be `SALT + blog_prefix + group + : + key`. The agent would need to discover this salt (e.g., via a different leak) to predict the MD5.","The Object Cache 4 everyone plugin (\u003C= 2.3.2) for WordPress stores cached data in predictable locations on the filesystem using MD5 hashes of cache keys. Due to a lack of directory access controls and the use of a .php extension, unauthenticated attackers can directly access and download these files to extract sensitive site configuration or user information.","\u002F\u002F object-cache-disk-template.php line 106\n    public function set($key, $value, $expiration = 0)\n    {\n        global $wp_filesystem;\n        \u002F\u002FFind file and put\n        $path = $this->_get_path($key);\n\n        \u002F\u002FFolder for file\n        $dir = dirname($path);\n        if (!$wp_filesystem->is_dir($dir)) {\n            $return = $wp_filesystem->mkdir($dir, 0755);\n            if (!$return) {\n                $this->result_code = self::RES_FAILURE;\n                return false;\n            }\n        }\n\n        if (is_object($value)) {\n            $value = clone $value;\n        }\n\n        $return = $wp_filesystem->put_contents($path, @serialize($value));\n        if (!$return) {\n            $this->result_code = self::RES_FAILURE;\n            return false;\n        }\n\n        $this->result_code = self::RES_SUCCESS;\n        return true;\n    }\n\n---\n\n\u002F\u002F object-cache-disk-template.php line 211\n    private function _get_path($key)\n    {\n        $hash = md5($key); \u002F\u002FReturns the hash as a 32-character hexadecimal number.\n\n        $array_hash = str_split($hash, 8); \u002F\u002F8 name based\n\n        $path = $this->local_path . implode(DIRECTORY_SEPARATOR, $array_hash);\n        \n        \u002F\u002F Ensure directory exists with an index.php to prevent directory listing\n        if (!file_exists($path . DIRECTORY_SEPARATOR . 'index.php')) {\n            @file_put_contents($path . DIRECTORY_SEPARATOR . 'index.php', '\u003C?php \u002F\u002F Silence is golden.');\n        }\n\n        $path .= DIRECTORY_SEPARATOR . '.object.php';\n        return $path;\n    }","Only in \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.3: changelog.txt\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.2\u002Fobject-cache-4-everyone.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.3\u002Fobject-cache-4-everyone.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.2\u002Fobject-cache-4-everyone.php\t2026-05-08 11:59:40.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.3\u002Fobject-cache-4-everyone.php\t2026-06-11 11:41:48.000000000 +0000\n@@ -5,7 +5,7 @@\n  * Description: Memcached or disk backend support for the WP Object Cache. Memcached server running and PHP Memcached class needed for better performance. No configuration needed, runs automatically\n  * Plugin URI: https:\u002F\u002Fwordpress.org\u002Fplugins\u002Fobject-cache-4-everyone\n  * Author: fpuenteonline\n- * Version: 2.3.2\n+ * Version: 2.3.3\n  * Author URI: https:\u002F\u002Ftwitter.com\u002Ffpuenteonline\n  * License:     GPLv2 or later\n  * License URI: http:\u002F\u002Fwww.gnu.org\u002Flicenses\u002Fold-licenses\u002Fgpl-2.0.html\n@@ -133,7 +133,7 @@\n                 '127.0.0.1:20001'\n             );\n \n-            if (defined('OC4EVERYONE_MEMCACHED_SERVER')) {\n+            if (defined('OC4EVERYONE_MEMCACHED_SERVER') && is_string(OC4EVERYONE_MEMCACHED_SERVER) && preg_match('\u002F^[A-Za-z0-9.\\\\-]+:[0-9]{1,5}$\u002F', OC4EVERYONE_MEMCACHED_SERVER)) {\n                 $memcached_servers =  array(OC4EVERYONE_MEMCACHED_SERVER);\n             } else {\n                 \u002F\u002F Try SG Memcached server first.\n@@ -188,8 +188,8 @@\n  *\n  *\u002F\n ?>\" . $template;\n-                $template .= '\u002F\u002FDetected memcached server - ' . gmdate('d\u002Fm\u002FY G:i:s', current_time('timestamp', 0)) . PHP_EOL;\n-                $template .= \"define('OC4EVERYONE_PREDEFINED_SERVER', '$found_server');\" . PHP_EOL;\n+                $template .= '\u002F\u002FDetected memcached server - ' . gmdate('d\u002Fm\u002FY G:i:s', time()) . PHP_EOL;\n+                $template .= 'define(\\'OC4EVERYONE_PREDEFINED_SERVER\\', ' . var_export($found_server, true) . ');' . PHP_EOL;\n \n                 $template .= \"if (! defined('WP_CACHE_KEY_SALT')) {\" . PHP_EOL;\n                 global $wpdb;\n@@ -231,8 +231,8 @@\n  *\u002F\n ?>\" . $template;\n \n-        $template .= '\u002F\u002FNo detected memcached server - ' . gmdate('d\u002Fm\u002FY G:i:s', current_time('timestamp', 0)) . PHP_EOL;\n-        $template .= \"define('OC4EVERYONE_PREDEFINED_SERVER', '');\" . PHP_EOL;\n+        $template .= '\u002F\u002FNo detected memcached server - ' . gmdate('d\u002Fm\u002FY G:i:s', time()) . PHP_EOL;\n+        $template .= 'define(\\'OC4EVERYONE_PREDEFINED_SERVER\\', ' . var_export('', true) . ');' . PHP_EOL;\n \n         $template .= \"if (! defined('WP_CACHE_KEY_SALT')) {\" . PHP_EOL;\n         $template .= \"define('WP_CACHE_KEY_SALT', '\" . filemtime(__FILE__) . \"');\" . PHP_EOL;\n@@ -254,31 +254,40 @@\n     global $wp_object_cache;\n \n     if (strpos($plugin_file_name, basename(__FILE__)) && class_exists('Memcached') && method_exists($wp_object_cache, 'getStats') && file_exists(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'object-cache.php')) {\n+        \u002F\u002F Read the stats once; every value below comes from the Memcached server, so it is treated as untrusted and escaped on output.\n+        $all_stats = $wp_object_cache->getStats();\n+\n         \u002F\u002F Extra check.\n-        if(!defined('OC4EVERYONE_PREDEFINED_SERVER') || !array_key_exists(OC4EVERYONE_PREDEFINED_SERVER, $wp_object_cache->getStats())){\n-            return $links_array;        \n+        if (!defined('OC4EVERYONE_PREDEFINED_SERVER') || !array_key_exists(OC4EVERYONE_PREDEFINED_SERVER, $all_stats)) {\n+            return $links_array;\n         }\n \n-        $hits = $wp_object_cache->getStats()[OC4EVERYONE_PREDEFINED_SERVER]['get_hits'];\n+        $stats = $all_stats[OC4EVERYONE_PREDEFINED_SERVER];\n+\n+        $hits = isset($stats['get_hits']) ? (int) $stats['get_hits'] : 0;\n         \u002F\u002F Extra check.\n-        if($hits == 0) {\n-            return $links_array;        \n+        if ($hits === 0) {\n+            return $links_array;\n         }\n-        $misses = $wp_object_cache->getStats()[OC4EVERYONE_PREDEFINED_SERVER]['get_misses'];\n-        $total = $hits + $misses;\n-        $found = @round((100 \u002F $total) * $hits, 2);\n+        $misses = isset($stats['get_misses']) ? (int) $stats['get_misses'] : 0;\n+        $total  = $hits + $misses;\n+        $found  = $total > 0 ? round((100 \u002F $total) * $hits, 2) : 0;\n \n         if (defined('WP_DEBUG') && WP_DEBUG) {\n-            error_log('Object Cache 4 everyone::Memcached Server running');\n+            error_log('Object Cache 4 everyone::Memcached Server running'); \u002F\u002F phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log\n         }\n         $nonce = wp_create_nonce('flush_memcached_nonce');\n \n-        $links_array[] = '\u003Ca href=\"' . esc_url(admin_url('admin-post.php?action=oc4flush_memcached&nonce=' . $nonce)) . '\">\u003Cstrong>' . esc_html__('Flush cache', 'object-cache-4-everyone') . '\u003C\u002Fstrong>\u003C\u002Fa>' . \n+        $uptime      = isset($stats['uptime']) ? (int) $stats['uptime'] : 0;\n+        $curr_items  = isset($stats['curr_items']) ? $stats['curr_items'] : 0;\n+        $total_items = isset($stats['total_items']) ? $stats['total_items'] : 0;\n+\n+        $links_array[] = '\u003Ca href=\"' . esc_url(admin_url('admin-post.php?action=oc4flush_memcached&nonce=' . $nonce)) . '\">\u003Cstrong>' . esc_html__('Flush cache', 'object-cache-4-everyone') . '\u003C\u002Fstrong>\u003C\u002Fa>' .\n             '\u003Cbr\u002F>\u003Cbr\u002F>' .\n-            esc_html__('Memcached Server running:', 'object-cache-4-everyone') . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . OC4EVERYONE_PREDEFINED_SERVER . '\u003C\u002Fcode>\u003C\u002Fstrong>' . '\u003Cbr\u002F>' .\n-            esc_html__('Cache Hit Ratio', 'object-cache-4-everyone') . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . $found . '%\u003C\u002Fcode>\u003C\u002Fstrong>' . '\u003Cbr\u002F>' .\n-            esc_html__('Uptime:', 'object-cache-4-everyone')  . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . secondsToHumanReadable($wp_object_cache->getStats()[OC4EVERYONE_PREDEFINED_SERVER]['uptime']) . '\u003C\u002Fstrong>\u003C\u002Fcode>' . '\u003Cbr\u002F>' .\n-            esc_html__('Current Unique Items \u002F Total Items:', 'object-cache-4-everyone') . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . $wp_object_cache->getStats()[OC4EVERYONE_PREDEFINED_SERVER]['curr_items'] . ' \u002F ' . $wp_object_cache->getStats()[OC4EVERYONE_PREDEFINED_SERVER]['total_items'] . '\u003C\u002Fstrong>\u003C\u002Fcode>' . '\u003Cbr\u002F>';\n+            esc_html__('Memcached Server running:', 'object-cache-4-everyone') . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . esc_html(OC4EVERYONE_PREDEFINED_SERVER) . '\u003C\u002Fcode>\u003C\u002Fstrong>' . '\u003Cbr\u002F>' .\n+            esc_html__('Cache Hit Ratio', 'object-cache-4-everyone') . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . esc_html($found) . '%\u003C\u002Fcode>\u003C\u002Fstrong>' . '\u003Cbr\u002F>' .\n+            esc_html__('Uptime:', 'object-cache-4-everyone')  . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . esc_html(secondsToHumanReadable($uptime)) . '\u003C\u002Fcode>\u003C\u002Fstrong>' . '\u003Cbr\u002F>' .\n+            esc_html__('Current Unique Items \u002F Total Items:', 'object-cache-4-everyone') . ' \u003Cstrong>\u003Ccode style=\"background-color: inherit;\">' . absint($curr_items) . ' \u002F ' . absint($total_items) . '\u003C\u002Fcode>\u003C\u002Fstrong>' . '\u003Cbr\u002F>';\n     }\n \n     return $links_array;\n@@ -288,6 +297,10 @@\n \n add_action('admin_post_oc4flush_memcached', 'oc4flush_memcached');\n function oc4flush_memcached() {\n+    \u002F\u002F Authorization: only administrators may flush the cache. The nonce alone is not an authorization check.\n+    if (!current_user_can('manage_options')) {\n+        wp_die(esc_html__('Access denied.', 'object-cache-4-everyone'), '', array('response' => 403));\n+    }\n     if (!class_exists('Memcached')) {\n         wp_die(esc_html__('Failed to flush Memcached server', 'object-cache-4-everyone'));\n     }\n@@ -295,7 +308,6 @@\n     if (isset($_GET['nonce']) && wp_verify_nonce(sanitize_key($_GET['nonce']), 'flush_memcached_nonce')) {\n         \u002F\u002F Flush cache.\n         global $wp_object_cache;\n-        error_log(print_r($wp_object_cache->getStats()[OC4EVERYONE_PREDEFINED_SERVER], true));        \n         $wp_object_cache->flush();\n \n         $memcached = new Memcached();\n@@ -304,7 +316,7 @@\n \n         if ($memcached->flush(0)) {\n             if (defined('WP_DEBUG') && WP_DEBUG) {\n-                error_log('Object Cache 4 everyone::Memcached server flushed.');\n+                error_log('Object Cache 4 everyone::Memcached server flushed.'); \u002F\u002F phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log\n             }\n             wp_redirect(admin_url('plugins.php'));\n             exit;\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.2\u002Fobject-cache-disk-template.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.3\u002Fobject-cache-disk-template.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.2\u002Fobject-cache-disk-template.php\t2026-05-08 10:42:42.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fobject-cache-4-everyone\u002F2.3.3\u002Fobject-cache-disk-template.php\t2026-06-11 11:41:48.000000000 +0000\n@@ -33,9 +33,49 @@\n                 return;\n             }\n         }\n+        \u002F\u002F Block direct web access and directory listing of the cache tree.\n+        $this->protect_dir($this->local_path);\n+\n         $this->result_code = self::RES_SUCCESS;\n     }\n \n+    \u002F**\n+     * Drops an index.php and a deny-all .htaccess in a cache directory so the\n+     * serialized cache files cannot be listed or served over the web.\n+     * @param string $dir Directory to protect (with trailing separator).\n+     *\u002F\n+    private function protect_dir($dir)\n+    {\n+        global $wp_filesystem;\n+\n+        if (!$wp_filesystem->exists($dir . 'index.php')) {\n+            $wp_filesystem->put_contents($dir . 'index.php', '\u003C?php \u002F\u002F Silence is golden.');\n+        }\n+\n+        if (!$wp_filesystem->exists($dir . '.htaccess')) {\n+            $htaccess = \"\u003CIfModule mod_authz_core.c>\\nRequire all denied\\n\u003C\u002FIfModule>\\n\u003CIfModule !mod_authz_core.c>\\nOrder allow,deny\\nDeny from all\\n\u003C\u002FIfModule>\\n\";\n+            $wp_filesystem->put_contents($dir . '.htaccess', $htaccess);\n+        }\n+    }\n+\n+    \u002F**\n+     * Returns the secret used to sign cache files on disk.\n+     * @return string\n+     *\u002F\n+    private function hmac_key()\n+    {\n+        if (defined('AUTH_SALT') && AUTH_SALT) {\n+            return AUTH_SALT;\n+        }\n+        if (defined('SECURE_AUTH_SALT') && SECURE_AUTH_SALT) {\n+            return SECURE_AUTH_SALT;\n+        }\n+        return 'oc4everyone-disk-cache';\n+    }\n+\n     public function quit()\n     {\n         return false;\n@@ -125,7 +165,11 @@\n             $value = clone $value;\n         }\n \n-        $return = $wp_filesystem->put_contents($path, @serialize($value));\n+        \u002F\u002F Sign the serialized payload so a tampered cache file is rejected before unserialize().\n+        $serialized = @serialize($value);\n+        $blob       = hash_hmac('sha256', $serialized, $this->hmac_key()) . $serialized;\n+\n+        $return = $wp_filesystem->put_contents($path, $blob);\n         if (!$return) {\n             $this->result_code = self::RES_FAILURE;\n             return false;\n@@ -146,12 +190,22 @@\n         }\n \n         $objData = $wp_filesystem->get_contents($path);\n-        if ($objData === false) {\n+        if ($objData === false || strlen($objData) \u003C 64) {\n             $this->result_code = self::RES_FAILURE;\n             return false;\n         }\n \n-        $data = unserialize($objData);\n+        \u002F\u002F Verify the HMAC signature before unserializing.\n+        $stored_hmac = substr($objData, 0, 64);\n+        $serialized  = substr($objData, 64);\n+        if (!hash_equals(hash_hmac('sha256', $serialized, $this->hmac_key()), $stored_hmac)) {\n+            $this->result_code = self::RES_FAILURE;\n+            return false;\n+        }\n+\n+        $data = unserialize($serialized);\n \n         $this->result_code = self::RES_SUCCESS;\n         return $data;\n@@ -164,13 +218,9 @@\n         $array_hash = str_split($hash, 8); \u002F\u002F8 name based\n \n         $path = $this->local_path . implode(DIRECTORY_SEPARATOR, $array_hash);\n-        \n-        \u002F\u002F Ensure directory exists with an index.php to prevent directory listing\n-        if (!file_exists($path . DIRECTORY_SEPARATOR . 'index.php')) {\n-            @file_put_contents($path . DIRECTORY_SEPARATOR . 'index.php', '\u003C?php \u002F\u002F Silence is golden.');\n-        }\n \n-        $path .= DIRECTORY_SEPARATOR . '.object.php';\n+        \u002F\u002F Cache payload stored with a non-PHP extension so it is never executed even if served directly.\n+        $path .= DIRECTORY_SEPARATOR . '.object.cache';\n         return $path;\n     }\n }","The exploit involves predicting the URI of sensitive cache entries stored on disk when Memcached is unavailable. The plugin generates paths by calculating the MD5 hash of a cache key (such as '1:options:alloptions' for site settings or '1:users:1' for user metadata) and splitting that hash into 8-character segments to form a directory structure within '\u002Fwp-content\u002Fcache\u002Fobject\u002F'. Since these files use the '.object.php' extension and are not protected by .htaccess or authorization checks in vulnerable versions, an unauthenticated attacker can perform a direct HTTP GET request to the calculated path to download the serialized PHP data and extract sensitive information.","gemini-3-flash-preview","2026-06-25 22:43:35","2026-06-25 22:45:01",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","2.3.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fobject-cache-4-everyone\u002Ftags\u002F2.3.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fobject-cache-4-everyone.2.3.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fobject-cache-4-everyone\u002Ftags\u002F2.3.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fobject-cache-4-everyone.2.3.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fobject-cache-4-everyone\u002Ftags"]