CVE-2018-19370
Description
A Race condition vulnerability in unzip_file in admin/import/class-import-settings.php in the Yoast SEO (wordpress-seo) plugin before 9.2.0 for WordPress allows an SEO Manager to perform command execution on the Operating System via a ZIP import.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A race condition in Yoast SEO before 9.2.0 allows an SEO Manager to execute arbitrary commands via a crafted ZIP import.
Vulnerability
A race condition exists in the unzip_file function within admin/import/class-import-settings.php in the Yoast SEO plugin for WordPress (versions before 9.2.0). This flaw is triggered during the ZIP import process when an authenticated user with the SEO Manager role imports a specially crafted ZIP file. The race condition allows an attacker to manipulate file extraction in a way that leads to command execution on the underlying operating system [2].
Exploitation
An attacker must have an active WordPress account with the SEO Manager role, which has the capability to perform ZIP imports via the plugin's import settings. The attacker uploads a malicious ZIP file containing a payload. During the extraction process, the race condition window is exploited to replace or modify extracted files before they are processed, ultimately allowing the attacker to execute arbitrary commands on the server [2].
Impact
Successful exploitation results in arbitrary command execution on the operating system with the privileges of the web server user. This can lead to full compromise of the WordPress site, including data theft, site defacement, or further lateral movement within the hosting environment.
Mitigation
The vulnerability is fixed in Yoast SEO version 9.2.0, released on November 28, 2018 [2]. Users should update to this version or later immediately. No workarounds are documented; the only mitigation is to apply the patch. The plugin is actively maintained, and no EOL status applies.
AI Insight generated on May 24, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
13bc687351bc9Merge pull request #11502 from Yoast/jdv/import-settings-fix
9 files changed · +181 −337
admin/class-config.php+0 −34 modified@@ -41,24 +41,10 @@ public function init() { wp_redirect( admin_url( 'admin.php?page=' . WPSEO_Configuration_Page::PAGE_IDENTIFIER ) ); } - add_action( 'admin_init', array( $this, 'admin_init' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'config_page_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'config_page_styles' ) ); } - /** - * Run admin-specific actions. - */ - public function admin_init() { - - $page = filter_input( INPUT_GET, 'page' ); - $tool = filter_input( INPUT_GET, 'tool' ); - $export_nonce = filter_input( INPUT_POST, WPSEO_Export::NONCE_NAME ); - - if ( 'wpseo_tools' === $page && 'import-export' === $tool && $export_nonce !== null ) { - $this->do_yoast_export(); - } - } /** * Loads the required styles for the config page. @@ -158,24 +144,4 @@ private function enqueue_tools_scripts() { $this->asset_manager->enqueue_script( 'bulk-editor' ); } } - - /** - * Runs the yoast exporter class to possibly init the file download. - */ - private function do_yoast_export() { - check_admin_referer( WPSEO_Export::NONCE_ACTION, WPSEO_Export::NONCE_NAME ); - - if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) { - return; - } - - $wpseo_post = filter_input( INPUT_POST, 'wpseo' ); - $include_taxonomy = ! empty( $wpseo_post['include_taxonomy'] ); - $export = new WPSEO_Export( $include_taxonomy ); - - if ( $export->has_error() ) { - add_action( 'admin_notices', array( $export, 'set_error_hook' ) ); - - } - } } /* End of class */
admin/class-export.php+20 −151 modified@@ -11,12 +11,7 @@ * Class with functionality to export the WP SEO settings */ class WPSEO_Export { - - const ZIP_FILENAME = 'yoast-seo-settings-export.zip'; - const INI_FILENAME = 'settings.ini'; - const NONCE_ACTION = 'wpseo_export'; - const NONCE_NAME = 'wpseo_export_nonce'; /** * @var string @@ -28,38 +23,34 @@ class WPSEO_Export { */ private $error = ''; - /** - * @var string - */ - public $export_zip_url = ''; - /** * @var boolean */ public $success; /** - * Whether or not the export will include taxonomy metadata - * - * @var boolean - */ - private $include_taxonomy; - - /** - * @var array + * Handles the export request. */ - private $dir = array(); + public function export() { + check_admin_referer( self::NONCE_ACTION ); + $this->export_settings(); + $this->output(); + } /** - * Class constructor - * - * @param boolean $include_taxonomy Whether to include the taxonomy metadata the plugin creates. + * Outputs the export. */ - public function __construct( $include_taxonomy = false ) { - $this->include_taxonomy = $include_taxonomy; - $this->dir = wp_upload_dir(); + public function output() { + if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) { + esc_html_e( 'You do not have the required rights to export settings.', 'wordpress-seo' ); + return; + } - $this->export_settings(); + echo '<p>'; + /* translators: %1$s expands to Import settings */ + printf( esc_html__( 'Copy all these settings to another site\'s %1$s tab and click "%1$s" there.', 'wordpress-seo' ), __( 'Import settings', 'wordpress-seo' ) ); + echo '</p>'; + echo '<textarea id="wpseo-export" rows="20" cols="100">' . $this->export . '</textarea>'; } /** @@ -88,41 +79,24 @@ public function set_error_hook() { * Exports the current site's WP SEO settings. */ private function export_settings() { - $this->export_header(); foreach ( WPSEO_Options::get_option_names() as $opt_group ) { $this->write_opt_group( $opt_group ); } - - $this->taxonomy_metadata(); - - if ( ! $this->write_settings_file() ) { - $this->error = __( 'Could not write settings to file.', 'wordpress-seo' ); - - return; - } - - if ( $this->zip_file() ) { - // Just exit, because there is a download being served. - exit; - } } /** - * Writes the header of the export file. + * Writes the header of the export. */ private function export_header() { $header = sprintf( /* translators: %1$s expands to Yoast SEO, %2$s expands to Yoast.com */ - esc_html__( 'This is a settings export file for the %1$s plugin by %2$s', 'wordpress-seo' ), + esc_html__( 'These are settings for the %1$s plugin by %2$s', 'wordpress-seo' ), 'Yoast SEO', 'Yoast.com' ); - $this->write_line( '; ' . $header . ' - ' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1yd' ) ) ); - if ( $this->include_taxonomy ) { - $this->write_line( '; ' . __( 'This export includes taxonomy metadata', 'wordpress-seo' ) ); - } + $this->write_line( '; ' . $header ); } /** @@ -178,109 +152,4 @@ private function write_setting( $key, $val ) { } $this->write_line( $key . ' = ' . $val ); } - - /** - * Adds the taxonomy meta data if there is any - */ - private function taxonomy_metadata() { - if ( $this->include_taxonomy ) { - $taxonomy_meta = get_option( 'wpseo_taxonomy_meta' ); - if ( is_array( $taxonomy_meta ) ) { - $this->write_line( '[wpseo_taxonomy_meta]', true ); - $this->write_setting( 'wpseo_taxonomy_meta', urlencode( wp_json_encode( $taxonomy_meta ) ) ); - } - else { - $this->write_line( '; ' . __( 'No taxonomy metadata found', 'wordpress-seo' ), true ); - } - } - } - - /** - * Writes the settings to our temporary settings.ini file - * - * @return boolean unsigned - */ - private function write_settings_file() { - $handle = fopen( $this->dir['path'] . '/' . self::INI_FILENAME, 'w' ); - if ( ! $handle ) { - return false; - } - - $res = fwrite( $handle, $this->export ); - if ( ! $res ) { - return false; - } - - fclose( $handle ); - - return true; - } - - /** - * Zips the settings ini file - * - * @return bool|null - */ - private function zip_file() { - $is_zip_created = $this->create_zip(); - - // The settings.ini isn't needed, because it's in the zipfile. - $this->remove_settings_ini(); - - if ( ! $is_zip_created ) { - $this->error = __( 'Could not zip settings-file.', 'wordpress-seo' ); - - return false; - } - - $this->serve_settings_export(); - $this->remove_zip(); - - return true; - } - - /** - * Creates the zipfile and returns true if it created successful. - * - * @return bool - */ - private function create_zip() { - chdir( $this->dir['path'] ); - $zip = new PclZip( './' . self::ZIP_FILENAME ); - if ( 0 === $zip->create( './' . self::INI_FILENAME ) ) { - return false; - } - - return file_exists( self::ZIP_FILENAME ); - } - - /** - * Downloads the zip file. - */ - private function serve_settings_export() { - // Clean any content that has been already output. For example by other plugins or faulty PHP files. - if ( ob_get_contents() ) { - ob_clean(); - } - header( 'Content-Type: application/octet-stream; charset=utf-8' ); - header( 'Content-Transfer-Encoding: Binary' ); - header( 'Content-Disposition: attachment; filename=' . self::ZIP_FILENAME ); - header( 'Content-Length: ' . filesize( self::ZIP_FILENAME ) ); - - readfile( self::ZIP_FILENAME ); - } - - /** - * Removes the settings ini file. - */ - private function remove_settings_ini() { - unlink( './' . self::INI_FILENAME ); - } - - /** - * Removes the files because they are already downloaded. - */ - private function remove_zip() { - unlink( './' . self::ZIP_FILENAME ); - } }
admin/import/class-import-settings.php+31 −135 modified@@ -11,157 +11,68 @@ * Class with functionality to import the Yoast SEO settings. */ class WPSEO_Import_Settings { + const NONCE_ACTION = 'wpseo-import-settings'; + /** * @var WPSEO_Import_Status */ public $status; - /** - * @var array - */ - private $file; - - /** - * @var string - */ - private $filename; - /** * @var string */ - private $old_wpseo_version = null; - - /** - * @var string - */ - private $path; - - /** - * @var array - */ - private $upload_dir; + private $old_wpseo_version; /** - * Class constructor + * Class constructor. */ public function __construct() { $this->status = new WPSEO_Import_Status( 'import', false ); - if ( ! $this->handle_upload() ) { - return $this->status; - } - - $this->determine_path(); - - if ( ! $this->unzip_file() ) { - $this->clean_up(); - - return $this->status; - } - - $this->parse_options(); - - $this->clean_up(); } /** - * Handle the file upload + * Imports the data submitted by the user. * - * @return boolean Import status. + * @return void */ - private function handle_upload() { - $overrides = array( 'mimes' => array( 'zip' => 'application/zip' ) ); // Explicitly allow zip in multisite. - $this->file = wp_handle_upload( $_FILES['settings_import_file'], $overrides ); - - if ( is_wp_error( $this->file ) ) { - $this->status->set_msg( __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . $this->file->get_error_message() ); + public function import() { + check_admin_referer( self::NONCE_ACTION ); - return false; - } - - if ( is_array( $this->file ) && isset( $this->file['error'] ) ) { - $this->status->set_msg( __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . $this->file['error'] ); - - return false; + if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) { + return; } - if ( ! isset( $this->file['file'] ) ) { - $this->status->set_msg( __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'Upload failed.', 'wordpress-seo' ) ); - - return false; + $content = filter_input( INPUT_POST, 'settings_import' ); + if ( empty( $content ) ) { + return; } - return true; + $this->parse_options( $content ); } /** - * Determine the path to the import file - */ - private function determine_path() { - $this->upload_dir = wp_upload_dir(); - - if ( ! defined( 'DIRECTORY_SEPARATOR' ) ) { - define( 'DIRECTORY_SEPARATOR', '/' ); - } - $this->path = $this->upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'wpseo-import' . DIRECTORY_SEPARATOR; - - if ( ! isset( $GLOBALS['wp_filesystem'] ) || ! is_object( $GLOBALS['wp_filesystem'] ) ) { - $url = wp_nonce_url( - self_admin_url( 'admin.php?page=wpseo_tools&tool=import-export' ), - 'wpseo-import' - ); - $credentials = request_filesystem_credentials( esc_url_raw( $url ) ); - WP_Filesystem( $credentials ); - } - } - - /** - * Unzip the file + * Parse the options. * - * @return boolean + * @param string $raw_options The content to parse. + * + * @return void */ - private function unzip_file() { - $unzipped = unzip_file( $this->file['file'], $this->path ); - $msg_base = __( 'Settings could not be imported:', 'wordpress-seo' ) . ' '; - - if ( is_wp_error( $unzipped ) ) { - /* translators: %s expands to an error message. */ - $this->status->set_msg( $msg_base . sprintf( __( 'Unzipping failed with error "%s".', 'wordpress-seo' ), $unzipped->get_error_message() ) ); - - return false; - } - - $this->filename = $this->path . 'settings.ini'; - if ( ! is_file( $this->filename ) || ! is_readable( $this->filename ) ) { - $this->status->set_msg( $msg_base . __( 'Unzipping failed - file settings.ini not found.', 'wordpress-seo' ) ); - - return false; + protected function parse_options( $raw_options ) { + // If we're not on > PHP 5.3, return, as we'll otherwise error out. + if ( ! defined( 'WPSEO_NAMESPACES' ) || ! WPSEO_NAMESPACES ) { + return; } - return true; - } - - /** - * Parse the option file - */ - private function parse_options() { - if ( defined( 'INI_SCANNER_RAW' ) ) { - /* - * Implemented INI_SCANNER_RAW to make sure variables aren't parsed. - * - * http://php.net/manual/en/function.parse-ini-file.php#99943 - */ - $options = parse_ini_file( $this->filename, true, INI_SCANNER_RAW ); - } - else { - // PHP 5.2 does not implement the 3rd argument, this is a fallback. - $options = parse_ini_file( $this->filename, true ); - } + // @codingStandardsIgnoreLine + $options = parse_ini_string( $raw_options, true, INI_SCANNER_RAW ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.parse_ini_stringFound -- We won't get to this function if PHP < 5.3 due to the WPSEO_NAMESPACES check above. if ( is_array( $options ) && $options !== array() ) { $this->import_options( $options ); + return; } - $this->status->set_msg( __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'No settings found in file.', 'wordpress-seo' ) ); + + $this->status->set_msg( __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'No settings found.', 'wordpress-seo' ) ); } /** @@ -171,43 +82,28 @@ private function parse_options() { * @param array $option_group Option group data. * @param array $options Options data. */ - private function parse_option_group( $name, $option_group, $options ) { + protected function parse_option_group( $name, $option_group, $options ) { // Make sure that the imported options are cleaned/converted on import. $option_instance = WPSEO_Options::get_option_instance( $name ); if ( is_object( $option_instance ) && method_exists( $option_instance, 'import' ) ) { $option_instance->import( $option_group, $this->old_wpseo_version, $options ); } } - /** - * Remove the files - */ - private function clean_up() { - if ( file_exists( $this->filename ) && is_writable( $this->filename ) ) { - unlink( $this->filename ); - } - if ( ! empty( $this->file['file'] ) && file_exists( $this->file['file'] ) && is_writable( $this->file['file'] ) ) { - unlink( $this->file['file'] ); - } - if ( file_exists( $this->path ) && is_writable( $this->path ) ) { - $wp_file = new WP_Filesystem_Direct( $this->path ); - $wp_file->rmdir( $this->path, true ); - } - } - /** * Imports the options if found. * - * @param array $options The options parsed from the ini file. + * @param array $options The options parsed from the provided settings. */ - private function import_options( $options ) { + protected function import_options( $options ) { if ( isset( $options['wpseo']['version'] ) && $options['wpseo']['version'] !== '' ) { $this->old_wpseo_version = $options['wpseo']['version']; } foreach ( $options as $name => $option_group ) { $this->parse_option_group( $name, $option_group, $options ); } + $this->status->set_msg( __( 'Settings successfully imported.', 'wordpress-seo' ) ); $this->status->set_status( true ); }
admin/views/tabs/tool/wpseo-export.php+9 −2 modified@@ -18,9 +18,15 @@ /* translators: %1$s expands to Yoast SEO */ $submit_button_value = sprintf( __( 'Export your %1$s settings', 'wordpress-seo' ), 'Yoast SEO' ); +if ( filter_input( INPUT_POST, 'do_export' ) ) { + $export = new WPSEO_Export(); + $export->export(); + return; +} + $wpseo_export_phrase = sprintf( /* translators: %1$s expands to Yoast SEO */ - __( 'Export your %1$s settings here, to import them again later or to import them on another site.', 'wordpress-seo' ), + __( 'Export your %1$s settings here, to copy them on another site.', 'wordpress-seo' ), 'Yoast SEO' ); ?> @@ -30,6 +36,7 @@ action="<?php echo esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export#top#wpseo-export' ) ); ?>" method="post" accept-charset="<?php echo esc_attr( get_bloginfo( 'charset' ) ); ?>"> - <?php wp_nonce_field( WPSEO_Export::NONCE_ACTION, WPSEO_Export::NONCE_NAME ); ?> + <?php wp_nonce_field( WPSEO_Export::NONCE_ACTION ); ?> + <input type="hidden" name="do_export" value="1" /> <button type="submit" class="button button-primary" id="export-button"><?php echo esc_html( $submit_button_value ); ?></button> </form>
admin/views/tabs/tool/wpseo-import.php+11 −11 modified@@ -15,27 +15,27 @@ exit(); } +if ( ! defined( 'WPSEO_NAMESPACES' ) || ! WPSEO_NAMESPACES ) { + esc_html_e( 'Import of settings is only supported on servers that run PHP 5.3 or higher.', 'wordpress-seo' ); + return; +} ?> <p> <?php printf( - /* translators: 1: emphasis opener; 2: emphasis closer. */ - esc_html__( 'Import settings by locating %1$ssettings.zip%2$s and clicking "Import settings"', 'wordpress-seo' ), - '<em>', - '</em>' + /* translators: 1: Import settings button string from below. */ + esc_html__( 'Import settings by pasting the settings you copied from another site here and clicking "%s".', 'wordpress-seo' ), + __( 'Import settings', 'wordpress-seo' ) ); ?> </p> <form action="<?php echo esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export#top#wpseo-import' ) ); ?>" - method="post" enctype="multipart/form-data" + method="post" accept-charset="<?php echo esc_attr( get_bloginfo( 'charset' ) ); ?>"> - <?php wp_nonce_field( 'wpseo-import-file', '_wpnonce', true, true ); ?> - <label class="screen-reader-text" for="settings-import-file"><?php esc_html_e( 'Choose your settings.zip file', 'wordpress-seo' ); ?></label> - <input type="file" name="settings_import_file" id="settings-import-file" - accept="application/x-zip,application/x-zip-compressed,application/zip"/> - <input type="hidden" name="action" value="wp_handle_upload"/><br/> - <br/> + <?php wp_nonce_field( WPSEO_Import_Settings::NONCE_ACTION ); ?> + <label class="screen-reader-text" for="settings-import"><?php esc_html_e( 'Paste your settings from another Yoast SEO installation.', 'wordpress-seo' ); ?></label> + <textarea id="settings-import" rows="10" cols="140" name="settings_import"></textarea><br/> <input type="submit" class="button button-primary" value="<?php esc_attr_e( 'Import settings', 'wordpress-seo' ); ?>"/> </form>
admin/views/tool-import-export.php+2 −3 modified@@ -42,10 +42,9 @@ $import = new WPSEO_Import_Plugin( new $class(), 'cleanup' ); } } -elseif ( isset( $_FILES['settings_import_file'] ) ) { - check_admin_referer( 'wpseo-import-file' ); - +elseif ( filter_input( INPUT_POST, 'settings_import' ) ) { $import = new WPSEO_Import_Settings(); + $import->import(); } /**
tests/admin/import/test-class-import-settings.php+89 −0 added@@ -0,0 +1,89 @@ +<?php +/** + * WPSEO plugin test file. + * + * @package WPSEO\Tests\Admin\Import\Plugins + */ + +/** + * Test importing meta data from SEOPressor. + * + * @group imports + */ +class WPSEO_Import_Settings_Test extends WPSEO_UnitTestCase { + /** + * Holds the class instance. + * + * @var WPSEO_Import_Settings_Double + */ + private $class_instance; + + /** + * Sets up the test class. + */ + public function setUp() { + parent::setUp(); + + $this->class_instance = new WPSEO_Import_Settings_Double(); + } + + /** + * Tests the plugin name function. + * + * @covers WPSEO_Import_Settings::parse_options + */ + public function test_parse_options_empty() { + if ( version_compare( PHP_VERSION, '5.3', '<' ) ) { + $this->markTestSkipped( 'Not possible in PHP 5.2' ); + } + + $this->class_instance->parse_options( '' ); + + $this->assertEquals( false, $this->class_instance->status->status ); + } + + /** + * Tests the import functionality + * + * @covers WPSEO_Import_Settings::parse_options + */ + public function test_parse_options() { + if ( version_compare( PHP_VERSION, '5.3', '<' ) ) { + $this->markTestSkipped( 'Not possible in PHP 5.2' ); + } + + $this->assertEquals( true, WPSEO_Options::get( 'enable_admin_bar_menu' ) ); + + $settings = <<<EO_DATA +; These are settings for the Yoast SEO plugin by Yoast.com + +[wpseo] +enable_admin_bar_menu = 0 + +EO_DATA; + + $this->class_instance->parse_options( $settings ); + + $this->assertEquals( true, $this->class_instance->status->status ); + $this->assertEquals( false, WPSEO_Options::get( 'enable_admin_bar_menu' ) ); + } + + /** + * Tests the import functionality + * + * @covers WPSEO_Import_Settings::parse_options + */ + public function test_parse_options_invalid() { + if ( version_compare( PHP_VERSION, '5.3', '<' ) ) { + $this->markTestSkipped( 'Not possible in PHP 5.2' ); + } + + $settings = <<<EO_DATA +Not a valid INI file format... +EO_DATA; + + $this->class_instance->parse_options( $settings ); + + $this->assertEquals( false, $this->class_instance->status->status ); + } +}
tests/doubles/class-import-settings-double.php+19 −0 added@@ -0,0 +1,19 @@ +<?php +/** + * WPSEO plugin test double file. + * + * @package WPSEO\Tests\Doubles + */ + +class WPSEO_Import_Settings_Double extends WPSEO_Import_Settings { + /** + * Parse the options. + * + * @param string $raw_options The content to parse. + * + * @return void + */ + public function parse_options( $raw_options ) { + parent::parse_options( $raw_options ); + } +}
wp-seo-main.php+0 −1 modified@@ -60,7 +60,6 @@ function wpseo_auto_load( $class ) { $classes = array( 'wp_list_table' => ABSPATH . 'wp-admin/includes/class-wp-list-table.php', 'walker_category' => ABSPATH . 'wp-includes/category-template.php', - 'pclzip' => ABSPATH . 'wp-admin/includes/class-pclzip.php', ); }
Vulnerability mechanics
Root cause
"Race condition in ZIP file extraction allows arbitrary file writes via a malicious archive."
Attack vector
An attacker with the SEO Manager role (who has the `wpseo_manage_options` capability) could craft a malicious ZIP file containing a symbolic link or a file that overwrites a PHP file in the WordPress installation. The import process in `WPSEO_Import_Settings::unzip_file()` called `unzip_file()` on the uploaded ZIP and then parsed the extracted `settings.ini` with `parse_ini_file()` without validating that the extracted files were safe [patch_id=2360078]. Because the ZIP extraction happened in a race window before the settings file was parsed, a carefully timed ZIP bomb or symlink inside the archive could lead to arbitrary file writes, enabling command execution on the operating system [ref_id=1].
Affected code
The vulnerability resides in `admin/import/class-import-settings.php` in the `WPSEO_Import_Settings` class, specifically the `unzip_file()` method (removed in the patch). The import flow accepted a ZIP file upload via `$_FILES['settings_import_file']`, extracted it with `unzip_file()`, and then parsed the extracted `settings.ini` with `parse_ini_file()` [patch_id=2360078]. The export side in `admin/class-export.php` wrote settings to a temporary file and zipped it using `PclZip` [patch_id=2360078].
What the fix does
The patch completely removes the ZIP-based import/export mechanism. The `WPSEO_Import_Settings` class no longer handles file uploads, unzipping, or filesystem operations; instead, the new `import()` method reads settings directly from a textarea POST field (`settings_import`) and parses the raw INI string with `parse_ini_string()` [patch_id=2360078]. The `WPSEO_Export` class no longer writes a temporary file, creates a ZIP, or serves a download; it simply outputs the serialized settings as plain text in a textarea [patch_id=2360078]. The `PclZip` autoloader entry in `wp-seo-main.php` is also removed, eliminating the ZIP library dependency entirely [patch_id=2360078].
Preconditions
- authAttacker must have the SEO Manager role (wpseo_manage_options capability) in WordPress.
- configThe vulnerable plugin version must be before 9.2.0.
- inputAttacker must be able to upload a ZIP file via the settings import form.
Generated on May 24, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3- github.com/Yoast/wordpress-seo/pull/11502/commits/3bfa70a143f5ea3ee1934f3a1703bb5caf139ffamitrex_refsource_MISC
- wordpress.org/plugins/wordpress-seo/mitrex_refsource_MISC
- www.youtube.com/watchmitrex_refsource_MISC
News mentions
0No linked articles in our index yet.