Missing Authorization Enables Contributor Media Deletion//Published on 2025-11-14//CVE-2025-12847

КОМАНДА БЕЗОПАСНОСТИ WP-FIREWALL

All In One SEO Pack CVE-2025-12847

Имя плагина All In One SEO Pack
Тип уязвимости Отсутствует авторизация
Номер CVE CVE-2025-12847
Срочность Низкий
Дата публикации CVE 2025-11-14
Исходный URL-адрес CVE-2025-12847

All In One SEO Pack <= 4.8.9 — Missing Authorization Allows Authenticated Contributor to Delete Arbitrary Media (CVE-2025-12847) — What Site Owners Must Do Now

Technical analysis and step-by-step mitigation guidance from WP-Firewall: CVE-2025-12847 in All In One SEO Pack permits authenticated contributors to delete media due to missing authorization checks. How to detect, mitigate, virtually patch and harden WordPress sites.

Дата: 2025-11-14
Автор: Команда безопасности WP-Firewall

Управляющее резюме

A recently disclosed vulnerability in the popular All In One SEO Pack plugin (versions ≤ 4.8.9) permits authenticated users with the Contributor role (or higher) to delete arbitrary media files because a server-side authorization (capability / nonce) check is missing or improperly enforced. The issue is tracked as CVE-2025-12847 and has a CVSS base score of 5.4 (classified as low severity). The vendor patched the issue in version 4.9.0.

Although the vulnerability is not remotely exploitable by unauthenticated users, it is serious for sites that allow untrusted or lightly trusted users to register and post content (for example multi-author blogs, membership sites that allow Contributor-level submission, or sites where staff and freelancers have Contributor access). An attacker with Contributor access can delete media assets, causing content loss, site breakage or reputational damage.

This post explains the vulnerability in plain technical terms, walks through detection and immediate mitigations you can apply (including virtual patching via a WAF), offers safe temporary code fixes and longer-term hardening guidance, and shows how WP-Firewall can help protect your site immediately — even before you apply the official plugin update.

Who should read this

  • Site owners and administrators using All In One SEO Pack on WordPress sites that allow contributor-level accounts or any non-admin user accounts with posting privileges.
  • WordPress professionals (developers, sysadmins, hosting teams) responsible for fast mitigation and recovery.
  • Security engineers who need to deploy virtual patches / WAF rules for hosting platforms or managed WordPress services.

What happened — the vulnerability in plain language

The All In One SEO Pack plugin implemented a function that accepts requests to delete media. That function lacked a proper authorization check (or nonce verification), which means the server did not verify that the currently authenticated user had the correct capability to perform the deletion. In WordPress capability terms, the code failed to confirm that a user can delete the targeted media attachment, and — crucially — allowed authenticated users with the Contributor role to reach a code path that deletes attachments.

Contributors normally cannot upload files by default, and they are not supposed to delete someone else’s attachments. But because the plugin exposed a deletion endpoint with missing or insufficient checks, a contributor could trigger deletion operations for arbitrary attachment IDs. The result is arbitrary media deletion — content loss and possible website disruption.

Vendor status: fixed in All In One SEO Pack 4.9.0. Affected versions: ≤ 4.8.9.

CVE: CVE-2025-12847

Severity: Low (CVSS 5.4). Patch priority: Low — but actionable for affected sites with Contributor-level users.

Why this matters (impact)

Even though the issue does not allow a full takeover, it can be weaponized in several ways:

  • Data loss and content removal: Attackers can remove featured images, downloadable files, and other media assets. That can break posts/pages or remove assets used for commerce (PDF invoices, product images).
  • Site disruption and SEO impact: Missing images or broken assets can reduce perceived quality and impact search engine results and user experience.
  • Escalation chain potential: Deleted media can force administrators to restore backups — an operational burden. In targeted attacks, deletion may be combined with other flaws or social engineering to pursue higher impact goals.
  • Trust abuse: On multi-author blogs, causing large-scale media deletions may be used as sabotage.

Technical analysis (what to look for)

