CVE-2025-15466

Image Photo Gallery Final Tiles Grid <= 3.6.9 - Missing Authorization to Authenticated (Contributor+) Arbitrary Gallery Management

mediumMissing Authorization
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
3.6.10
Patched in
1d
Time to patch

Description

The Image Photo Gallery Final Tiles Grid plugin for WordPress is vulnerable to unauthorized access and modification of data due to missing capability checks on multiple AJAX actions in all versions up to, and including, 3.6.9. This makes it possible for authenticated attackers, with Contributor-level access and above, to view, create, modify, clone, delete, and reassign ownership of galleries created by other users, including administrators.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.6.9
PublishedJanuary 19, 2026
Last updatedJanuary 19, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets **CVE-2025-15466**, a missing authorization vulnerability in the **Image Photo Gallery Final Tiles Grid** plugin. ### 1. Vulnerability Summary The plugin fails to implement proper capability checks (e.g., `current_user_can( 'manage_options' )` or ownership verification) i…

Show full research plan

This research plan targets CVE-2025-15466, a missing authorization vulnerability in the Image Photo Gallery Final Tiles Grid plugin.

1. Vulnerability Summary

The plugin fails to implement proper capability checks (e.g., current_user_can( 'manage_options' ) or ownership verification) in its AJAX request handlers. While these handlers are registered for authenticated users (wp_ajax_ hooks), they do not verify that the requester has the necessary permissions to manage galleries. This allows any authenticated user with at least Contributor-level access to manipulate galleries belonging to other users, including administrators.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Vulnerable Actions:
    • ftg_save_gallery (Create/Modify)
    • ftg_delete_gallery (Delete)
    • ftg_duplicate_gallery (Clone)
  • Authentication: Authenticated (Contributor+)
  • Payload Parameter: id (Gallery ID), gallery (JSON data for saving)
  • Preconditions: The attacker must be logged in as a Contributor or higher.

3. Code Flow (Inferred)

  1. Registration: The plugin (likely in lib/FinalTilesGalleryAdmin.php) registers AJAX actions using add_action( 'wp_ajax_ftg_delete_gallery', array( $this, 'delete_gallery' ) );.
  2. Entry Point: When a Contributor sends a POST request to admin-ajax.php with action=ftg_delete_gallery, the delete_gallery method is invoked.
  3. Missing Check: Inside delete_gallery, the code checks for a valid nonce but fails to call current_user_can().
  4. Database Sink: The code proceeds to delete the record from the custom database table (usually {$wpdb->prefix}ftg_galleries) based on the provided id parameter.

4. Nonce Acquisition Strategy

The plugin likely localizes admin scripts and provides a nonce. Contributors have access to the WordPress dashboard (/wp-admin/), so they can retrieve nonces intended for administrative use.

  1. Script Localization: Look for wp_localize_script calls in the plugin source (often in an admin-init related hook).
  2. JS Variable: The script likely outputs a variable named ftg_admin_vars or FinalTilesGallery (inferred).
  3. Acquisition Steps:
    • Log in as the Contributor.
    • Navigate to the WordPress Dashboard (/wp-admin/index.php).
    • Use browser_eval to extract the nonce:
      // Example check (adjust based on actual script name found during exploration)
      window.ftg_admin_vars?.nonce || window.FinalTilesGallery?.nonce
      
    • If the scripts only load on the gallery page (which a Contributor might not see in the menu), check if the Contributor can access /wp-admin/admin.php?page=final-tiles-grid-gallery-lite. If access is blocked, the nonce might still be available on the main dashboard if the plugin loads scripts globally in the admin.

5. Exploitation Strategy

We will demonstrate the vulnerability by deleting a gallery created by the Administrator.

Step 1: Identify Target Gallery
Obtain the ID of a gallery owned by the Admin. (e.g., ID 1).

Step 2: Prepare the Attack Request

  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=ftg_delete_gallery&id=1&nonce=[EXTRACTED_NONCE]
    

Step 3: Execute via http_request
Submit the request using the Contributor's session cookies.

6. Test Data Setup

  1. Admin Gallery: Create a gallery as an Administrator.
    • Command: Use wp eval to insert a row into the {prefix}ftg_galleries table or use the plugin UI.
  2. Attacker User: Create a user with the Contributor role.
    • Command: wp user create attacker attacker@example.com --role=contributor --user_pass=password
  3. Identify Table: Confirm the table name using wp db tables | grep ftg.

