XMLRPC: Prevent authentication from occuring after a failed authentication attmept in any single XML-RPC call.
This hardens WordPress against a common vector which uses multiple user identifiers in a single `system.multicall` call. In the event that authentication fails, all following authentication attempts ''in that call'' will also fail. Props dd32, johnbillion. Fixes #34336 git-svn-id: https://develop.svn.wordpress.org/trunk@35366 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
3a992e266f
commit
75ab50c70e
@ -45,6 +45,14 @@ class wp_xmlrpc_server extends IXR_Server {
|
|||||||
*/
|
*/
|
||||||
public $error;
|
public $error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags that the user authentication has failed in this instance of wp_xmlrpc_server.
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $auth_failed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register all of the XMLRPC methods that XMLRPC server understands.
|
* Register all of the XMLRPC methods that XMLRPC server understands.
|
||||||
*
|
*
|
||||||
@ -251,11 +259,18 @@ class wp_xmlrpc_server extends IXR_Server {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = wp_authenticate($username, $password);
|
if ( $this->auth_failed ) {
|
||||||
|
$user = new WP_Error( 'login_prevented' );
|
||||||
|
} else {
|
||||||
|
$user = wp_authenticate( $username, $password );
|
||||||
|
}
|
||||||
|
|
||||||
if (is_wp_error($user)) {
|
if ( is_wp_error( $user ) ) {
|
||||||
$this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) );
|
$this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) );
|
||||||
|
|
||||||
|
// Flag that authentication has failed once on this wp_xmlrpc_server instance
|
||||||
|
$this->auth_failed = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter the XML-RPC user login error message.
|
* Filter the XML-RPC user login error message.
|
||||||
*
|
*
|
||||||
|
@ -11,11 +11,14 @@ class WP_XMLRPC_UnitTestCase extends WP_UnitTestCase {
|
|||||||
|
|
||||||
add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
|
add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
|
||||||
|
|
||||||
$this->myxmlrpcserver = new wp_xmlrpc_server();
|
$this->myxmlrpcserver = new WP_XMLRPC_Server_UnitTestable();
|
||||||
}
|
}
|
||||||
|
|
||||||
function tearDown() {
|
function tearDown() {
|
||||||
remove_filter( 'pre_option_enable_xmlrpc', '__return_true' );
|
remove_filter( 'pre_option_enable_xmlrpc', '__return_true' );
|
||||||
|
|
||||||
|
$this->myxmlrpcserver->reset_failed_auth();
|
||||||
|
|
||||||
$this->remove_added_uploads();
|
$this->remove_added_uploads();
|
||||||
|
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
@ -29,3 +32,9 @@ class WP_XMLRPC_UnitTestCase extends WP_UnitTestCase {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WP_XMLRPC_Server_UnitTestable extends wp_xmlrpc_server {
|
||||||
|
public function reset_failed_auth() {
|
||||||
|
$this->auth_failed = false;
|
||||||
|
}
|
||||||
|
}
|
@ -19,10 +19,78 @@ class Tests_XMLRPC_Basic extends WP_XMLRPC_UnitTestCase {
|
|||||||
function test_login_pass_ok() {
|
function test_login_pass_ok() {
|
||||||
$user_id = $this->make_user_by_role( 'subscriber' );
|
$user_id = $this->make_user_by_role( 'subscriber' );
|
||||||
|
|
||||||
$this->assertFalse( $this->myxmlrpcserver->login_pass_ok( 'username', 'password' ) );
|
|
||||||
$this->assertFalse( $this->myxmlrpcserver->login( 'username', 'password' ) );
|
|
||||||
|
|
||||||
$this->assertTrue( $this->myxmlrpcserver->login_pass_ok( 'subscriber', 'subscriber' ) );
|
$this->assertTrue( $this->myxmlrpcserver->login_pass_ok( 'subscriber', 'subscriber' ) );
|
||||||
$this->assertInstanceOf( 'WP_User', $this->myxmlrpcserver->login( 'subscriber', 'subscriber' ) );
|
$this->assertInstanceOf( 'WP_User', $this->myxmlrpcserver->login( 'subscriber', 'subscriber' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test_login_pass_bad() {
|
||||||
|
$user_id = $this->make_user_by_role( 'subscriber' );
|
||||||
|
|
||||||
|
$this->assertFalse( $this->myxmlrpcserver->login_pass_ok( 'username', 'password' ) );
|
||||||
|
$this->assertFalse( $this->myxmlrpcserver->login( 'username', 'password' ) );
|
||||||
|
|
||||||
|
// The auth will still fail due to authentication blocking after the first failed attempt
|
||||||
|
$this->assertFalse( $this->myxmlrpcserver->login_pass_ok( 'subscriber', 'subscriber' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ticket 34336
|
||||||
|
*/
|
||||||
|
function test_multicall_invalidates_all_calls_after_invalid_call() {
|
||||||
|
$editor_id = $this->make_user_by_role( 'editor' );
|
||||||
|
$post_id = self::factory()->post->create( array(
|
||||||
|
'post_author' => $editor_id,
|
||||||
|
) );
|
||||||
|
|
||||||
|
$method_calls = array(
|
||||||
|
// Valid login
|
||||||
|
array(
|
||||||
|
'methodName' => 'wp.editPost',
|
||||||
|
'params' => array(
|
||||||
|
0,
|
||||||
|
'editor',
|
||||||
|
'editor',
|
||||||
|
$post_id,
|
||||||
|
array(
|
||||||
|
'title' => 'Title 1',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// *Invalid* login
|
||||||
|
array(
|
||||||
|
'methodName' => 'wp.editPost',
|
||||||
|
'params' => array(
|
||||||
|
0,
|
||||||
|
'editor',
|
||||||
|
'password',
|
||||||
|
$post_id,
|
||||||
|
array(
|
||||||
|
'title' => 'Title 2',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Valid login
|
||||||
|
array(
|
||||||
|
'methodName' => 'wp.editPost',
|
||||||
|
'params' => array(
|
||||||
|
0,
|
||||||
|
'editor',
|
||||||
|
'editor',
|
||||||
|
$post_id,
|
||||||
|
array(
|
||||||
|
'title' => 'Title 3',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->myxmlrpcserver->callbacks = $this->myxmlrpcserver->methods;
|
||||||
|
|
||||||
|
$result = $this->myxmlrpcserver->multiCall( $method_calls );
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey( 'faultCode', $result[0] );
|
||||||
|
$this->assertArrayHasKey( 'faultCode', $result[1] );
|
||||||
|
$this->assertArrayHasKey( 'faultCode', $result[2] );
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user