Naprawiono poważną lukę w UpdraftPlus 1.22.3

Opublikowany: 2022-02-18

Podczas wewnętrznego audytu wtyczki UpdraftPlus wykryliśmy lukę w zabezpieczeniach pobierania kopii zapasowych, która może pozwolić użytkownikom o niskich uprawnieniach, takim jak subskrybenci, na pobranie najnowszych kopii zapasowych witryny.

W przypadku wykorzystania luka ta może zapewnić atakującym dostęp do uprzywilejowanych informacji z bazy danych witryny, której dotyczy luka (np. nazw użytkowników i zaszyfrowanych haseł).

Zgłosiliśmy tę lukę autorom wtyczki, która niedawno wydała wersję 1.22.3, aby ją naprawić. Wymuszone automatyczne aktualizacje również zostały przesunięte ze względu na powagę tego problemu. Jeśli Twoja witryna jeszcze tego nie zrobiła, zdecydowanie zalecamy zaktualizowanie do najnowszej wersji (1.22.3) i posiadanie w witrynie sprawdzonego rozwiązania zabezpieczającego, takiego jak Jetpack Security.

Własne porady UpdraftPlus można znaleźć tutaj.

Detale

Nazwa wtyczki: UpdraftPlus
URI wtyczki: https://wordpress.org/plugins/updraftplus/
Autor: https://updraftplus.com/

Podatność

Pobieranie arbitralnych kopii zapasowych

Wersje, których dotyczy problem: Każda wersja od 1.16.7 do 1.22.3 (wersja bezpłatna) oraz
CVE-ID: CVE-2022-0633
Identyfikator WPVDB: d257c28f-3c7e-422b-a5c2-e618ed3c0bf3
CVSSv3.1: 8,5
CWSS: 87,6

Wtyczka wykorzystuje niestandardowe „nonces” i znaczniki czasu, aby bezpiecznie identyfikować kopie zapasowe. Biorąc pod uwagę, że wiedza o wspomnianym numerze i znaczniku czasu może dać komuś dostęp do wielu funkcji wtyczki, upewnienie się, że te informacje są dostępne tylko dla tych, którzy prawnie ich potrzebują, ma kluczowe znaczenie.

Niestety, jak wykażemy, tak nie było.

Brak wycieku

Pierwszy winowajca znajdował się na UpdraftPlus_Admin::process_status_in_heartbeat metoda.

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

Nie upewnił się właściwie, że użytkownik wysyłający to żądanie pulsu był administratorem (np. za pomocą funkcji takich jak current_user_can), co stanowiło problem, ponieważ pierwszą rzeczą, jaką ta funkcja próbuje zrobić, jest pobranie listy aktywnych zadań kopii zapasowej za pomocą metody get_activejobs_list .

W ten sposób osoba atakująca może stworzyć złośliwe żądanie skierowane na to wywołanie zwrotne pulsu, aby uzyskać dostęp do informacji o najnowszej kopii zapasowej witryny do tej pory, która będzie zawierać między innymi kopię zapasową.

Pobieranie kopii zapasowej

Istnieje kilka sposobów pobierania kopii zapasowych w programie UpdraftPlus, z których większość była odpowiednio zabezpieczona.

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

Niestety, metoda UpdraftPlus_Admin::maybe_download_backup_from_email, która jest podpięta do admin_init, również nie sprawdzała bezpośrednio ról użytkowników.

Chociaż zastosowała pewne sprawdzenia pośrednio, takie jak sprawdzenie zmiennej globalnej $pagenow , wcześniejsze badania wykazały, że ta zmienna może zawierać dowolne dane wejściowe użytkownika. Źli aktorzy mogą używać tego punktu końcowego do pobierania kopii zapasowych plików i baz danych na podstawie informacji, które wyciekli ze wspomnianego błędu pulsu.

Oś czasu

2022-02-14 – Pierwszy kontakt z UpdraftPlus
2022-02-15 – Przesyłamy im szczegóły dotyczące tej luki
2022-02-16 – Wydano UpdraftPlus 1.22.3, uruchomiono wymuszone aktualizacje automatyczne

Wniosek

Zalecamy sprawdzenie, której wersji wtyczki UpdraftPlus używa Twoja witryna, a jeśli znajduje się w zakresie, którego dotyczy problem, jak najszybciej ją zaktualizuj!

W Jetpack ciężko pracujemy, aby zapewnić ochronę Twoich stron internetowych przed tego typu lukami. Zalecamy posiadanie planu bezpieczeństwa dla witryny, który obejmuje skanowanie złośliwych plików i tworzenie kopii zapasowych. Jetpack Security to świetna opcja bezpieczeństwa WordPress, która zapewnia bezpieczeństwo Twojej witrynie i odwiedzającym.

Kredyty

Autor oryginalny: Marc Montpas

Podziękowania dla reszty zespołu Jetpack Scan za opinie, pomoc i poprawki.