A typical vulnerable implementation exhibits one or more of the following characteristics:

  • An AJAX or REST endpoint that accepts an attachment ID and deletes it (e.g., via wp_delete_attachment()) but does not call current_user_can() to confirm the requesting user has the capability to delete the given attachment.
  • No or improper nonce verification (no check for check_ajax_referer() or wp_verify_nonce()).
  • Capability checks performed against a generic capability that Contributors may accidentally have (for example delete_posts instead of delete_others_pages or delete_plugins), or checks that verify the existence of a user session only (is_user_logged_in()).
  • Endpoints hooked to admin-post.php, admin-ajax.php or to the WP REST API that lack the proper permission_callback.

Where to inspect in plugin code:

  • Look for use of wp_delete_attachment($attachment_id, true) or wp_trash_post() within plugin files.
  • Search for ajax endpoints: add_action(‘wp_ajax_…’, …) or add_action(‘wp_ajax_nopriv_…’, …) and for register_rest_route() calls where permission_callback might be missing or permissive.

Reproduction (high-level, responsible description)

We will not provide exploit code, but here is a high-level flow so admins and security teams understand how an attacker might abuse the issue:

  1. Attacker registers or obtains an account with Contributor privileges on a targeted site.
  2. Using the authenticated session, the attacker crafts a request to the plugin’s deletion endpoint (either an AJAX call or REST route) and supplies an attachment ID belonging to another user or any public ID they discovered.
  3. Because the endpoint lacks proper authorization checks, the server executes wp_delete_attachment() (or similar) and removes the specified media file.
  4. The attacker repeats the request over many IDs, removing multiple assets.

This shows why limiting registration and auditing user privileges matters.

Немедленные действия (пошаговые)

If your site uses All In One SEO Pack, take the following steps immediately. Apply steps in order from fastest to more intrusive:

  1. Upgrade the plugin to 4.9.0 or later (recommended)
      – The vendor published a fix in 4.9.0. Upgrading is the definitive fix. Test updates on staging first when possible.
  2. If you cannot upgrade immediately, apply one or more temporary mitigations:
      – Reduce contributor privileges temporarily (see role adjustments below).
      – Disable the plugin until you can update (if your site can operate without it).
      – Use your WAF to block or challenge suspicious requests to the plugin’s deletion endpoint (examples below).
      – Apply a small server-side capability check via a must-use mu-plugin to intercept requests and reject those missing proper permissions (sample code provided later).
  3. Enforce strong logging and monitoring:
      – Enable detailed request logging for POST/DELETE requests to admin-ajax.php, admin-post.php and wp-json/*.
      – Search logs for requests that contain attachment deletion parameters or repeated deletion attempts originating from Contributor accounts.
  4. Restore lost media (if deletion occurred):
      – If you have backups, restore missing media files. Prefer restoring media to a separate staging site and then re-uploading to production to avoid contaminating the backup with compromised data.
      – If backups are not available, retrieve files from the CDN if you use one (many CDNs retain copies for a time).

Quick role-based mitigation (immediate, non-technical)

If you are uncomfortable applying code changes, do this first:

  • Temporarily suspend Contributor accounts from posting privileges (change role to Subscriber) or remove user accounts you cannot verify.
  • Disable user registration (Settings → General → Membership).
  • Audit existing Contributor accounts and increase scrutiny for new account registrations by enabling email verification or manual approval workflows.

Temporary mu-plugin to block deletion calls

Create an mu-plugin (must-use plugin) to add a server-side gate that blocks requests to known deletion endpoints unless the current user is an Administrator. Place this file in wp-content/mu-plugins/ (create the folder if necessary).

Example mu-plugin (safe, non-destructive):

<?php
/*
Plugin Name: Emergency Media Deletion Guard
Description: Temporary guard: block unauthorized media deletion requests until All In One SEO Pack is patched.
Version: 1.0
Author: WP-Firewall Security Team
*/

