Skip to content

Conversation

picandocodigo
Copy link
Owner

  • Don't skip password protected filter when showing content.
  • Sanitize post_status so some posts are only shown if user is Editor or Administrator.
  • Addresses reported vulnerability: CVE-2025-11377, Authenticated (Contributor+) Information Exposure. `
    • CVSS Severity Score: 4.3 (Medium)
    • CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
    • Organization: Wordfence
    • Vulnerability Researcher(s): Athiwat Tiprasaharn (Jitlada)

This is a low risk vulnerability that could potentially be executed by an authenticated attacker, with contributor-level access and above. But it should be fixed with this version.

Comment on lines +452 to +453
$lcp_content = get_the_password_form($single);
return $lcp_content;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@klemens-st I think it's ok to just return the content here, without the filters and replace we do in the else branch. Since get_the_password_form applies its own filters. The goal is not to show the content unless you've submitted the right password.
From my tests, if you input the right password, WordPress redirects you to the page and if you go back to where the posts are listed, it does show the content (if content=yes and show_protected=yes are set).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it works nicely and yes the get_the_password_form function seems to return display ready HTML and doesn't need further filters.

@klemens-st
Copy link
Collaborator

@picandocodigo I'll have a look and review tomorrow. Should probably also make sure the content isn't manualny fetched by any other features of the plugin so that this doesn't get resubmitted as a vulnerability.

Copy link
Collaborator

@klemens-st klemens-st left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The password protection fix is sufficient I think, but the issue with statuses is somewhat complicated and I'm not sure what is expected of us exactly. I've written in comments all I could think of but right now I don't have enough time to come up with a comprehensive fix. Perhaps later next week if you haven't solved it already by then.

Comment on lines +614 to +623
private function sanitize_status($statuses){
if (in_array('private', $statuses) || in_array('draft', $statuses)) {
if ( !( current_user_can('editor') || current_user_can('administrator')) ) {
$private_index = array_search('private', $statuses);
unset($statuses[$private_index]);
$draft_index = array_search('draft', $statuses);
unset($statuses[$draft_index]);
}
}
return implode(',', $statuses);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When array_search returns false, the unset statement destroys the first element of an array. Unfortunately PHP's type juggling converts false to 0 in numeric contexts so this method doesn't work as intended. Easiest fix:

private function sanitize_status($statuses){
    if (in_array('private', $statuses) || in_array('draft', $statuses)) {
      if ( !( current_user_can('editor') || current_user_can('administrator')) ) {
        $private_index = array_search('private', $statuses);
        if ($private_index !== false) {
          unset($statuses[$private_index]);
        }
        $draft_index = array_search('draft', $statuses);
        if ($draft_index !== false) {
          unset($statuses[$draft_index]);
        }
      }
    }
    return implode(',', $statuses);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the vulnerability anly about private and draft statuses? What about other statuses like pending or future? Once this method is fixed, users with Author role will be able to do e.g. [catlist post_status=future] and display a list of all scheduled posts to the public Internet, which will probably also raise some oversensitive eyebrows ;)

Another problem is this section of lcp-parameters.php:

//Get private posts
if( is_user_logged_in() ){
if ( !empty($args['post_status']) ){
$args['post_status'] = array_merge($args['post_status'], array('private'));
} else{
$args['post_status'] = array('private', 'publish');
}
}

No need to delete it but maybe just add a check if a user is Editor or Administrator.

Comment on lines +452 to +453
$lcp_content = get_the_password_form($single);
return $lcp_content;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it works nicely and yes the get_the_password_form function seems to return display ready HTML and doesn't need further filters.

@picandocodigo
Copy link
Owner Author

@klemens-st Thanks! I'll add that fix for the false to 0 casting 👍

The report only talks about private, draft and password protected. So I think it should be fine for now.
I'll try getting this out, but I'll be out on holidays soon, so I won't be around to check if anything breaks 😅

@klemens-st
Copy link
Collaborator

@picandocodigo Sure, as I said I'm going to have some time later this week so will be able to fix whatever is necesarry.

I've just had another thought about the new sanitize_status method: to make it bullet proof against security researchers' tests it has to also work if someone tries [catlist post_status="private,private,draft"] etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants