Lucene search

K
seebugZ3r0yuSSV:92732
HistoryMar 04, 2017 - 12:00 a.m.

Wordpress < 4.7.1 - Username Enumeration (CVE-2017-5487)

2017-03-0400:00:00
Z3r0yu
www.seebug.org
1760

EPSS

0.874

Percentile

98.7%

Author: p0wd3r (know Chong Yu 404 security lab)

Date: 2017-03-05

0x00 vulnerability overview

Vulnerability description

Recently exploit-db is published on a Wordpress < 4.7.1 username enumeration vulnerabilities: <https://www.exploit-db.com/exploits/41497/&gt; , in fact, the vulnerability to 1-month 14, has been posted on the Internet, and given the CVE-2017-5487。 Using the vulnerability an attacker can be in the unauthorized state get the previously posted article of the user’s username, id and other information.

Vulnerability

Unauthorized state of access previously posted articles to the user’s username, id and other information.

Trigger premise: Wordpress configuration REST API

Affect version:< 4.7.1

0x01 vulnerability reproduction

Environment to build

Download the appropriate version of Wordpress, and then configure the REST API, specifically see: <https://www.seebug.org/vuldb/ssvid-92637&gt;

Reproduction

We first look at the exploit-db on the given exp is:

``php

! usr/bin/php

<? php

Author: Mateus a. k. a Dctor

fb: fb.com/hatbashbr/

E-mail: [email protected]

Site: https://mateuslino.tk

header (‘Content-type: text/html; charset=UTF-8’);

$url= “https://bucaneiras.org/”; $payload=“wp-json/wp/v2/users/”; $urli = file_get_contents($url.$ payload); $json = json_decode($urli, true); if($json){ echo “-----------------------------\n”; foreach($json as $users){ echo “[] ID : |" .$ users[‘id’] .“|\ n”; echo "[] Name: |” .$ users[‘name’] .“|\ n”; echo “[] User :|" .$ users[‘slug’] .“|\ n”; echo “\n”; }echo "-----------------------------";} else{echo "[] No user”;}

?> ``

You can see it is to use the REST API to get the user information, the corresponding file is wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php next use the exp and open the dynamic debug.

First, the program proceeds to get_items_permissions_check function:

``php /__ * Permissions check for getting all users. _ * @since 4.7.0 * @access public _ * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access, otherwise WP_Error object. */ public function get_items_permissions_check( $request ) { // Check if roles is specified in the GET request and if the user can list users. if ( ! empty( $request[‘roles’] ) && ! current_user_can( ‘list_users’ ) ) { return new WP_Error( ‘rest_user_cannot_view’, __( ‘Sorry, you are not allowed to filter users by role.’ ), array( ‘status’ => rest_authorization_required_code() ) ); }

 if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
 return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to list users.' ), array( 'status' =&gt; rest_authorization_required_code() ) );
}

 if ( in_array( $request['orderby'], array( 'email', 'registered_date' ), true ) && ! current_user_can( 'list_users' ) ) {
 return new WP_Error( 'rest_forbidden_orderby', __( 'Sorry, you are not allowed to order the users by this parameter.' ), array( 'status' =&gt; rest_authorization_required_code() ) );
}

 return true;
}

``

The function has three conditional statements, if the condition is satisfied it returns an error. But look carefully at each of the conditions is $request[xxx] && ! current_user_can( 'list_users' ), which also means that as long as the previous statement does not hold, then back current_user_can('list_users') will lose the role. As for the$request['roles'] and$request['context'] and$request['orderby'] value, by debugging we can see that the three values are as follows:

request_array.png

Do not meet the conditions, so the function returns true, the successfully passed the permission check.

Next, the program proceeds to the get_items function, the first is to set some query parameters and then use$query = new WP_User_Query( $prepared_args ); for the query, we directly in the WP_User_Query of the query function at the following breakpoints:

./break_query.png

$this-&gt;request is the execution of the query, its value is as follows:

sql SELECT SQL_CALC_FOUND_ROWS wp_users.* FROM wp_users WHERE 1=1 AND wp_users. ID IN ( SELECT DISTINCT wp_posts. post_author FROM wp_posts WHERE wp_posts. post_status = 'publish' AND wp_posts. post_type IN ( 'post', 'page', 'attachment' ) ) ORDER BY display_name ASC LIMIT 0, 10

This indicates that the API can access user must meet the following conditions:

  • Published articles
  • The article’s current status is publish
  • Post type is post, a page, an attachment in which one of the

In our environment, the admin user by default will have articles, so we perform exp after will give the admin some information:

./admin_info.png

Next we’ll create a new user, tommy, and then perform the exp found results and as above, the reason is because the also did not send the article. We log on tommy and publish an article, and then perform the exp: the

./tommy.png

This time you can get tommy information.

0x02 patch analysis

The official Wordpress of the given patch are as follows:

./commit.png

> Only show users that have authored a post of a post type that has show_in_rest set to true.

Mean only when the user post the type of show_in_rest property to true only when you can obtain the user information.

At the code level, patch set$prepared_args['has_published_posts'] value, the value in the construction of the query statement will be used to:

php if ( $qv['has_published_posts'] && $blog_id ) { if ( true === $qv['has_published_posts'] ) { $post_types = get_post_types( array( 'public' =&gt; true ) ); } else { $post_types = (array) $qv['has_published_posts']; } ...

The query$post_type is set to show_in_rest=true those types, then which type of show_in_rest is true?

In wp-includes/post.php in create_initial_post_types function can be seen in the post, the page and the attachment of the show_in_rest are true, and the patch before the query of the same type, that is in fact the latest version by default or you can use the exp, and the actual test result is also true:

exp_again.png

As for why this is, I believe that may be the API of the design intent is to let other people get to publish articles of user name, because the article has been disclosed, the user name is disclosed. This patch gives the user more customized space, because the user can own via register_post_type to create the post type, the patch provided in the show_in_rest attribute lets the user choose their own user information for API visibility.

This article is written really hastily if where have no place, also hope you a lot of advice.

0x03 reference