Gallery by FooGallery <= 3.1.9 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Gallery Metadata Exposure
Description
The Gallery by FooGallery plugin for WordPress is vulnerable to unauthorized access of data due to a missing capability check on the ajax_get_gallery_info() function in all versions up to, and including, 3.1.9. This makes it possible for authenticated attackers, with Subscriber-level access and above, to retrieve metadata (name, image count, thumbnail URL) of private, draft, and password-protected galleries by enumerating gallery IDs.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=3.1.9Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-15524 (FooGallery Metadata Exposure) ## 1. Vulnerability Summary The **Gallery by FooGallery** plugin (<= 3.1.9) contains a missing authorization vulnerability in its AJAX handler for retrieving gallery information. The function `ajax_get_gallery_info()` fails…
Show full research plan
Exploitation Research Plan: CVE-2025-15524 (FooGallery Metadata Exposure)
1. Vulnerability Summary
The Gallery by FooGallery plugin (<= 3.1.9) contains a missing authorization vulnerability in its AJAX handler for retrieving gallery information. The function ajax_get_gallery_info() fails to perform capability checks (e.g., current_user_can) or check the post_status of the requested gallery. This allows any authenticated user, including those with Subscriber roles, to retrieve metadata (title, image count, and thumbnail URLs) for galleries that should be restricted, such as those in Draft, Private, or Password-Protected states.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
foogallery_get_gallery_info(inferred from plugin naming conventions and function name) - Vulnerable Parameter:
gallery_id(orpost_id) - Authentication: Authenticated (Subscriber-level or higher)
- Payload: A numeric ID corresponding to a private or draft gallery.
- Preconditions: The attacker must be logged in as a Subscriber and must obtain a valid nonce if the function calls
check_ajax_referer.
3. Code Flow (Inferred)
- Registration: The plugin registers the AJAX hook:
add_action( 'wp_ajax_foogallery_get_gallery_info', array( $class, 'ajax_get_gallery_info' ) ); - Entry Point: An authenticated user sends a POST request to
admin-ajax.phpwithaction=foogallery_get_gallery_info. - Vulnerable Function:
ajax_get_gallery_info()is called. - Missing Check: The function likely calls
get_post( $_POST['gallery_id'] )or a FooGallery wrapper without verifying the user's capability to view that specific post or checking if the post status is 'publish'. - Information Leak: The function constructs an array containing the gallery's title, image count, and thumbnail URL and returns it as a JSON response.
4. Nonce Acquisition Strategy
While the vulnerability is "Missing Authorization," WordPress AJAX handlers usually require a nonce for CSRF protection. Even if the capability check is missing, check_ajax_referer might still be present.
- Identify Nonce Location: FooGallery often localizes admin data for its gallery picker or settings.
- Creation of Trigger Page: As an administrator, create a page containing a FooGallery shortcode to see if the plugin enqueues scripts that include nonces.
wp post create --post_type=page --post_status=publish --post_title="Gallery Test" --post_content='[foogallery id="EXISTING_ID"]' - Browser Extraction: Navigate to the site as the Subscriber user. Even if the Subscriber cannot edit galleries, FooGallery scripts may load on the frontend or in the Subscriber's profile page.
- JavaScript Variable: Look for localized scripts. Common FooGallery keys include:
window.FooGallery_Admin_Settings?.noncewindow.foogallery_vars?.noncewindow.FooGallery_Picker_Data?.nonce
- Action String: The nonce action is likely
'foogallery_nonce','foogallery_admin', or'foogallery-get-gallery-info'.
5. Exploitation Strategy
Step 1: Discover Target IDs
The attacker can enumerate gallery IDs (integers) to find hidden content.
Step 2: Send Exploitation Request
Using the http_request tool, send a request as the Subscriber:
Request Template:
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=foogallery_get_gallery_info&gallery_id=[TARGET_ID]&_wpnonce=[NONCE]
Step 3: Analyze Response
A successful exploit will return a 200 OK with a JSON body similar to:
{
"success": true,
"data": {
"id": 123,
"title": "Top Secret Private Gallery",
"image_count": 45,
"thumbnail_url": "http://localhost:8080/wp-content/uploads/..."
}
}
6. Test Data Setup
- Administrator Actions:
- Install FooGallery 3.1.9.
- Create a Private Gallery:
wp post create --post_type=foogallery --post_status=private --post_title="Private Leak" - Create a Draft Gallery:
wp post create --post_type=foogallery --post_status=draft --post_title="Draft Leak" - Note the IDs of these galleries.
- Subscriber Actions:
- Create a Subscriber user:
wp user create victim victim@example.com --role=subscriber --user_pass=password
- Create a Subscriber user:
7. Expected Results
- The Subscriber should be able to receive a JSON response containing the titles ("Private Leak", "Draft Leak") and metadata of the galleries created in Step 6.
- Normally, a Subscriber should receive an error (e.g.,
403 Forbiddenor{"success":false}) when requesting info for a non-published gallery they do not own.
8. Verification Steps
- Check IDs: Use
wp post list --post_type=foogalleryto confirm the IDs and statuses. - Compare Output: Match the
titleandimage_countreturned in the AJAX response with the actual metadata of the private/draft galleries in the database. - Confirm Role: Ensure the
victimuser has only thesubscriberrole:wp user get victim.
9. Alternative Approaches
- Enum via Shortcode Preview: If
foogallery_get_gallery_inforequires a higher privilege, check for other AJAX actions likefoogallery_load_moreorfoogallery_get_gallery_templatewhich might also lack authorization checks. - Nonce Bypass: Check if
ajax_get_gallery_infocallscheck_ajax_refererwith thedieparameter set tofalse. If it does, the request will proceed even with an invalid nonce. - REST API: Check if the metadata is also exposed via the WordPress REST API (
/wp-json/wp/v2/foogallery/[ID]) if the plugin incorrectly registered the post type asshow_in_rest => true.
Summary
The Gallery by FooGallery plugin for WordPress fails to implement proper authorization checks in its AJAX handler for retrieving gallery information. This allows authenticated users with Subscriber-level permissions to retrieve metadata, including titles and thumbnail URLs, for galleries that are otherwise restricted, such as those in Private, Draft, or Password-Protected states.
Vulnerable Code
// Inferred from research plan and plugin architecture // File: includes/admin/class-foogallery-admin.php public function ajax_get_gallery_info() { check_ajax_referer( 'foogallery_nonce', 'nonce' ); $gallery_id = intval( $_POST['gallery_id'] ); $gallery = get_post( $gallery_id ); if ( $gallery ) { $data = array( 'id' => $gallery->ID, 'title' => $gallery->post_title, 'image_count' => foogallery_get_image_count( $gallery_id ), 'thumbnail_url' => foogallery_get_thumbnail_url( $gallery_id ) ); wp_send_json_success( $data ); } wp_send_json_error(); }
Security Fix
@@ -10,6 +10,10 @@ public function ajax_get_gallery_info() { check_ajax_referer( 'foogallery_nonce', 'nonce' ); + if ( ! current_user_can( 'edit_posts' ) ) { + wp_send_json_error( 'Unauthorized' ); + } + $gallery_id = intval( $_POST['gallery_id'] ); $gallery = get_post( $gallery_id );
Exploit Outline
The exploit involves an authenticated Subscriber-level user enumerating gallery IDs to extract metadata from restricted galleries. First, the attacker logs in and obtains a valid AJAX nonce, which is typically exposed in localized JavaScript variables (e.g., `foogallery_vars` or `FooGallery_Admin_Settings`) on the WordPress dashboard or frontend. Using this nonce, the attacker sends a POST request to `/wp-admin/admin-ajax.php` with the action parameter set to `foogallery_get_gallery_info` and the `gallery_id` of a private or draft gallery. Because the function lacks a capability check (like `current_user_can('edit_posts')`), the server responds with a JSON object containing the gallery's title, image count, and thumbnail URLs regardless of the post status or the user's lack of viewing permissions.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.