add_action('init', function() {
    // Only intervene for logged in users. Adjust logic if endpoints are REST or AJAX.
    if ( ! is_user_logged_in() ) {
        return;
    }

    // Current user object
    $user = wp_get_current_user();

    // If the current user is an administrator, allow (do nothing).
    if ( in_array( 'administrator', (array) $user->roles, true ) ) {
        return;
    }

    // Define request indicators used by the vulnerable plugin.
    $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
    $method = $_SERVER['REQUEST_METHOD'];

    // Common places to watch: admin-ajax.php, admin-post.php, wp-json/ (REST)
    // and query parameters like 'attachment_id', 'media_id', 'delete_att' (plugin specific).
    $block_patterns = [
        '/admin-ajax\.php/i',
        '/admin-post\.php/i',
        '/wp-json/aioseo/i',  // example REST namespace - adjust per plugin's registered REST route
        '/wp-json/all-in-one-seo/i',
    ];

    foreach ($block_patterns as $pattern) {
        if ( preg_match($pattern, $request_uri) ) {
            // Inspect POST body for evidence of deletion
            if ( $method === 'POST' && ( isset($_POST['attachment_id']) || isset($_POST['media_id']) || isset($_POST['id']) ) ) {
                // Stop the request and respond 403.
                wp_die('Unauthorized request blocked by emergency guard.', 'Forbidden', array('response' => 403));
            }
        }
    }
}, 1);

Примечания:

  • This is a temporary defensive measure. Adjust patterns to match your site and the plugin’s actual endpoints.
  • Test on staging before deploying on production.

WAF / virtual patching guidance (rules you can deploy right now)

If you have a Web Application Firewall (WAF) such as the one provided by WP-Firewall or a hosting-level WAF, deploy rules that target the vulnerable behavior rather than the plugin name. Here are recommended WAF signatures and actions:

  1. Generic POST-blocking rule for deletion-like parameters
    • Match: HTTP method = POST AND (POST param names contain “attachment_id” OR “attachment” OR “media_id” OR “delete_attachment” OR “delete_media” OR “delete_att”)
    • Condition: Request URI contains “admin-ajax.php” OR “admin-post.php” OR URI starts with “/wp-json/”
    • Action: Challenge (CAPTCHA) or Block for authenticated sessions that are non-admin (determine via session cookie patterns) OR Block all if temporary risk tolerance is acceptable.
  2. REST route permission enforcement
    • Match: Requests to any /wp-json/* route that call endpoints where the handler name or namespace includes “aioseo” or “all-in-one-seo”.
    • Action: Require authentication AND verify role via a challenge or return 403 for sessions that are not administrator.
  3. Block suspicious parameter abuse
    • Match: Repeated POST with different numeric attachment IDs within a short time window (e.g., more than 5 deletion requests in 60 seconds).
    • Action: Temporary IP block (throttle) and notify administrator.
  4. Header and cookie checks
    • Match: Requests to admin-ajax.php that lack expected WP nonces (X-WP-Nonce header or WP nonce param).
    • Action: Block or challenge.
  5. Response body tampering
    • Monitor for 200 responses to deletion calls and log them as potential deletions so site owners can investigate quickly.

Example pseudo-WAF rule (conceptual):

  • Rule name: Block unauthorised attachment deletion attempts
  • Если:
      – HTTP_METHOD == POST AND
      – (REQUEST_URI MATCHES /admin-ajax.php|admin-post.php|^/wp-json/.*/i) AND
      – (REQUEST_BODY MATCHES /attachment_id=|media_id=|delete_attachment|delete_media|att_id=/i)
  • Затем:
      – If session cookie indicates non-admin role OR cannot identify role -> RETURN 403 and LOG

Важный: Tailor rules to your environment. False positives are possible; use challenge (CAPTCHA) as a softer action before full block.

