Krytyczna luka w WPvivid Backup: nieautoryzowany upload plików i potencjalne RCE (CVE-2026-1357)
W ekosystemie WordPressa co jakiś czas wraca ten sam, wyjątkowo niebezpieczny scenariusz: arbitrary file upload, czyli możliwość wgrania na serwer pliku wybranego przez atakującego. W praktyce często kończy się to zdalnym wykonaniem kodu (RCE, Remote Code Execution) i przejęciem całej witryny. Dokładnie taki problem opisano dla wtyczki WPvivid Backup & Migration (slug: wpvivid-backuprestore) – i to w kontekście funkcji, która ma ułatwiać transfer kopii zapasowych między serwisami.
Co się stało i kogo dotyczy problem
Do programu bug bounty Wordfence 12 stycznia 2026 zgłoszono podatność typu Unauthenticated Arbitrary File Upload we wtyczce WPvivid Backup. Wtyczka ma ponad 800 000 aktywnych instalacji, więc temat jest istotny operacyjnie dla wielu adminów i osób utrzymujących WordPressy.
Najważniejszy niuans: wg opisu Wordfence podatność krytycznie dotyka przede wszystkim te instalacje, na których w ustawieniach wtyczki wygenerowano klucz umożliwiający innemu serwisowi wysłanie backupu na tę stronę. Ta funkcja jest domyślnie wyłączona, a czas ważności klucza da się ustawić maksymalnie na 24 godziny.
Identyfikatory i zakres podatności (Wordfence Intelligence)
- Nazwa w bazie: Migration, Backup, Staging <= 0.9.123 - Unauthenticated Arbitrary File Upload
- CVSS: 9.8 (Critical)
- CVE: CVE-2026-1357
- Wersje podatne: <= 0.9.123
- Wersja z poprawką: 0.9.124
- Bounty: 2,145.00 USD
- Wtyczka: Migration, Backup, Staging – WPvivid Backup & Migration
- Slug wtyczki: wpvivid-backuprestore
Dlaczego to jest groźne: od uploadu do przejęcia strony
Podatności z tej klasy zwykle nie kończą się na samym „wgraniu pliku”. Jeśli atakujący może umieścić na serwerze plik .php w publicznie dostępnym katalogu, to często wystarczy jedno wejście w URL, żeby uruchomić payload – a dalej w grę wchodzą webshell’e, kradzież danych, pivotowanie po środowisku czy trwałe backdoory.
W omawianym przypadku scenariusz jest jeszcze gorszy z perspektywy obrońcy, bo atak nie wymaga uwierzytelnienia (unauthenticated). Według Wordfence to mogło prowadzić do zdalnego wykonania kodu i w efekcie do pełnego przejęcia witryny.
Techniczne tło: jak powstał łańcuch błędów
Problem dotyczył ścieżki odpowiedzialnej za odbieranie backupu z innego serwisu. WPvivid ma funkcję „receive a backup from another site”, która opiera się o krótkotrwały, wygenerowany w ustawieniach klucz. W kodzie za odbiór pliku odpowiada metoda send_to_site() w klasie WPvivid_Send_to_site.
Z opisu analizy wynika, że łańcuch prowadzący do wykorzystania podatności składał się z kilku elementów:
- Wtyczka odszyfrowywała dane przesyłane w
$_POST['wpvivid_content'](base64) przy użyciu mechanizmu opartego o RSA + szyfr symetryczny (w opisie pojawia się Rijndael/AES przezphpseclib). - Klucz sesyjny był odszyfrowywany, a następnie wykorzystywany do inicjalizacji szyfru symetrycznego. Klucz generowany w ustawieniach miał posłużyć do bezpiecznej wymiany/odbioru danych.
- Błąd w obsłudze przypadku niepowodzenia: gdy odszyfrowanie klucza prywatnym kluczem (w praktyce wskazano
openssl_private_decrypt()jako punkt niepowodzenia w procesie) się nie udało, wykonanie nie było przerywane, a w miejsce klucza przekazywana była wartość logicznafalse. - Biblioteka szyfrująca (phpseclib) traktowała
falsejak ciąg bajtów o wartości0x00(null bytes). W efekcie powstawał przewidywalny klucz do szyfrowania/odszyfrowywania, który atakujący mógł odtworzyć i użyć do przygotowania poprawnie „zaszyfrowanego” payloadu. - Po odszyfrowaniu wtyczka przyjmowała nazwę pliku z payloadu bez sanitizacji ścieżki, co umożliwiało directory traversal i „wyjście” poza chroniony katalog backupów.
- Na końcu brakowało skutecznych ograniczeń typu/rozszerzenia wgrywanego pliku, więc możliwe było umieszczenie np. pliku PHP w publicznie dostępnym katalogu i uruchomienie go w przeglądarce.
- Wektor wykorzystania był wiązany z parametrem akcji
wpvivid_action=send_to_site(wektor odbioru pliku).
Fragmenty kodu, które pokazują sedno problemu
Poniżej – dla kontekstu – kluczowy fragment przepływu z send_to_site(), w którym pobierane są ustawienia tokenu/klucza i wykonywane jest odszyfrowanie danych z wpvivid_content:
public function send_to_site()
{
include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';
$test_log = new WPvivid_Log();
$test_log->CreateLogFile('test_backup','no_folder','transfer');
$test_log->WriteLog('test upload.','notice');
try
{
if(isset($_POST['wpvivid_content']))
{
global $wpvivid_plugin;
$wpvivid_plugin->wpvivid_log = new WPvivid_Log();
$default = array();
$option = get_option('wpvivid_api_token',$default);
if(empty($option))
{
die();
}
if($option['expires'] != 0 && $option['expires'] < time())
{
die();
}
$crypt = new WPvivid_crypt(base64_decode($option['private_key']));
$body = base64_decode($_POST['wpvivid_content']);
$data = $crypt->decrypt_message($body);
// ... dalsza obsługa
}
}
catch (Exception $e)
{
// ...
}
}Z kolei w decrypt_message() kluczową rolę odgrywała sytuacja, w której odszyfrowanie klucza RSA zwracało false, ale kod i tak przechodził do setKey():
public function decrypt_message($message)
{
$len = substr($message, 0, 3);
$len = hexdec($len);
$key = substr($message, 3, $len);
$cipherlen = substr($message, ($len + 3), 16);
$cipherlen = hexdec($cipherlen);
$data = substr($message, ($len + 19), $cipherlen);
$rsa = new Crypt_RSA();
$rsa->loadKey($this->public_key);
$key = $rsa->decrypt($key);
$rij = new Crypt_Rijndael();
$rij->setKey($key);
return $rij->decrypt($data);
}Jak to naprawiono: co zmieniono w 0.9.124
Deweloper wydał poprawkę w wersji 0.9.124. Z opisu Wordfence wynika, że łatka składa się z dwóch istotnych zmian.
1) Zatrzymanie procesu, gdy klucz RSA nie został odszyfrowany
Dodano sprawdzenie, czy $key jest fałszywe/puste. Jeśli tak, funkcja przerywa i zwraca false, zamiast inicjalizować szyfr symetryczny przewidywalnym kluczem:
public function decrypt_message($message)
{
$len = substr($message, 0, 3);
$len = hexdec($len);
$key = substr($message, 3, $len);
$cipherlen = substr($message, ($len + 3), 16);
$cipherlen = hexdec($cipherlen);
$data = substr($message, ($len + 19), $cipherlen);
$rsa = new Crypt_RSA();
$rsa->loadKey($this->public_key);
$key = $rsa->decrypt($key);
if ($key === false || empty($key))
{
return false;
}
$rij = new Crypt_Rijndael();
$rij->setKey($key);
return $rij->decrypt($data);
}2) Ograniczenie typu plików w send_to_site() + bezpieczniejsza nazwa
Po stronie uploadu dodano mechanizm „utwardzenia” nazwy oraz walidację rozszerzenia. W praktyce: nazwa pliku jest sprowadzana do basename(), czyszczona regexem z niedozwolonych znaków, a następnie sprawdzane jest, czy rozszerzenie należy do listy dozwolonych formatów backupu:
$safe_name = basename($params['name']);
$safe_name = preg_replace('/[^a-zA-Z0-9._-]/', '', $safe_name);
$allowed_extensions = array('zip', 'gz', 'tar', 'sql');
$file_ext = strtolower(pathinfo($safe_name, PATHINFO_EXTENSION));
if (!in_array($file_ext, $allowed_extensions, true))
{
$ret['result'] = WPVIVID_FAILED;
$ret['error'] = 'Invalid file type - only backup files allowed.';
echo wp_json_encode($ret);
die();
}Reakcja ekosystemu: Wordfence, firewall i terminy
Zgłoszenie i obsługa odbyły się w ramach Wordfence Bug Bounty Program. Podatność została odkryta i odpowiedzialnie zgłoszona przez Lucas Montes (NiRoX), zaledwie pięć dni po tym, jak błąd został wprowadzony (wg opisu Wordfence). Badacz otrzymał nagrodę 2,145.00 USD.
Jeśli korzystasz z Wordfence, istotne są też terminy dystrybucji reguł WAF (firewall). Użytkownicy Wordfence Premium, Wordfence Care i Wordfence Response dostali regułę blokującą próby wykorzystania podatności 22 stycznia 2026. W przypadku Wordfence Free ta sama ochrona miała trafić z opóźnieniem 30 dni – 21 lutego 2026.
Timeline ujawnienia (disclosure) – konkretne daty
- January 12, 2026 – zgłoszenie podatności (Arbitrary File Upload) do Wordfence Bug Bounty Program.
- January 22, 2026 – weryfikacja zgłoszenia i potwierdzenie PoC; kontakt z producentem i zaproszenie do użycia Wordfence Vulnerability Management Portal.
- January 22, 2026 – użytkownicy Wordfence Premium/Care/Response otrzymali regułę WAF chroniącą przed exploitami.
- January 23, 2026 – producent odpowiedział i wybrał komunikację mailową w ramach disclosure.
- January 23, 2026 – przekazanie pełnych szczegółów; producent potwierdził i rozpoczął prace nad poprawką.
- January 28, 2026 – wydanie w pełni załatanej wersji wtyczki: 0.9.124.
- February 21, 2026 – planowana data udostępnienia ochrony także dla Wordfence Free.
Co powinieneś zrobić jako admin/dev (checklista)
- Sprawdź, czy na stronie jest zainstalowana wtyczka WPvivid Backup & Migration (slug
wpvivid-backuprestore). - Zweryfikuj wersję: podatne są <= 0.9.123.
- Zaktualizuj wtyczkę do wersji 0.9.124 (lub nowszej, jeśli jest dostępna).
- Jeśli używasz funkcji odbierania kopii z innej witryny: przejrzyj ustawienia i upewnij się, że generowane klucze są używane tylko wtedy, gdy faktycznie tego potrzebujesz, oraz że mają krótką ważność (wtyczka i tak ogranicza ją do 24h).
- Jeżeli masz podejrzenie kompromitacji (np. nietypowe pliki w
wp-content/uploads, nieznane pliki.phpw katalogach publicznych, niepokojące logi), potraktuj to jako incydent bezpieczeństwa i przeprowadź pełną analizę oraz czyszczenie.
Podsumowanie
CVE-2026-1357 w WPvivid Backup to klasyczny przykład tego, jak pozornie drobna decyzja w obsłudze błędu kryptograficznego (kontynuowanie wykonania po nieudanym odszyfrowaniu i przekazanie false) może doprowadzić do przewidywalnego klucza i dalej do pełnego łańcucha ataku. Do tego doszedł brak sanitizacji ścieżek/nazw plików i brak twardych ograniczeń typu plików. Wersja 0.9.124 adresuje problem: zatrzymuje proces przy błędnym kluczu i dopuszcza wyłącznie rozszerzenia typowe dla backupów (zip, gz, tar, sql).
Odniesienia / Źródła
- 800,000 WordPress Sites Affected by Arbitrary File Upload Vulnerability in WPvivid Backup WordPress Plugin
- Migration, Backup, Staging <= 0.9.123 — Unauthenticated Arbitrary File Upload
- CVE-2026-1357
- WPvivid Backup & Migration (wpvivid-backuprestore)
- Wordfence Bug Bounty Program
- Wordfence Vulnerability Management Portal
Katarzyna Nowak
Redaktor naczelna zespołu polskiego, architekt enterprise Java i mikroserwisów. Projektowanie i optymalizacja systemów wielkoskalowych to moja specjalność.
Wszystkie wpisyWięcej od Katarzyna Nowak
WordPress wraca do trzech dużych wydań w 2026: start planów dla 7.0, AI Client, odświeżenie admina i PHP 7.4 na stole
Krytyczna luka w wtyczce WordPress Modular DS (CVE-2026-23550): aktywne ataki i szybkie kroki obrony
Automatyzacja formularzy w WordPressie z n8n i WPForms: od webhooka do gotowego workflow