Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/wp-includes/pluggable.php
Original file line number Diff line number Diff line change
Expand Up @@ -774,12 +774,18 @@ function wp_logout() {
*
* @global int $login_grace_period
*
* @param string $cookie Optional. If used, will validate contents instead of cookie's.
* @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
* @param int|string $cookie Optional. User ID if passed via 'determine_current_user' filter,
* or cookie string to validate. Default empty string.
* @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
* Note: This does *not* default to 'auth' like other cookie functions.
* @return int|false User ID if valid cookie, false if invalid.
* @return int|false User ID if valid cookie, false if invalid. If a user ID from an earlier filter
* callback is received, that value is returned.
*/
function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
if ( $cookie && ( ! is_string( $cookie ) || is_numeric( $cookie ) ) ) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is the auth bypass: is_numeric() on a value that can legitimately be the raw, attacker-controlled $_COOKIE[ LOGGED_IN_COOKIE ] string (see wp_validate_logged_in_cookie() in user.php:598) lets a forged bare-numeric cookie value skip HMAC/token verification entirely and return as an authenticated user ID. Verified end-to-end — see review comment.

return $cookie;
}

$cookie_elements = wp_parse_auth_cookie( $cookie, $scheme );
if ( ! $cookie_elements ) {
/**
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/tests/auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function test_auth_cookie_invalid() {
$cookie = wp_generate_auth_cookie( self::$user_id, time() + 3600, 'auth' );
list($a, $b, $c) = explode( '|', $cookie );
$cookie = $a . '|' . ( $b + 1 ) . '|' . $c;
$this->assertFalse( wp_validate_auth_cookie( self::$user_id, 'auth' ), 'altered cookie' );
$this->assertFalse( wp_validate_auth_cookie( $cookie, 'auth' ), 'altered cookie' );
}

public function test_auth_cookie_scheme() {
Expand Down
34 changes: 34 additions & 0 deletions tests/phpunit/tests/user/wpDetermineCurrentUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* Tests for the determine_current_user filter.
*
* @group user
* @group auth
*
* @covers ::wp_validate_auth_cookie
*/
class Tests_User_WpDetermineCurrentUser extends WP_UnitTestCase {

/**
* Tests that determine_current_user filter callbacks with priority < 10 return their user ID
* without being overridden by wp_validate_auth_cookie.
*
* @ticket 28212
*/
public function test_determine_current_user_early_filter_priority_less_than_10() {
$user_id = self::factory()->user->create();

$callback = function ( $current_user_id ) use ( $user_id ) {
return $user_id;
};

add_filter( 'determine_current_user', $callback, 5 );

$determined_user_id = apply_filters( 'determine_current_user', false );

remove_filter( 'determine_current_user', $callback, 5 );

$this->assertSame( $user_id, $determined_user_id );
}
}
Loading