- Vulnerability Background
What is this vulnerability?
- CVE-2025-13693 is a stored cross-site scripting (XSS) vulnerability in the WordPress plugin “Image Photo Gallery Final Tiles Grid” (Final Tiles Grid Gallery Lite).
- The issue exists in the plugin’s “Custom scripts” setting, where user-supplied JavaScript is accepted and later rendered into pages without sufficient output sanitization/authorization.
Why is it critical/important?
- Stored XSS enables an attacker to persistently inject script code into content that is later viewed by other users.
- In the WordPress context, this is especially dangerous because authenticated authors/editors can target administrators or other privileged users who visit the injected gallery page.
- Execution of arbitrary scripts can lead to session theft, privilege escalation, content manipulation, or complete site compromise.
What systems/versions are affected?
- Affected: Final Tiles Grid Gallery Lite versions up to and including 3.6.8.
- The vulnerability is present in the plugin’s settings/UI handling and persists until the code changes described below are applied.
- Technical Details
Root cause analysis
-
The vulnerability is caused by two related flaws:
- Insufficient sanitization in
FinalTilesGalleryLite.phpwhen saving$_POST['ftg_script']for users without theunfiltered_htmlcapability. - Missing capability-based access control in
admin/include/fields.php, which exposed the custom script input field to users regardless of their permissions.
- Insufficient sanitization in
-
In
FinalTilesGalleryLite.php, the code attempted to sanitize input usingwp_strip_all_tags()for non-privileged users:wp_strip_all_tags()only removes HTML tags and does not prevent script injection in contexts where the value is inserted into JavaScript or inline event handlers.
-
In
admin/include/fields.php, the plugin added the “Custom scripts” textarea field for all users without validating that the current user had the right capability to supply arbitrary script content.
Attack vector and exploitation conditions
- The attacker needs authenticated access to the WordPress back end with Author-level access or above.
- The attacker must be able to access the gallery settings page and edit the “Custom scripts” field.
- Because the custom script is stored in plugin configuration and rendered on gallery pages, any user visiting the affected page can execute the injected payload.
Security implications
- Stored XSS in an admin-controlled page means that high-value users, including administrators, can be targeted with malicious scripts.
- A successful exploit can:
- execute arbitrary JavaScript in the context of the victim’s browser,
- steal authentication cookies or tokens,
- perform actions on behalf of the victim,
- inject additional payloads or backdoors into the site,
- degrade trust and integrity of the WordPress site.
- Patch Analysis
What code changes were made?
-
FinalTilesGalleryLite.php- Old behavior:
- If
current_user_can('unfiltered_html'), sanitize withwp_kses_post(wp_unslash($_POST['ftg_script'])). - Otherwise sanitize with
wp_strip_all_tags(wp_unslash($_POST['ftg_script'])).
- If
- New behavior:
- If
current_user_can('unfiltered_html'), preserve the sanitized post. - Otherwise set
$script = ''.
- If
- Old behavior:
-
admin/include/fields.php- Old behavior:
- Always add the “Custom scripts” textarea field.
- New behavior:
- Add the textarea field only if
current_user_can('unfiltered_html').
- Add the textarea field only if
- Old behavior:
How do these changes fix the vulnerability?
- The fix eliminates the unsafe attempt to accept custom script content from users who lack
unfiltered_html. - Non-privileged users no longer get a script value stored, preventing them from injecting arbitrary code into pages.
- The capability check prevents lower-privileged users from even seeing or using the field, reducing the attack surface.
Security improvements introduced
- Stronger access control: only users with
unfiltered_htmlcan provide custom scripts. - Safer input handling: unprivileged input is dropped rather than inadequately sanitized.
- Consistent enforcement between UI exposure and input processing.
- Proof of Concept (PoC) Guide
Prerequisites for exploitation
- WordPress installation with Final Tiles Grid Gallery Lite version <= 3.6.8.
- Authenticated user account with Author-level access or higher.
- Access to the gallery configuration interface where “Custom scripts” is available.
Step-by-step exploitation approach
- Log in to WordPress with an Author/editor account.
- Navigate to the plugin’s gallery settings or edit an existing gallery.
- Locate the “Custom scripts” field.
- Enter a payload such as:
alert('XSS') - Save the gallery.
- Visit the page containing the affected gallery as another authenticated user or administrator.
Expected behavior vs exploited behavior
- Expected behavior after patch:
- Non-privileged users do not see the custom script field.
- If they attempt to submit a script payload, the stored value is blanked out.
- No arbitrary JavaScript executes on gallery page load.
- Exploited behavior before patch:
- The payload is stored and later executed when any user loads the injected gallery page.
- The attacker can execute arbitrary browser-side code in the context of victim sessions.
How to verify the vulnerability exists
- Verify presence of the field in the admin interface for non-
unfiltered_htmlusers. - Submit a script payload through the custom script field.
- Inspect the stored settings or rendered gallery page for the injected payload.
- Visit the gallery page and confirm execution of the injected script.
- Recommendations
Mitigation strategies
- Update the plugin to a patched version that includes the fixes.
- Restrict the
unfiltered_htmlcapability to trusted administrator accounts only. - Remove or disable custom script functionality entirely if it is not required.
Detection methods
- Audit plugin settings and database entries for suspicious script fragments in gallery configuration.
- Monitor the admin interface for unauthorized access to custom script fields.
- Use web application scanners to detect stored XSS in plugin-managed pages.
Best practices to prevent similar issues
- Enforce capability-based access control consistently on both UI rendering and input processing paths.
- Do not rely on
wp_strip_all_tags()for XSS prevention in contexts where the input may be interpreted as executable code. - For custom script inputs, limit use to users with
unfiltered_htmland apply strict sanitization/escaping on output. - Review any plugin feature that allows arbitrary code or markup from users, especially when it is persisted and later rendered to other users.
- Implement least privilege for user roles and regularly audit role capabilities in WordPress.