Elementor Pro opravil kritickú chybu nahrávania súborov
V plugine Elementor Pro sa našla kritická zraniteľnosť, ktorá útočníkovi bez účtu umožňuje nahrať ľubovoľný súbor vrátane spustiteľného PHP. Chyba zasahuje všetky verzie po 4.2.1 vrátane a môže viesť k vzdialenému spusteniu kódu aj úplnému ovládnutiu webovej stránky.
Aktualizuj Elementor Pro
Nainštaluj Elementor Pro 4.2.2 alebo novšiu verziu čo najskôr. Zraniteľnosť má skóre CVSS 9,8 z 10 a eviduje sa ako CVE-2026-32475.
Kedy je web zraniteľný
Chyba sa prejaví len pri konkrétnej konfigurácii. Web musí mať publikovanú stránku s Form widgetom z Elementor Pro a aspoň jedno pole File Upload, ktoré nemáš označené ako povinné. Elementor Pro má odhadom 6 000 000 aktívnych inštalácií, preto sa oplatí skontrolovať všetky webové stránky, ktoré spravuješ.
- V nástenke WordPressu otvor zoznam pluginov a over verziu Elementor Pro.
- Ak používaš verziu 4.2.1 alebo staršiu, aktualizuj plugin na 4.2.2 alebo novší dostupný release.
- Prejdi publikované formuláre Elementor Pro a identifikuj polia File Upload, ktoré nie sú povinné.
- Kým aktualizáciu nenasadíš, obmedz prístup k dotknutým formulárom alebo ich dočasne zruš z publikovaných stránok.
Chyba prerušila validáciu po prvom prázdnom súbore
Formulár spracúva odoslanie cez metódu ajax_send_form() v triede Ajax_Handler, ktorú môže zavolať aj návštevník bez prihlásenia. Plugin vloží dáta formulára a nahrané súbory do objektu Form_Record, potom ich pri poli File Upload odovzdá metódam validation() a process_field() v triede ElementorProModulesFormsFieldsUpload.
Metóda validation() najprv upraví indexy súborov, načíta $_FILES['form_fields'] a pri nastavenom limite kontroluje maximálny počet súborov. Vo validačnom cykle však pri nepovinnom poli ukončila celú metódu príkazom return, ak prvá položka poľa obsahovala chybu UPLOAD_ERR_NO_FILE. Namiesto toho mala použiť continue, ktorý by preskočil iba prázdnu položku a pokračoval kontrolou ďalšej.
public function validation( $field, ClassesForm_Record $record, ClassesAjax_Handler $ajax_handler ) {
static $upload_errors = false;
if ( ! $upload_errors ) {
$upload_errors = [
UPLOAD_ERR_OK => esc_html__( 'There is no error, the file uploaded with success.', 'elementor-pro' ),
UPLOAD_ERR_INI_SIZE => sprintf( esc_html__( 'The uploaded file exceeds the %1$s directive in %2$s.', 'elementor-pro' ), 'upload_max_filesize', 'php.ini' ),
UPLOAD_ERR_FORM_SIZE => sprintf( esc_html__( 'The uploaded file exceeds the %s directive that was specified in the HTML form.', 'elementor-pro' ), 'MAX_FILE_SIZE' ),
UPLOAD_ERR_PARTIAL => esc_html__( 'The uploaded file was only partially uploaded.', 'elementor-pro' ),
UPLOAD_ERR_NO_FILE => esc_html__( 'No file was uploaded.', 'elementor-pro' ),
UPLOAD_ERR_NO_TMP_DIR => esc_html__( 'Missing a temporary folder.', 'elementor-pro' ),
UPLOAD_ERR_CANT_WRITE => esc_html__( 'Failed to write file to disk.', 'elementor-pro' ),
UPLOAD_ERR_EXTENSION => sprintf( esc_html__( 'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with %s may help.', 'elementor-pro' ), 'phpinfo()' ),
];
}
$this->fix_file_indices();
$id = $field['id'];
$files = Utils::_unstable_get_super_global_value( $_FILES, 'form_fields' );
if ( ! empty( $field['max_files'] ) ) {
if ( count( $files[$id] ) > $field['max_files'] ) {
$error_message = sprintf(
_n( 'You can upload only %d file.', 'You can upload up to %d files.', intval( $field['max_files'] ), 'elementor-pro' ),
intval( $field['max_files'] )
);
$ajax_handler->add_error( $id, $error_message );
return;
}
}
foreach ( $files[$id] as $index => $file ) {
// Pri nepovinnom poli táto vetva nesprávne ukončila celú validáciu.
if ( ! $field['required'] && UPLOAD_ERR_NO_FILE === $file['error'] ) {
return;
}
if ( $field['required'] && UPLOAD_ERR_NO_FILE === $file['error'] ) {
$ajax_handler->add_error( $id, $upload_errors[$file['error']] );
return;
}
if ( $file['error'] > UPLOAD_ERR_OK ) {
$ajax_handler->add_error( $id, $upload_errors[$file['error']] );
return;
}
if ( ! $this->is_file_type_valid( $field, $file ) ) {
$ajax_handler->add_error( $id, esc_html__( 'This file type is not allowed.', 'elementor-pro' ) );
}
if ( ! $this->is_file_size_valid( $field, $file ) ) {
$ajax_handler->add_error( $id, esc_html__( 'This file exceeds the maximum allowed size.', 'elementor-pro' ) );
}
}
}Útočník mohol pole odoslať ako pole s dvoma položkami: prvá ostala prázdna a vyvolala UPLOAD_ERR_NO_FILE, druhá obsahovala súbor so zvolenou príponou. Predčasný return tak preskočil overenie prípony cez is_file_type_valid() aj kontrolu veľkosti cez is_file_size_valid() pri každom zostávajúcom súbore v rovnakom poli.
Spracovanie potom neoverený súbor uložilo
Metóda process_field() pracovala inak: prázdnu položku korektne preskočila cez continue a pokračovala druhou položkou. Príponu prevzala priamo z názvu súboru, ktorý poslal klient, vytvorila jedinečný názov cez uniqid(), upravila ho pomocou wp_unique_filename() a súbor presunula na disk.
public function process_field( $field, ClassesForm_Record $record, ClassesAjax_Handler $ajax_handler ) {
$id = $field['id'];
$files = Utils::_unstable_get_super_global_value( $_FILES, 'form_fields' );
foreach ( $files[$id] as $index => $file ) {
if ( UPLOAD_ERR_NO_FILE === $file['error'] ) {
continue;
}
$uploads_dir = $this->get_ensure_upload_dir();
$file_extension = pathinfo( $file['name'], PATHINFO_EXTENSION );
$filename = uniqid() . '.' . $file_extension;
$filename = wp_unique_filename( $uploads_dir, $filename );
$new_file = trailingslashit( $uploads_dir ) . $filename;
if ( is_dir( $uploads_dir ) && is_writable( $uploads_dir ) ) {
$move_new_file = Plugin::instance()->php_api->move_uploaded_file( $file['tmp_name'], $new_file );
if ( false !== $move_new_file ) {
$perms = 0644;
@chmod( $new_file, $perms );
$record->add_file( $id, $index, [
'path' => $new_file,
'url' => $this->get_file_url( $filename ),
] );
} else {
$ajax_handler->add_error( $id, esc_html__( 'There was an error while trying to upload your file.', 'elementor-pro' ) );
}
} else {
$ajax_handler->add_admin_error_message( esc_html__( 'Upload directory is not writable or does not exist.', 'elementor-pro' ) );
}
}
}Pri zachovanej príponě .php sa súbor zapísal do adresára /wp-content/uploads/elementor/forms/. Útočník potom mohol nahraný súbor vyžiadať cez web a spustiť jeho PHP kód na serveri. Takýto ľubovoľný upload často otvorí cestu k webshellu a následnému úplnému kompromitovaniu webovej stránky.
Oprava vyšla 19. augusta 2026
Wordfence prijal hlásenie zraniteľnosti 24. júla 2026. Po overení proof-of-conceptu ju 27. júla oznámil tímu Elementor; 2. augusta Elementor potvrdil, že rovnakú chybu nahlásil aj ďalší výskumník a že pripravuje opravu.
Elementor Pro vydal úplnú opravu vo verzii 4.2.2 dňa 19. augusta 2026. Pôvodne pridelené CVE sa nepoužíva, keďže zraniteľnosť eviduje CVE pridelené pri druhom nahlásení: CVE-2026-32475.
Čo urob teraz
Over verziu Elementor Pro na každej svojej webovej stránke a bez odkladu nasadzuj 4.2.2 alebo novšiu verziu. Ak na niektorej stránke nájdeš publikovaný Form widget s nepovinným nahrávaním súborov, prioritne skontroluj aj možné neoprávnené súbory v /wp-content/uploads/elementor/forms/.
Zuzana Horváthová
Šéfredaktorka slovenského tímu, vývojárka WordPress tém a pluginov. Gutenberg bloky a FSE sú moja špecializácia. Moderný WordPress je cieľ.
Všetky príspevky