
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) प्राप्त कर सकता है।
| विशेषता | मान |
|---|---|
| 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 से बदलता है) |
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) रूप में संग्रहीत हो जाती हैं।