WordPress Core 6.2.1 Security Update - Technical Advisory

Published 17 May 2023
Updated 11 December 2023
Rafie Muhammad
Security Researcher at Patchstack
Table of Contents

On the 16th of May 2023, the WordPress Core 6.2.1 version was released with a security update. It recommended users update their sites as soon as possible. This WordPress core 6.2.1 security release addresses 5 different security vulnerabilities that affect multiple WordPress core versions.

For many, WordPress automatically updates the core to the latest version. Check if your WordPress version is 6.2.1 - if not, update immediately!

Cross-Site Request Forgery

See more from the Patchstack database

The underlying vulnerable code located in the wp_ajax_set_attachment_thumbnail function:

function wp_ajax_set_attachment_thumbnail() {
	if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) {
		wp_send_json_error();
	}

	$thumbnail_id = (int) $_POST['thumbnail_id'];
	if ( empty( $thumbnail_id ) ) {
		wp_send_json_error();
	}

	$post_ids = array();
	// For each URL, try to find its corresponding post ID.
	foreach ( $_POST['urls'] as $url ) {
		$post_id = attachment_url_to_postid( $url );
		if ( ! empty( $post_id ) ) {
			$post_ids[] = $post_id;
		}
	}

	if ( empty( $post_ids ) ) {
		wp_send_json_error();
	}

	$success = 0;
	// For each found attachment, set its thumbnail.
	foreach ( $post_ids as $post_id ) {
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			continue;
		}

		if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
			$success++;
		}
	}

	if ( 0 === $success ) {
		wp_send_json_error();
	} else {
		wp_send_json_success();
	}

	wp_send_json_error();
}

The Ajax action already implemented a permission check using current_user_can function, but the function still lacks of nonce check, resulting in a CSRF issue.

Potential impact

This allows unauthenticated users to update an image thumbnail by tricking privileged users into doing an action such as clicking a link.

The patch in WordPress Core 6.2.1

Since the main issue is the lack of a nonce check, implementing a nonce check fixes the issue. The patch can be seen below:

in WordPress Core 6.2.1

Unauthenticated Shortcode Execution

See more from the Patchstack database

The underlying vulnerable code exists in the get_the_block_template_html function:

function get_the_block_template_html() {
	global $_wp_current_template_content;
	global $wp_embed;

	if ( ! $_wp_current_template_content ) {
		if ( is_user_logged_in() ) {
			return '<h1>' . esc_html__( 'No matching template found' ) . '</h1>';
		}
		return;
	}

	$content = $wp_embed->run_shortcode( $_wp_current_template_content );
	$content = $wp_embed->autoembed( $content );
	$content = do_blocks( $content );
	$content = wptexturize( $content );
	$content = convert_smilies( $content );
	$content = shortcode_unautop( $content );
	$content = wp_filter_content_tags( $content, 'template' );
	$content = do_shortcode( $content );
	$content = str_replace( ']]>', ']]&gt;', $content );

	// Wrap block template in .wp-site-blocks to allow for specific descendant styles
	// (e.g. `.wp-site-blocks > *`).
	return '<div class="wp-site-blocks">' . $content . '</div>';
}

The function is used to process user-generated content on the block theme. The function could generate an arbitrary shortcode supplied by an unauthenticated user via comment or other content.

Potential impact

This allows unauthenticated users to arbitrarily generate any shortcode feature available on the target WordPress site, which generally could only be performed by authenticated users such as a Contributor role. This issue in general has a minimum impact but could be chained with another vulnerability in the targeted shortcode to increase the impact.

The patch in WordPress Core 6.2.1

Since the main issue is the code tries to generate arbitrary shortcodes from the supplied content, removing the shortcode generation process fixes the issue. The patch can be seen below:

WordPress Core 6.2.1

Block Attributes Improper Sanitization

See more from the Patchstack database

The underlying vulnerable code located in the filter_block_content function:

function filter_block_content( $text, $allowed_html = 'post', $allowed_protocols = array() ) {
	$result = '';

	$blocks = parse_blocks( $text );
	foreach ( $blocks as $block ) {
		$block   = filter_block_kses( $block, $allowed_html, $allowed_protocols );
		$result .= serialize_block( $block );
	}

	return $result;
}

The function is used to filter block content generated by a user with a Contributor role and higher. The sanitization process lacks proper sanitization on the block comments, resulting in a user being able to embed arbitrary content by utilizing the HTML comment.

Potential impact

This allows authenticated users with a Contributor role and higher to embed arbitrary content and could potentially lead to Cross Site Scripting if chained with another vulnerability.

The patch in WordPress Core 6.2.1

Since the main issue is the lack of a sanitization process, implementing a more robust sanitization check using regex fixes the issue. The patch can be seen below:

wordpress patch

Stored Cross-Site Scripting

See more from the Patchstack database

The underlying vulnerable code located in the receiveEmbedMessage JS function:

window.wp.receiveEmbedMessage = function( e ) {
	var data = e.data;

	if ( ! data ) {
		return;
	}

	if ( ! ( data.secret || data.message || data.value ) ) {
		return;
	}

	if ( /[^a-zA-Z0-9]/.test( data.secret ) ) {
		return;
	}

	var iframes = document.querySelectorAll( 'iframe[data-secret="' + data.secret + '"]' ),
		blockquotes = document.querySelectorAll( 'blockquote[data-secret="' + data.secret + '"]' ),
		i, source, height, sourceURL, targetURL;

	for ( i = 0; i < blockquotes.length; i++ ) {
		blockquotes[ i ].style.display = 'none';
	}

	for ( i = 0; i < iframes.length; i++ ) {
		source = iframes[ i ];

		if ( e.source !== source.contentWindow ) {
			continue;
		}

		source.removeAttribute( 'style' );

		/* Resize the iframe on request. */
		if ( 'height' === data.message ) {
			height = parseInt( data.value, 10 );
			if ( height > 1000 ) {
				height = 1000;
			} else if ( ~~height < 200 ) {
				height = 200;
			}

			source.height = height;
		}

		/* Link to a specific URL on request. */
		if ( 'link' === data.message ) {
			sourceURL = document.createElement( 'a' );
			targetURL = document.createElement( 'a' );

			sourceURL.href = source.getAttribute( 'src' );
			targetURL.href = data.value;

			/* Only continue if link hostname matches iframe's hostname. */
			if ( targetURL.host === sourceURL.host ) {
				if ( document.activeElement === source ) {
					window.top.location.href = data.value;
				}
			}
		}
	}
};

The JS function lacks of protocol validation check when processing Open Embed auto-discovery. The function will act as a receiver and will store the message to the data variable. If the data.message value is set to a link, the code will construct a hyperlink via the <a> element to the targetURL variable. The code then will assign the targetURL.href value to the data.value. Since there is no validation check on the content of data.value, XSS is possible since the value in the end is set as the window.top.location.href value.

Potential impact

This allows authenticated users with a Contributor role and higher to inject a script into the WordPress page via the Open Embed message and could result from stealing sensitive information to potentially privilege escalation on the WordPress site.

The patch in WordPress Core 6.2.1

Since the main issue is a lack of sanitization on the href value, implementing a protocol check fixes the issue. The patch can be seen below:

wordpress patch

Directory Traversal

See more from the Patchstack database

The underlying vulnerable code located in the determine_locale function:

function determine_locale() {
	/**
	 * Filters the locale for the current request prior to the default determination process.
	 *
	 * Using this filter allows to override the default logic, effectively short-circuiting the function.
	 *
	 * @since 5.0.0
	 *
	 * @param string|null $locale The locale to return and short-circuit. Default null.
	 */
	$determined_locale = apply_filters( 'pre_determine_locale', null );

	if ( ! empty( $determined_locale ) && is_string( $determined_locale ) ) {
		return $determined_locale;
	}

	$determined_locale = get_locale();

	if ( is_admin() ) {
		$determined_locale = get_user_locale();
	}

	if ( isset( $_GET['_locale'] ) && 'user' === $_GET['_locale'] && wp_is_json_request() ) {
		$determined_locale = get_user_locale();
	}

	$wp_lang = '';

	if ( ! empty( $_GET['wp_lang'] ) ) {
		$wp_lang = sanitize_text_field( $_GET['wp_lang'] );
	} elseif ( ! empty( $_COOKIE['wp_lang'] ) ) {
		$wp_lang = sanitize_text_field( $_COOKIE['wp_lang'] );
	}

	if ( ! empty( $wp_lang ) && ! empty( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {
		$determined_locale = $wp_lang;
	}

	/**
	 * Filters the locale for the current request.
	 *
	 * @since 5.0.0
	 *
	 * @param string $locale The locale.
	 */
	return apply_filters( 'determine_locale', $determined_locale );
}

The function acts as a handler to determine the current locale desired for the request. The locale value could be constructed by $_GET['wp_lang'] or $_COOKIE['wp_lang']. But the code only uses sanitize_text_field function to sanitize the value. This function is not sufficient to protect against directory traversal attacks, resulting in an unauthenticated user being able to access and load arbitrary locale files.

Potential impact

This allows unauthenticated users to access and load arbitrary locale files. If the user can somehow upload a crafted local file to the WordPress site, this issue could potentially lead to cross-site scripting.

The patch of the WordPress Core 6.2.1

Since the main issue is a lack of proper sanitization checks, implementing a simple regex validation fixes the issue. The patch can be seen below:

in WordPress Core 6.2.1

Thanks to security contributors!

Credit to the researchers and developers who contribute to making WordPress (and a large portion of the web) more secure: Liam Gladdy (WP Engine), John Blackbourn (WordPress security team), Jakub Żoczek (Securitum), Ramuel Gall (Wordfence).

See the official WordPress.org announcement: https://wordpress.org/news/2023/05/wordpress-6-2-1-maintenance-security-release/

The latest in Security Advisories

Looks like your browser is blocking our support chat widget. Turn off adblockers and reload the page.
crossmenu