WordPress Core 6.5.5 Security Update - Technical Advisory

Published 1 July 2024
Rafie Muhammad
Security Researcher at Patchstack
Table of Contents

WordPress Core

Contributor+ Path Traversal (Windows Only)

WordPress Core

Contributor+ Stored Cross-Site Scripting via template-part

WordPress Core

Contributor+ Stored Cross-Site Scripting via HTML API

On the 24th of June 2024, WordPress.org released a security update and recommended users update their sites as soon as possible. This WordPress core 6.5.5 security release addresses 3 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.5.5 or any of the other patched versions - if not, update immediately!

Contributor+ Path Traversal (Windows Only)

Credits to our own security research team at Patchstack for discovering this vulnerability. Additionally to other researchers apple502j, David Fifield, x89, and mishre.

The underlying vulnerable code is located in the validate_file function:

function validate_file( $file, $allowed_files = array() ) {
	if ( ! is_scalar( $file ) || '' === $file ) {
		return 0;
	}

	// `../` on its own is not allowed:
	if ( '../' === $file ) {
		return 1;
	}

	// More than one occurrence of `../` is not allowed:
	if ( preg_match_all( '#\.\./#', $file, $matches, PREG_SET_ORDER ) && ( count( $matches ) > 1 ) ) {
		return 1;
	}

	// `../` which does not occur at the end of the path is not allowed:
	if ( str_contains( $file, '../' ) && '../' !== mb_substr( $file, -3, 3 ) ) {
		return 1;
	}

	// Files not in the allowed file list are not allowed:
	if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files, true ) ) {
		return 3;
	}

	// Absolute Windows drive paths are not allowed:
	if ( ':' === substr( $file, 1, 1 ) ) {
		return 2;
	}

	return 0;
}

The function itself acts as a function to validate a file path to prevent path traversal. This function is used in several areas of WordPress Core code, one of the cases is it is called from the render_block_core_template_part function which handles the wp:template-part block. Due to an incomplete check on the validate_file function, users are able to supply a Windows-based path traversal and read or include arbitrary .html files which could potentially lead to an XSS.

Potential impact

This allows authenticated users with a Contributor role and higher to load or include arbitrary .html files which could potentially lead to an XSS.

The patch

Since the main issue is the lack of validation process on function, the Core team decided to use the wp_normalize_path function to normalize the path and prevent Windows-based path traversal. The patch can be seen below:

Contributor+ Stored Cross-Site Scripting via template-part

Credits to our security research team at Patchstack for discovering this vulnerability.

The main vulnerable feature exists on the render_block_core_template_part function:

function render_block_core_template_part( $attributes ) {
	static $seen_ids = array();

	$template_part_id = null;
	$content          = null;
	$area             = WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
	$theme            = isset( $attributes['theme'] ) ? $attributes['theme'] : get_stylesheet();

------------- CUT HERE -------------

	if ( empty( $attributes['tagName'] ) ) {
		$area_tag = 'div';
		if ( $area_definition && isset( $area_definition['area_tag'] ) ) {
			$area_tag = $area_definition['area_tag'];
		}
		$html_tag = $area_tag;
	} else {
		$html_tag = esc_attr( $attributes['tagName'] );
	}
	$wrapper_attributes = get_block_wrapper_attributes();

	return "<$html_tag $wrapper_attributes>" . str_replace( ']]>', ']]&gt;', $content ) . "</$html_tag>";
}

In short, this function will handle the wp:template-part block process. Notice that the $attributes['tagName'] value will be constructed into $html_tag variable with an esc_attr function. This escaping process is not proper and can still cause an XSS since the $html_tag variable will be constructed directly as an HTML opening and closing tag value.

Potential impact

This allows users with the minimum role of Contributor to inject arbitrary JavaScript codes to the drafted posts and potentially could take over the sites with certain JS payload usage.

The patch

The Core team decided to add a new function called filter_block_core_template_part_attributes to check if the supplied $attributes['tagName'] value is in the allowed list of wp_kses_allowed_html. The patch can be seen below:

Contributor+ Stored Cross-Site Scripting via HTML API

Credits to Alex Concha (WP Security Team), Dennis Snell (WP Core Team), Grzegorz Ziółkowski (WP Security Team), and Aaron Jorbin for discovering this vulnerability.

The main vulnerable feature exists on the set_attribute function:

public function set_attribute( $name, $value ) {
------------- CUT HERE -------------

if ( false === $value ) {
		return $this->remove_attribute( $name );
	}

	if ( true === $value ) {
		$updated_attribute = $name;
	} else {
		$comparable_name = strtolower( $name );

		/*
			* Escape URL attributes.
			*
			* @see https://html.spec.whatwg.org/#attributes-3
			*/
		$escaped_new_value = esc_attr( $value );
		$updated_attribute = wp_kses_one_attr( "{$comparable_name}=\"{$escaped_new_value}\"", $tag_name );
	}

------------- CUT HERE -------------

	if ( isset( $this->attributes[ $comparable_name ] ) ) {
		/*
			* Update an existing attribute.
			*
			* Example – set attribute id to "new" in <div id="initial_id" />:
			*
			*     <div id="initial_id"/>
			*          ^-------------^
			*          start         end
			*     replacement: `id="new"`
			*
			*     Result: <div id="new"/>
			*/
		$existing_attribute                        = $this->attributes[ $comparable_name ];
		$this->lexical_updates[ $comparable_name ] = new WP_HTML_Text_Replacement(
			$existing_attribute->start,
			$existing_attribute->length,
			$updated_attribute
		);
	} else {
		/*
			* Create a new attribute at the tag's name end.
			*
			* Example – add attribute id="new" to <div />:
			*
			*     <div/>
			*         ^
			*         start and end
			*     replacement: ` id="new"`
			*
			*     Result: <div id="new"/>
			*/
		$this->lexical_updates[ $comparable_name ] = new WP_HTML_Text_Replacement(
			$this->tag_name_starts_at + $this->tag_name_length,
			0,
			' ' . $updated_attribute
		);
	}

	/*
		* Any calls to update the `class` attribute directly should wipe out any
		* enqueued class changes from `add_class` and `remove_class`.
		*/
	if ( 'class' === $comparable_name && ! empty( $this->classname_updates ) ) {
		$this->classname_updates = array();
	}

	return true;
}

In short, this function will construct the $updated_attribute variable with URL value which is could potentially lead to an XSS.

Potential impact

This allows users with the minimum role of Contributor to inject arbitrary JavaScript codes to the drafted posts and potentially could take over the sites with certain JS payload usage.

The patch

The Core team decided to apply a more strict escaping or sanitization to the vulnerable code. The patch can be seen below:

Conclusion

In this article, we covered 3 medium severity vulnerabilities that were patched in WordPress Core 6.5.5. Our research team also contributed to 2 out of 3 reported vulnerabilities.

See the official WordPress.org announcement: https://wordpress.org/news/2024/06/wordpress-6-5-5/

Help us make the Internet a safer place

Making the WordPress ecosystem more secure is a team effort, and we believe that plugin developers and security researchers should work together.

  • If you're a plugin developer, join our mVDP program that makes it easier to report, manage, and address vulnerabilities in your software.
  • If you're a security researcher, join Patchstack Alliance to report vulnerabilities & earn rewards.

The latest in Security Advisories

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