Detection — how to tell if someone has already abused the bug

  1. Audit web and application logs
    • Look for POST requests to admin-ajax.php, admin-post.php, or relevant REST endpoints containing “attachment_id” or similarly named parameters.
    • Look for successful 200 responses followed by missing media. Match timestamps against media file deletion timestamps (e.g., file-system or CDN logs).
  2. WordPress audit trails
    • If you have logging plugins or centralized logging, search for calls to wp_delete_attachment in logs or custom debug outputs.
  3. Database and file checks
    • Media attachments are stored in wp_posts with post_type = ‘attachment’. Run queries to find recent deletions:
      SELECT * FROM wp_posts WHERE post_type = 'attachment' AND post_modified >= '2025-11-01' ORDER BY post_modified DESC;
    • Compare against backups to see what was removed.
  4. CDN and object storage
    • Check CDN logs and object storage for delete actions or missing objects. Sometimes CDN caches hold stale copies you can recover temporarily.
  5. User behavior
    • Inspect Contributor accounts for suspicious activity: rapid page edits, repetitive requests, or evidence of scripted behavior.

Контрольный список восстановления и реагирования на инциденты

  1. Изолировать:
    • Temporarily disable the vulnerable plugin or apply the mu-plugin guard/WAF rules.
    • Suspend suspicious user accounts (change roles to Subscriber or temporarily disable).
  2. Восстанавливаться:
    • Restore missing media from backup — ideally to a staging area first.
    • If you have CDN copies, re-pull media from there.
  3. Remediate:
    • Apply the plugin update to 4.9.0 or later.
    • Rotate admin and sensitive user credentials where appropriate.
    • Revoke active sessions for impacted users (WordPress allows session expiration or use plugins that can force sign-outs).
  4. Hardening:
    • Enforce least privilege for all user accounts.
    • Enable two-factor authentication for admin-level users.
    • Disable file editing in wp-admin (define('DISALLOW_FILE_EDIT', true);).
    • Limit the list of users who can upload and delete files.
  5. Монитор:
    • Increase monitoring for unusual deletions or file-system operations.
    • Implement WAF rules for deletion endpoints long-term.

Code-level fix suggestion for plugin developers

If you maintain or customize the plugin code and need a quick developer-level patch, ensure:

  1. Any public endpoint performing deletion must:
    • Verify a valid nonce with check_ajax_referer() or wp_verify_nonce() for AJAX/POST usage.
    • Use current_user_can() with the appropriate capability, e.g., current_user_can(‘delete_post’, $attachment_id) or current_user_can(‘delete_others_posts’) as applicable.
    • Validate the attachment ID and ensure it belongs to the appropriate context (e.g., belongs to the current post or current user if that is required by your policy).

Example snippet to validate capability before deletion:

<?php
// $attachment_id should be integer and sanitized
$attachment_id = isset($_POST['attachment_id']) ? intval($_POST['attachment_id']) : 0;

if ( ! $attachment_id ) {
    wp_send_json_error( array( 'message' => 'Invalid attachment ID' ), 400 );
}

// Nonce check for AJAX
if ( ! isset($_POST['nonce']) || ! wp_verify_nonce( sanitize_text_field($_POST['nonce']), 'aioseo_delete_attachment' ) ) {
    wp_send_json_error( array( 'message' => 'Missing or invalid nonce' ), 403 );
}

// Capability check
if ( ! current_user_can( 'delete_post', $attachment_id ) ) {
    wp_send_json_error( array( 'message' => 'Insufficient permissions' ), 403 );
}

// Now safe to delete (or trash)
$result = wp_delete_attachment( $attachment_id, true );

if ( $result ) {
    wp_send_json_success( array( 'message' => 'Attachment deleted' ) );
} else {
    wp_send_json_error( array( 'message' => 'Failed to delete' ), 500 );
}

This pattern ensures the request came from a user who can delete that attachment and that the request was authorized by a nonce.

Рекомендации по долгосрочному закаливанию

  1. Принцип наименьших привилегий:
    • Ensure user roles and capabilities are minimal. Contributors should not be able to delete attachments belonging to others.
  2. Harden plugin development:
    • All plugin download/upload/delete operations must check nonce and capability.
    • REST endpoints must provide secure permission_callback functions.
  3. Version control and environment policy:
    • Test plugin updates in staging with the same user roles and content model.
    • Apply scheduled plugin updates where safe, and consider auto-updating security patches if you have a safety net.
  4. Backup policy:
    • Maintain frequent off-site backups of both database and wp-content/uploads.
    • Test restores regularly.
  5. Logging and alerting:
    • Log administrative actions and send alerts for mass deletions or unusual patterns.
  6. Account and registration controls:
    • Limit public registration where not needed.
    • Use moderation or admin approval for first-time contributors.

