
A/B Docker-Lab + PoC für CVE-2026-32475 (Elementor Pro Forms – nicht authentifizierter beliebiger Datei-Upload -> RCE über Validierungs-/Verschiebe-Schleifen-Desynchronisierung)
Ein A/B-Docker-Lab, das CVE-2026-32475 reproduziert (CVSS 9.x, Patchstack; Elementor Pro Forms-Datei-Upload-Feld, betroffen ≤ 4.2.1, behoben in 4.2.2 am 19.08.2026; gemeldet von Tin Pham / TF1T). Nicht authentifiziert, keine Benutzerinteraktion.
Nur für autorisierte Tests / Bildung. Dieses Lab läuft vollständig auf
127.0.0.1in wegwerfbaren Docker-Containern. Richten Sie den PoC nicht gegen Systeme, für die Sie nicht ausdrücklich autorisiert sind.build.shlädt den Elementor-Pro-Quellcode von einem öffentlichen GPL-Spiegel; in diesem Repository wird kein Premium-Code weiterverbreitet.
modules/forms/fields/upload.php. Zwei Schleifen iterieren über dieselbe umgeformte
$_FILES['form_fields'][$id], behandeln aber einen leeren Teil (UPLOAD_ERR_NO_FILE) unterschiedlich:
// validation() — Zeile ~269
foreach ( $_FILES['form_fields'][ $id ] as $index => $file ) {
if ( ! $field['required'] && UPLOAD_ERR_NO_FILE === $file['error'] ) {
return; // <-- bricht die GESAMTE restliche Validierung dieses Feldes ab
}
...
if ( ! $this->is_file_type_valid( $field, $file ) ) { // Erweiterungs-Zulassungs-/Sperrliste
$ajax_handler->add_error( $id, 'This file type is not allowed.' );
}
}
// process_field() — Zeile ~418
foreach ( $_FILES['form_fields'][ $id ] as $index => $file ) {
if ( UPLOAD_ERR_NO_FILE === $file['error'] ) {
continue; // <-- überspringt NUR den leeren Teil, fährt fort
}
$filename = uniqid() . '.' . pathinfo( $file['name'], PATHINFO_EXTENSION ); // Angreifer-Erweiterung, keine erneute Prüfung
move_uploaded_file( $file['tmp_name'], trailingslashit( $uploads_dir ) . $filename );
}
Senden Sie ein optionales Upload-Feld mit zwei Teilen — zuerst einen leeren, dann die PHP-
Payload. validation() trifft auf den leeren Teil und führt return aus, bevor die .php jemals
geprüft wird; process_field() führt continue über den leeren Teil aus und verschiebt die .php nach
wp-content/uploads/elementor/forms/<uniqid()>.php. Die Blockliste get_blacklist_file_ext()
(php,phtml,pht,shtml,…) läuft nur innerhalb von validation(), wird also vollständig umgangen.
Vollständig ohne Authentifizierung: wp_ajax_nopriv_elementor_pro_forms_send_form (ajax-handler.php:295).
get_ensure_upload_dir() legt eine .htaccess im Formularverzeichnis ab, die nur
Options -Indexes + Header set Content-Disposition attachment enthält. Das ist ein Browser-Download-
Hinweis — auf Apache + mod_php führt die .php trotzdem serverseitig aus; der Angreifer (curl) erhält
einfach die Befehlsausgabe mit einem Download-Header. Das Formularmodul enthält keine unlink-Bereinigung,
die Shell bleibt also bestehen.
Eine veröffentlichte Seite mit einem Elementor-Pro-Formular-Widget, das ein Datei-Upload-Feld hat, dessen
Erforderlich = Nein ist (der ! $field['required']-Zweig). Die Feldkonfiguration wird serverseitig aus
_elementor_data gelesen, der Angreifer kann sie also nicht umschalten — aber „optionale Anhänge“-Felder sind üblich.
Gespeicherter Name = uniqid() = %08x%05x = (Unix-Sekunde)(Mikrosekunde). Die Sekunde wird exakt
durch den HTTP-Date-Antwortheader preisgegeben; nur die Mikrosekunde (0–999999) ist unbekannt →
eine begrenzte Online-GET-Brute-Force mit ≤10⁶ Versuchen. poc.py --recover verankert die Mikrosekunde an der
Antwortankunftszeit (am selben Standort / NTP-synchronisiert ⇒ Sekunden–Minuten; hier: ~26k Anfragen) und
verwandelt den Schreibzugriff in Remote-Code-Ausführung.
| Dienst | Port | Elementor Pro |
|---|---|---|
wp-vuln | http://127.0.0.1:8975 | 3.6.4 — verwundbar (authentischer Baum; validation()-Desynchronisation identisch zu ≤4.2.1) |
wp-patched | http://127.0.0.1:8976 | 3.6.4 mit dem 4.2.2-äquivalenten Fix (return → continue, Ausrichtung der Schleifen) |
Stack: wordpress:php7.4-apache (mod_php) + MariaDB 10.6 + Elementor (kostenlos) 3.6.8 + Elementor Pro 3.6.4.
Jede Variante hat einen Admin (admin/labpass) und eine veröffentlichte Seite „CVE-2026-32475 Lab“
(post_id=5, form_id=frm00001, Upload-Feld field_cv, Erforderlich=Nein).
Versionshinweis. Es gibt keinen sauberen öffentlich weiterverbreitbaren 4.2.1/4.2.2-Quellcode, daher läuft das Lab auf dem authentischen 3.6.4-Baum, dessen
validation()/process_field()-Desynchronisation byte-identisch mit dem Code ist, den CVE-2026-32475 beschreibt (der Fehler ist seit langem latent; 4.2.2 hat die beiden Schleifen ausgerichtet). Der A/B-„gepatchte“ Build wendet genau diese Ausrichtung an.
Erfordert Docker + Docker Compose und ausgehenden Netzwerkzugriff (zum Ziehen von Images, Elementor kostenlos und
des Elementor-Pro-Quellspiegels). build.sh klont den authentischen Elementor-Pro-Baum automatisch.
git clone https://github.com/dinosn/cve-2026-32475-elementor-pro-lab
cd cve-2026-32475-elementor-pro-lab
bash build.sh # Plugin klonen + compose up + WP/Elementor installieren + Formularseite erstellen (beide Varianten)
bash verify.sh # A/B-Nachweis: verwundbar schreibt+führt eine Shell aus; gepatcht lehnt ab. Räumt auf.
# vollständig remote, nicht authentifizierte RCE (extrahiert post_id/form_id/field_id automatisch aus der Seite):
python3 poc.py -t http://127.0.0.1:8975 --page-id 5 --recover -c "id; uname -a"
# nur die Blocklisten-Umgehung nachweisen (Shell zur Inspektion hinterlassen):
python3 poc.py -t http://127.0.0.1:8975 --post-id 5 --form-id frm00001 --field-id field_cv
docker-compose.yml — 2× (WordPress + MariaDB), vuln:8975 / patched:8976elementor-pro-vuln/ — authentisches Elementor Pro 3.6.4 (verwundbar); generiert von build.sh, git-ignoriertelementor-pro-patched/ — derselbe Baum, validation() return→continue; generiert von patch_pro.sh, git-ignoriertevidence/verify_transcript.txt — ein aufgezeichneter erfolgreicher verify.sh-Laufinstall_wp.sh — wp-cli-Installation von WP + Elementor kostenlos + Pro aktivieren + Formularseite erstellen + Upload-Berechtigungen korrigierensetup_page.php — erstellt die Elementor-Formularseite mit einem optionalen Upload-Feldpatch_pro.sh — leitet den gepatchten Baum ab + gibt die einzeilige Diff auspoc.py — nicht authentifizierter Desynchronisations-Upload + uniqid()-Dateinamen-Wiederherstellung + RCEverify.sh — A/B-Nachweis mit einem unabhängigen Dateisystem-Orakel (beweist Ausführung, nicht nur Schreiben)build.sh — Einmal-Buildcd /root/cve-2026-32475-lab && docker compose down -v