Vulnerabilidad grave solucionada en UpdraftPlus 1.22.3

Publicado: 2022-02-18

Durante una auditoría interna del complemento UpdraftPlus, descubrimos una vulnerabilidad de descarga de copia de seguridad arbitraria que podría permitir a los usuarios con pocos privilegios, como los suscriptores, descargar las últimas copias de seguridad de un sitio.

Si se explota, la vulnerabilidad podría otorgar a los atacantes acceso a información privilegiada de la base de datos del sitio afectado (por ejemplo, nombres de usuario y contraseñas cifradas).

Informamos la vulnerabilidad a los autores del complemento, y recientemente lanzaron la versión 1.22.3 para solucionarlo. Las actualizaciones automáticas forzadas también se han impulsado debido a la gravedad de este problema. Si su sitio aún no lo ha hecho, le recomendamos enfáticamente que actualice a la última versión (1.22.3) y tenga una solución de seguridad establecida en su sitio, como Jetpack Security.

Puede encontrar el propio aviso de UpdraftPlus aquí.

Detalles

Nombre del complemento: UpdraftPlus
URI del complemento: https://wordpress.org/plugins/updraftplus/
Autor: https://updraftplus.com/

la vulnerabilidad

Descargas de copias de seguridad arbitrarias

Versiones afectadas: todas las versiones entre 1.16.7 y 1.22.3 (versión gratuita), y
CVE-ID: CVE-2022-0633
ID de WPVDB: d257c28f-3c7e-422b-a5c2-e618ed3c0bf3
CVSSv3.1: 8.5
CWS: 87,6

El complemento utiliza "nonces" personalizados y marcas de tiempo para identificar de forma segura las copias de seguridad. Dado que el conocimiento de dicho nonce y la marca de tiempo puede dar a alguien acceso a algunas de las funciones del complemento, asegurarse de que esta información solo sea accesible para aquellos que la necesitan legítimamente es crucial.

Desafortunadamente, como demostraremos, no fue el caso.

Fuga de nonce

El primer culpable se encuentra en UpdraftPlus_Admin::process_status_in_heartbeat método.

	/**
	 * Receive Heartbeat data and respond.
	 *
	 * Processes data received via a Heartbeat request, and returns additional data to pass back to the front end.
	 *
	 * @param array $response - Heartbeat response data to pass back to front end.
	 * @param array $data     - Data received from the front end (unslashed).
	 */
	public function process_status_in_heartbeat($response, $data) {
		if (!is_array($response) || empty($data['updraftplus'])) return $response;
		try {
			$response['updraftplus'] = $this->get_activejobs_list(UpdraftPlus_Manipulation_Functions::wp_unslash($data['updraftplus']));
		} catch (Exception $e) {
			$log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during get active job list. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
			error_log($log_message);
			$response['updraftplus'] = array(
				'fatal_error' => true,
				'fatal_error_message' => $log_message
			);
		// @codingStandardsIgnoreLine
		} catch (Error $e) {
			$log_message = 'PHP Fatal error ('.get_class($e).') has occurred during get active job list. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
			error_log($log_message);
			$response['updraftplus'] = array(
				'fatal_error' => true,
				'fatal_error_message' => $log_message
			);
		}

		if (UpdraftPlus_Options::user_can_manage() && isset($data['updraftplus']['updraft_credentialtest_nonce'])) {
			if (!wp_verify_nonce($data['updraftplus']['updraft_credentialtest_nonce'], 'updraftplus-credentialtest-nonce')) {
				$response['updraftplus']['updraft_credentialtest_nonce'] = wp_create_nonce('updraftplus-credentialtest-nonce');
			}
		}

		$response['updraftplus']['time_now'] = get_date_from_gmt(gmdate('Y-m-d H:i:s'), 'D, F j, Y H:i');

		return $response;
	}

No se aseguró correctamente de que el usuario que enviaba esta solicitud de latido fuera un administrador (por ejemplo, a través de funciones como current_user_can), lo cual era un problema ya que lo primero que intenta hacer esta función es obtener la lista de trabajos de copia de seguridad activos a través del método get_activejobs_list .

Por lo tanto, un atacante podría crear una solicitud maliciosa dirigida a esta devolución de llamada de latido para obtener acceso a información sobre la última copia de seguridad del sitio hasta la fecha, que, entre otras cosas, contendrá el nonce de una copia de seguridad.

Descarga de copia de seguridad

Hay algunas formas de descargar copias de seguridad en UpdraftPlus, la mayoría de las cuales estaban debidamente protegidas.

	/**
	 * Find out if the current request is a backup download request, and proceed with the download if it is
	 */
	public function maybe_download_backup_from_email() {
		global $pagenow;
		if ((!defined('DOING_AJAX') || !DOING_AJAX) && UpdraftPlus_Options::admin_page() === $pagenow && isset($_REQUEST['page']) && 'updraftplus' === $_REQUEST['page'] && isset($_REQUEST['action']) && 'updraft_download_backup' === $_REQUEST['action']) {
			$findexes = empty($_REQUEST['findex']) ? array(0) : $_REQUEST['findex'];
			$timestamp = empty($_REQUEST['timestamp']) ? '' : $_REQUEST['timestamp'];
			$nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce'];
			$type = empty($_REQUEST['type']) ? '' : $_REQUEST['type'];
			if (empty($timestamp) || empty($nonce) || empty($type)) wp_die(__('The download link is broken, you may have clicked the link from untrusted source', 'updraftplus'), '', array('back_link' => true));
			$backup_history = UpdraftPlus_Backup_History::get_history();
			if (!isset($backup_history[$timestamp]['nonce']) || $backup_history[$timestamp]['nonce'] !== $nonce) wp_die(__("The download link is broken or the backup file is no longer available", 'updraftplus'), '', array('back_link' => true));
			$this->do_updraft_download_backup($findexes, $type, $timestamp, 2, false, '');
			exit; // we don't need anything else but an exit
		}
	}
}

Desafortunadamente, el método UpdraftPlus_Admin::maybe_download_backup_from_email, que está vinculado a admin_init, tampoco validó directamente los roles de los usuarios.

Si bien aplicó algunas comprobaciones indirectamente, como comprobar la variable global $pagenow , investigaciones anteriores han demostrado que esta variable puede contener entradas de usuario arbitrarias. Los malos actores podrían usar este punto final para descargar copias de seguridad de archivos y bases de datos en función de la información que filtraron del error de latido mencionado anteriormente.

Cronología

2022-02-14 – Contacto inicial con UpdraftPlus
2022-02-15 – Les enviamos detalles sobre esta vulnerabilidad
2022-02-16: se lanza UpdraftPlus 1.22.3, se lanzan las actualizaciones automáticas forzadas

Conclusión

Le recomendamos que verifique qué versión del complemento UpdraftPlus está usando su sitio y, si está dentro del rango afectado, ¡actualícelo lo antes posible!

En Jetpack, trabajamos arduamente para asegurarnos de que sus sitios web estén protegidos contra este tipo de vulnerabilidades. Le recomendamos que tenga un plan de seguridad para su sitio que incluya análisis y copias de seguridad de archivos maliciosos. Jetpack Security es una excelente opción de seguridad de WordPress para garantizar que su sitio y los visitantes estén seguros.

Créditos

Investigador original: Marc Montpas

Gracias al resto del equipo de Jetpack Scan por sus comentarios, ayuda y correcciones.