How WP-Firewall helps (virtual patching and detection)

At WP-Firewall we provide both managed firewall rules and automated virtual patching that can block exploitation patterns before you can apply an official plugin update. If you are using our free plan or paid tiers, you get protection immediately:

  • Managed rule sets that target deletion-like requests (see WAF guidance above) so attempts to abuse missing authorization are blocked at the edge.
  • Malware scanning and heuristic detection to detect suspicious activity correlated with deletion requests.
  • Detailed alerts and logs so you can spot attempts to exploit the plugin and respond quickly.
  • For paid tiers, auto virtual patching can be applied across your fleet so you are protected even if you defer plugin updates for compatibility testing.

If you prefer to fix the plugin code yourself, we still recommend deploying WAF rules as a safety layer because not all environments update instantly.

Sign up for immediate basic protection (Free)

Get Immediate Protection — Start with WP-Firewall Free Plan

If you want immediate, low-friction protection while you upgrade or investigate, try WP-Firewall Basic (Free). The free plan gives essential protection: a managed firewall, unlimited bandwidth, WAF, malware scanning, and mitigation for OWASP Top 10 risks — enough to block common exploitation patterns like the one described here. Start protecting your site right away:

https://my.wp-firewall.com/buy/wp-firewall-free-plan/

If you need automated recovery aids, targeted virtual patches or dedicated support, our paid plans add those capabilities at progressively higher tiers.

Practical checklist you can use right now

  • Upgrade All In One SEO Pack to 4.9.0 or later (best).
  • If immediate upgrade not possible:
    • Disable the plugin temporarily, or
    • Implement the mu-plugin guard, or
    • Deploy WAF rules described above.
  • Audit contributor accounts and temporary suspend or downgrade unverified accounts.
  • Search logs for POST requests to admin-ajax.php/admin-post.php/wp-json that include deletion-like parameters (attachment_id, media_id).
  • Restore any deleted media from backups or CDN copies.
  • Enforce role and capability review: ensure only trusted users can delete media.
  • Enable continuous monitoring and alerts for abnormal deletion activity.
  • Schedule plugin updates and test them in staging prior to production rollouts.

Часто задаваемые вопросы (FAQ)

Q: Can unauthenticated users exploit this?
A: No. The vulnerability requires an authenticated session. However, sites that allow user registration or have multiple authors remain at risk.

Q: Will my backups be sufficient?
A: Backups are vital. If you have recent backups, restoration is straightforward. If not, check CDN caches or object storage, and implement a recovery plan.

В: Приведет ли отключение плагина к поломке моего сайта?
A: It depends. All In One SEO Pack controls SEO meta and sitemaps; disabling it may affect site metadata but usually won’t break core functionality. Test in staging if possible.

Q: Is virtual patching safe?
A: Yes. Virtual patching is a defensive layer implemented at the WAF level. It doesn’t change plugin code but blocks exploit traffic. It’s an effective stop-gap while you apply the official patch.

Final notes from WP-Firewall Security Team

Broken access control issues like this one are a reminder that server-side authorization and nonce checks are essential on every endpoint that changes content or files. Even functionality that “should” be limited by role can become risky if the server assumes client-side enforcement is enough. A layered defense — least privilege, robust logging, quick patching, and WAF virtual patching — is the right approach.

If you want help assessing your exposure, deploying WAF rules tailored to this vulnerability, or rapidly restoring deleted media, our security team can assist. Start with the free plan to get immediate baseline protection and then consider upgrading if you need automatic virtual patching and managed support.

Stay safe, keep your plugins updated, and treat any endpoint that modifies files or content as a sensitive one.


wordpress security update banner

Получайте WP Security Weekly бесплатно 👋
Зарегистрируйтесь сейчас
!!

Подпишитесь, чтобы каждую неделю получать обновления безопасности WordPress на свой почтовый ящик.

Мы не спамим! Читайте наши политика конфиденциальности для получения более подробной информации.