CVE-2022-25277
Description
Drupal core sanitizes filenames with dangerous extensions upon upload (reference: SA-CORE-2020-012) and strips leading and trailing dots from filenames to prevent uploading server configuration files (reference: SA-CORE-2019-010). However, the protections for these two vulnerabilities previously did not work correctly together. As a result, if the site were configured to allow the upload of files with an htaccess extension, these files' filenames would not be properly sanitized. This could allow bypassing the protections provided by Drupal core's default .htaccess files and possible remote code execution on Apache web servers. This issue is mitigated by the fact that it requires a field administrator to explicitly configure a file field to allow htaccess as an extension (a restricted permission), or a contributed module or custom code that overrides allowed file uploads.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Drupal core's filename sanitization fails when htaccess extension is allowed, enabling Apache RCE via bypassed .htaccess protections.
Vulnerability
Overview
CVE-2022-25277 is a vulnerability in Drupal core's file upload sanitization logic. Drupal previously implemented two separate protections: one that sanitizes filenames with dangerous extensions (SA-CORE-2020-012) and another that strips leading and trailing dots from filenames to prevent uploading server configuration files (SA-CORE-2019-010). However, these two protections did not work correctly together. When a site is configured to allow the upload of files with an .htaccess extension, the combined sanitization fails, allowing a file with a name like .htaccess to bypass the dot-stripping mechanism [2][3].
Exploitation
Conditions
Exploitation requires that a field administrator explicitly configures a file field to allow the htaccess extension—a restricted permission—or that a contributed module or custom code overrides the allowed file upload extensions. The attacker must be able to upload a file with a crafted name that, after the flawed sanitization, results in an .htaccess file being placed on the server. This attack is specific to Apache web servers because the .htaccess file is an Apache configuration directive [3].
Impact
If successful, an attacker can overwrite or create an .htaccess file in the Drupal files directory, bypassing the protections provided by Drupal core's default .htaccess files. This can lead to arbitrary PHP code execution on the Apache web server, as the attacker can configure Apache to execute arbitrary code [2][3].
Mitigation
Drupal has released patches in versions 9.3.19 and 9.4.3. All versions of Drupal 9 prior to 9.3.x are end-of-life and do not receive security coverage. Drupal 7 core is not affected. Administrators should update immediately and audit their files directory for any unauthorized .htaccess files [3].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
drupal/corePackagist | >= 8.0.0, < 9.3.19 | 9.3.19 |
drupal/corePackagist | >= 9.4.0, < 9.4.3 | 9.4.3 |
Affected products
3- osv-coords2 versions
>= 8.0.0, < 9.3.19+ 1 more
- (no CPE)range: >= 8.0.0, < 9.3.19
- (no CPE)range: >= 8.0.0, < 9.3.19
- Drupal/Corev5Range: 9.4
Patches
21cd1830d79f2SA-CORE-2022-014 by elarlang, pwolanin, xjm, mcdruid, effulgentsia, greggles, jenlampton, larowlan, longwave
3 files changed · +14 −4
lib/Drupal/Core/File/FileSystemInterface.php+2 −2 modified@@ -37,14 +37,14 @@ interface FileSystemInterface { * * @see \Drupal\Core\File\FileSystemInterface::INSECURE_EXTENSION_REGEX */ - public const INSECURE_EXTENSIONS = ['phar', 'php', 'pl', 'py', 'cgi', 'asp', 'js']; + public const INSECURE_EXTENSIONS = ['phar', 'php', 'pl', 'py', 'cgi', 'asp', 'js', 'htaccess']; /** * The regex pattern used when checking for insecure file types. * * @see \Drupal\Core\File\FileSystemInterface::INSECURE_EXTENSIONS */ - public const INSECURE_EXTENSION_REGEX = '/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i'; + public const INSECURE_EXTENSION_REGEX = '/\.(phar|php|pl|py|cgi|asp|js|htaccess)(\.|$)/i'; /** * Moves an uploaded file to a new location.
modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php+10 −1 modified@@ -63,6 +63,15 @@ public function sanitizeName(FileUploadSanitizeNameEvent $event): void { $filename = array_shift($filename_parts); // Remove final extension. $final_extension = (string) array_pop($filename_parts); + // Check if we're dealing with a dot file that is also an insecure extension + // e.g. .htaccess. In this scenario there is only one 'part' and the + // extension becomes the filename. We use the original filename from the + // event rather than the trimmed version above. + $insecure_uploads = $this->config->get('allow_insecure_uploads'); + if (!$insecure_uploads && $final_extension === '' && str_contains($event->getFilename(), '.') && in_array(strtolower($filename), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) { + $final_extension = $filename; + $filename = ''; + } $extensions = $event->getAllowedExtensions(); if (!empty($extensions) && !in_array(strtolower($final_extension), $extensions, TRUE)) { @@ -76,7 +85,7 @@ public function sanitizeName(FileUploadSanitizeNameEvent $event): void { return; } - if (!$this->config->get('allow_insecure_uploads') && in_array(strtolower($final_extension), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) { + if (!$insecure_uploads && in_array(strtolower($final_extension), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) { if (empty($extensions) || in_array('txt', $extensions, TRUE)) { // Add .txt to potentially executable files prior to munging to help prevent // exploits. This results in a filenames like filename.php being changed to
modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php+2 −1 modified@@ -84,7 +84,8 @@ public function provideFilenames() { 'filename is munged' => ['foo.phar.png.php.jpg', 'jpg png', 'foo.phar_.png_.php_.jpg'], 'filename is munged regardless of case' => ['FOO.pHAR.PNG.PhP.jpg', 'jpg png', 'FOO.pHAR_.PNG_.PhP_.jpg'], 'null bytes are removed' => ['foo' . chr(0) . '.txt' . chr(0), '', 'foo.txt'], - 'dot files are renamed' => ['.htaccess', '', 'htaccess'], + 'dot files are renamed' => ['.git', '', 'git'], + 'htaccess files are renamed even if allowed' => ['.htaccess', 'htaccess txt', '.htaccess_.txt', '.htaccess'], ]; }
5d464ea4407cSA-CORE-2022-014 by elarlang, pwolanin, xjm, mcdruid, effulgentsia, greggles, jenlampton, larowlan, longwave
3 files changed · +14 −4
lib/Drupal/Core/File/FileSystemInterface.php+2 −2 modified@@ -37,14 +37,14 @@ interface FileSystemInterface { * * @see \Drupal\Core\File\FileSystemInterface::INSECURE_EXTENSION_REGEX */ - public const INSECURE_EXTENSIONS = ['phar', 'php', 'pl', 'py', 'cgi', 'asp', 'js']; + public const INSECURE_EXTENSIONS = ['phar', 'php', 'pl', 'py', 'cgi', 'asp', 'js', 'htaccess']; /** * The regex pattern used when checking for insecure file types. * * @see \Drupal\Core\File\FileSystemInterface::INSECURE_EXTENSIONS */ - public const INSECURE_EXTENSION_REGEX = '/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i'; + public const INSECURE_EXTENSION_REGEX = '/\.(phar|php|pl|py|cgi|asp|js|htaccess)(\.|$)/i'; /** * Moves an uploaded file to a new location.
modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php+10 −1 modified@@ -63,6 +63,15 @@ public function sanitizeName(FileUploadSanitizeNameEvent $event): void { $filename = array_shift($filename_parts); // Remove final extension. $final_extension = (string) array_pop($filename_parts); + // Check if we're dealing with a dot file that is also an insecure extension + // e.g. .htaccess. In this scenario there is only one 'part' and the + // extension becomes the filename. We use the original filename from the + // event rather than the trimmed version above. + $insecure_uploads = $this->config->get('allow_insecure_uploads'); + if (!$insecure_uploads && $final_extension === '' && str_contains($event->getFilename(), '.') && in_array(strtolower($filename), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) { + $final_extension = $filename; + $filename = ''; + } $extensions = $event->getAllowedExtensions(); if (!empty($extensions) && !in_array(strtolower($final_extension), $extensions, TRUE)) { @@ -76,7 +85,7 @@ public function sanitizeName(FileUploadSanitizeNameEvent $event): void { return; } - if (!$this->config->get('allow_insecure_uploads') && in_array(strtolower($final_extension), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) { + if (!$insecure_uploads && in_array(strtolower($final_extension), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) { if (empty($extensions) || in_array('txt', $extensions, TRUE)) { // Add .txt to potentially executable files prior to munging to help prevent // exploits. This results in a filenames like filename.php being changed to
modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php+2 −1 modified@@ -84,7 +84,8 @@ public function provideFilenames() { 'filename is munged' => ['foo.phar.png.php.jpg', 'jpg png', 'foo.phar_.png_.php_.jpg'], 'filename is munged regardless of case' => ['FOO.pHAR.PNG.PhP.jpg', 'jpg png', 'FOO.pHAR_.PNG_.PhP_.jpg'], 'null bytes are removed' => ['foo' . chr(0) . '.txt' . chr(0), '', 'foo.txt'], - 'dot files are renamed' => ['.htaccess', '', 'htaccess'], + 'dot files are renamed' => ['.git', '', 'git'], + 'htaccess files are renamed even if allowed' => ['.htaccess', 'htaccess txt', '.htaccess_.txt', '.htaccess'], ]; }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-6955-67hm-vjjqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-25277ghsaADVISORY
- github.com/FriendsOfPHP/security-advisories/blob/master/drupal/core/CVE-2022-25277.yamlghsaWEB
- github.com/drupal/core/commit/1cd1830d79f221cc8490f53c2bb487dd07094f17ghsaWEB
- github.com/drupal/core/commit/5d464ea4407c50e40dcf6cb5ee376e7b8dd36f3aghsaWEB
- www.drupal.org/sa-core-2022-014ghsaWEB
News mentions
0No linked articles in our index yet.