VYPR
High severityNVD Advisory· Published Apr 26, 2023· Updated Feb 3, 2025

CVE-2022-25277

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.

PackageAffected versionsPatched versions
drupal/corePackagist
>= 8.0.0, < 9.3.199.3.19
drupal/corePackagist
>= 9.4.0, < 9.4.39.4.3

Affected products

3

Patches

2
1cd1830d79f2

SA-CORE-2022-014 by elarlang, pwolanin, xjm, mcdruid, effulgentsia, greggles, jenlampton, larowlan, longwave

https://github.com/drupal/corexjmJul 20, 2022via ghsa
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'],
         ];
       }
     
    
5d464ea4407c

SA-CORE-2022-014 by elarlang, pwolanin, xjm, mcdruid, effulgentsia, greggles, jenlampton, larowlan, longwave

https://github.com/drupal/corexjmJul 20, 2022via ghsa
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

News mentions

0

No linked articles in our index yet.