В UpdraftPlus 1.22.3 устранена серьезная уязвимость

Опубликовано: 2022-02-18

Во время внутреннего аудита плагина UpdraftPlus мы обнаружили уязвимость произвольной загрузки резервных копий, которая может позволить пользователям с низким уровнем привилегий, таким как подписчики, загружать последние резервные копии сайта.

В случае использования уязвимости злоумышленники могут получить доступ к привилегированной информации из базы данных уязвимого сайта (например, к именам пользователей и хешированным паролям).

Мы сообщили об уязвимости авторам плагина, и недавно они выпустили версию 1.22.3 для ее устранения. Принудительные автоматические обновления также были запущены из-за серьезности этой проблемы. Если на вашем сайте это еще не сделано, мы настоятельно рекомендуем вам обновиться до последней версии (1.22.3) и иметь установленное решение для обеспечения безопасности на вашем сайте, например Jetpack Security.

Вы можете найти собственный совет UpdraftPlus здесь.

Подробности

Название плагина: Updraft Plus
URI плагина: https://wordpress.org/plugins/updraftplus/
Автор: https://updraftplus.com/

Уязвимость

Произвольные загрузки резервных копий

Затрагиваемые версии: все версии от 1.16.7 до 1.22.3 (бесплатная версия) и
CVE-ID: CVE-2022-0633
Идентификатор WPVDB: d257c28f-3c7e-422b-a5c2-e618ed3c0bf3
CVSSv3.1: 8.5
КВСС: 87,6

Плагин использует настраиваемые «одноразовые номера» и метки времени для надежной идентификации резервных копий. Учитывая, что знание указанного одноразового номера и временной метки может дать кому-то доступ к довольно многим функциям плагина, крайне важно убедиться, что эта информация доступна только тем, кто на законных основаниях в ней нуждается.

К сожалению, как мы покажем, это было не так.

Утечка одноразового номера

Первый виновник был обнаружен в UpdraftPlus_Admin::process_status_in_heartbeat. метод.

	/**
	 * 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;
	}

Она не гарантировала должным образом, что пользователь, отправляющий этот тактовый запрос, был администратором (например, с помощью таких функций, как current_user_can), что было проблемой, поскольку первое, что пытается сделать эта функция, — это получить список активных заданий резервного копирования с помощью метода get_activejobs_list .

Таким образом, злоумышленник может создать вредоносный запрос, нацеленный на этот обратный вызов пульса, чтобы получить доступ к информации о последней резервной копии сайта на сегодняшний день, которая, среди прочего, будет содержать одноразовый номер резервной копии.

Резервная загрузка

Есть несколько способов загрузки резервных копий на UpdraftPlus, большинство из которых были должным образом защищены.

	/**
	 * 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
		}
	}
}

К сожалению, метод UpdraftPlus_Admin::maybe_download_backup_from_email, который подключен к admin_init, также не проверял роли пользователей напрямую.

Хотя некоторые проверки применялись косвенно, например проверка глобальной переменной $pagenow , прошлые исследования показали, что эта переменная может содержать произвольный пользовательский ввод. Злоумышленники могут использовать эту конечную точку для загрузки резервных копий файлов и баз данных на основе информации, которую они просочились из-за вышеупомянутой ошибки пульса.

График

14.02.2022 — Первый контакт с UpdraftPlus
15.02.2022 — Мы отправляем им подробную информацию об этой уязвимости.
16.02.2022 — Выпущен UpdraftPlus 1.22.3, запущены принудительные автообновления

Вывод

Мы рекомендуем вам проверить, какая версия плагина UpdraftPlus используется на вашем сайте, и, если она находится в пределах затронутого диапазона, обновить ее как можно скорее!

В Jetpack мы прилагаем все усилия, чтобы ваши веб-сайты были защищены от уязвимостей такого типа. Мы рекомендуем вам иметь план безопасности для вашего сайта, который включает сканирование вредоносных файлов и резервное копирование. Jetpack Security — это отличный вариант безопасности WordPress для обеспечения безопасности вашего сайта и посетителей.

Кредиты

Оригинальный исследователь: Марк Монпас

Спасибо остальной команде Jetpack Scan за отзывы, помощь и исправления.