7. Expected Results

  • The admin-ajax.php response should return a success code (likely JSON {"success": true} or a string like "1").
  • The gallery with the targeted ID should be removed from the database, even though it was owned by an Admin and the request came from a Contributor.

8. Verification Steps

  1. Check Database: Run a SQL query via WP-CLI to verify the gallery is gone.
    wp db query "SELECT * FROM wp_ftg_galleries WHERE id = 1"
    
    Successful exploit if 0 rows are returned.
  2. Check Plugin UI: Attempt to view the gallery list as Admin; the deleted gallery should be missing.

9. Alternative Approaches

If ftg_delete_gallery is strictly protected, attempt Arbitrary Ownership Reassignment or Modification:

  • Action: ftg_save_gallery
  • Payload: action=ftg_save_gallery&gallery={"id":"1","name":"Hacked By Contributor"}&nonce=[NONCE]
  • Goal: Prove that a Contributor can change the name of an Admin's gallery.

If the nonce is hard to find, check if the plugin uses a generic nonce like wp_create_nonce( 'ftg_gallery' ) which might be exposed in the frontend if a gallery is embedded via shortcode:

  1. Admin creates a page: wp post create --post_content='[final-tiles-grid-gallery id="1"]' --post_status=publish
  2. Attacker (Contributor) views the public page and inspects the source for localized scripts or data attributes containing a nonce.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Image Photo Gallery Final Tiles Grid plugin fails to perform capability checks in multiple AJAX handlers, including those for saving, deleting, and duplicating galleries. This allows authenticated users with Contributor-level access or higher to manage any gallery on the site, regardless of ownership, by exploiting accessible nonces within the WordPress admin dashboard.

Vulnerable Code

// lib/FinalTilesGalleryAdmin.php

add_action( 'wp_ajax_ftg_save_gallery', array( $this, 'save_gallery' ) );
add_action( 'wp_ajax_ftg_delete_gallery', array( $this, 'delete_gallery' ) );
add_action( 'wp_ajax_ftg_duplicate_gallery', array( $this, 'duplicate_gallery' ) );

// ...

public function delete_gallery() {
    check_ajax_referer('ftg_gallery', 'nonce');

    $id = (int)$_POST['id'];
    // Missing capability check (e.g., current_user_can('manage_options'))
    $this->db->delete_gallery($id);

    echo "1";
    die();
}

---

// lib/FinalTilesGalleryAdmin.php

public function save_gallery() {
    check_ajax_referer('ftg_gallery', 'nonce');

    $gallery = json_decode(stripslashes($_POST['gallery']));
    // Missing capability check (e.g., current_user_can('manage_options'))
    $id = $this->db->save_gallery($gallery);

    echo $id;
    die();
}

Security Fix

--- a/lib/FinalTilesGalleryAdmin.php
+++ b/lib/FinalTilesGalleryAdmin.php
@@ -242,6 +242,10 @@
     public function delete_gallery() {
         check_ajax_referer('ftg_gallery', 'nonce');
 
+        if (!current_user_can('manage_options')) {
+            wp_die(__('You do not have sufficient permissions to perform this action.'));
+        }
+
         $id = (int)$_POST['id'];
         $this->db->delete_gallery($id);
 
@@ -253,6 +257,10 @@
     public function save_gallery() {
         check_ajax_referer('ftg_gallery', 'nonce');
 
+        if (!current_user_can('manage_options')) {
+            wp_die(__('You do not have sufficient permissions to perform this action.'));
+        }
+
         $gallery = json_decode(stripslashes($_POST['gallery']));
         $id = $this->db->save_gallery($gallery);

Exploit Outline

To exploit this vulnerability, an attacker first authenticates as a Contributor and accesses the WordPress dashboard to retrieve a valid security nonce (typically found in localized scripts like 'ftg_admin_vars'). The attacker then sends an unprivileged POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to a vulnerable handler (such as 'ftg_delete_gallery' or 'ftg_save_gallery'), providing the target gallery ID and the acquired nonce. Because the server-side handler lacks capability checks, it executes the requested database operation (deletion, modification, or creation) on behalf of the Contributor, even for galleries owned by Administrators.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.