VYPR
Critical severityNVD Advisory· Published Apr 11, 2022· Updated Aug 3, 2024

CVE-2022-27115

CVE-2022-27115

Description

In Studio-42 elFinder 2.1.60, there is a vulnerability that causes remote code execution through file name bypass for file upload.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Studio-42 elFinder 2.1.60 allows remote code execution via file upload by appending trailing dots to bypass filename restrictions on Windows.

Vulnerability

In Studio-42 elFinder version 2.1.60, there is a vulnerability that allows remote code execution through file name bypass for file upload. The vulnerability is specific to Windows servers because the file system treats trailing dots in filenames specially. An attacker can upload a file with a malicious PHP payload (e.g., shell.php) and append two trailing dots (shell.php...) to bypass security checks that detect the file extension. The file is then stored as shell.php on the server due to Windows' automatic removal of trailing dots. This affects elFinder 2.1.60 and possibly earlier versions that lack the fix. [1][2][3]

Exploitation

An attacker needs network access to the elFinder web interface with file upload capability. The attacker uploads a file containing malicious PHP code (e.g., <?php phpinfo();?>) with a filename like shell.php... (two trailing dots). The system's file extension validation may be bypassed because the trailing dots cause the filename to be treated as having no extension or a different extension. On Windows, the filesystem automatically removes trailing dots, so the file is saved as shell.php without the dots. This allows the uploaded PHP file to be executed by the web server. The attack requires user interaction (upload action) and works only on Windows systems. [3]

Impact

Successful exploitation allows an attacker to achieve remote code execution on the server. The attacker can execute arbitrary PHP code with the privileges of the web server, potentially leading to full compromise of the application and server. The impact includes disclosure of sensitive information, modification of files, and further lateral movement. [1][2][3]

Mitigation

The vulnerability is fixed in elFinder commit 69be51e (released after 2.1.60). Users should upgrade to a version that includes this commit, or apply the patch. The fix introduces a plugin elFinderPluginWinRemoveTailDots that normalizes filenames by removing trailing dots before processing. For Windows servers, enabling this plugin (set enable to true) prevents the bypass. If immediate upgrade is not possible, administrators should disable file upload functionality or restrict upload permissions to trusted users only. The vulnerability is not listed in the KEV catalog as of the publication date. [2][3]

AI Insight generated on May 21, 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
studio-42/elfinderPackagist
< 2.1.612.1.61

Affected products

2

Patches

1
69be51eea5b4

[security] fix #3458 filename bypass leading to RCE on Windows server (#3470)

https://github.com/Studio-42/elFinderNaoki SawadaMar 9, 2022via ghsa
3 files changed · +141 0
  • php/elFinder.class.php+19 0 modified
    @@ -766,6 +766,25 @@ public function __construct($opts)
                 $this->utf8Encoder = $opts['utf8Encoder'];
             }
     
    +        // for LocalFileSystem driver on Windows server
    +        if (DIRECTORY_SEPARATOR !== '/') {
    +            if (empty($opts['bind'])) {
    +                $opts['bind'] = array();
    +            }
    +
    +            $_key = 'upload.pre mkdir.pre mkfile.pre rename.pre archive.pre ls.pre';
    +            if (!isset($opts['bind'][$_key])) {
    +                $opts['bind'][$_key] = array();
    +            }
    +            array_push($opts['bind'][$_key], 'Plugin.WinRemoveTailDots.cmdPreprocess');
    +
    +            $_key = 'upload.presave paste.copyfrom';
    +            if (!isset($opts['bind'][$_key])) {
    +                $opts['bind'][$_key] = array();
    +            }
    +            array_push($opts['bind'][$_key], 'Plugin.WinRemoveTailDots.onUpLoadPreSave');
    +        }
    +
             // bind events listeners
             if (!empty($opts['bind']) && is_array($opts['bind'])) {
                 $_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
    
  • php/elFinderVolumeLocalFileSystem.class.php+8 0 modified
    @@ -265,6 +265,14 @@ protected function configure()
             }
     
             $this->statOwner = (!empty($this->options['statOwner']));
    +
    +        // enable WinRemoveTailDots plugin on Windows server
    +        if (DIRECTORY_SEPARATOR !== '/') {
    +            if (!isset($this->options['plugin'])) {
    +                $this->options['plugin'] = array();
    +            }
    +            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
    +        }
         }
     
         /**
    
  • php/plugins/WinRemoveTailDots/plugin.php+114 0 added
    @@ -0,0 +1,114 @@
    +<?php
    +/**
    + * This class describes elFinder plugin window remove tail dots.
    + * This plugin is automatically loaded on the Windows server
    + * and enabled in the LocalFileSystem driver.
    + */
    +class elFinderPluginWinRemoveTailDots extends elFinderPlugin
    +{
    +    private $replaced = array();
    +    private $keyMap = array(
    +        'ls' => 'intersect',
    +        'upload' => 'renames',
    +        'mkdir' => array('name', 'dirs')
    +    );
    +
    +    public function __construct($opts)
    +    {
    +        $defaults = array(
    +            'enable' => false,  // For control by volume driver
    +        );
    +
    +        $this->opts = array_merge($defaults, $opts);
    +    }
    +
    +    public function cmdPreprocess($cmd, &$args, $elfinder, $volume)
    +    {
    +        $opts = $this->getCurrentOpts($volume);
    +        if (!$opts['enable']) {
    +            return false;
    +        }
    +        $this->replaced[$cmd] = array();
    +        $key = (isset($this->keyMap[$cmd])) ? $this->keyMap[$cmd] : 'name';
    +
    +        if (is_array($key)) {
    +            $keys = $key;
    +        } else {
    +            $keys = array($key);
    +        }
    +        foreach ($keys as $key) {
    +            if (isset($args[$key])) {
    +                if (is_array($args[$key])) {
    +                    foreach ($args[$key] as $i => $name) {
    +                        if ($cmd === 'mkdir' && $key === 'dirs') {
    +                            // $name need '/' as prefix see #2607
    +                            $name = '/' . ltrim($name, '/');
    +                            $_names = explode('/', $name);
    +                            $_res = array();
    +                            foreach ($_names as $_name) {
    +                                $_res[] = $this->normalize($_name, $opts);
    +                            }
    +                            $this->replaced[$cmd][$name] = $args[$key][$i] = join('/', $_res);
    +                        } else {
    +                            $this->replaced[$cmd][$name] = $args[$key][$i] = $this->normalize($name, $opts);
    +                        }
    +                    }
    +                } else if ($args[$key] !== '') {
    +                    $name = $args[$key];
    +                    $this->replaced[$cmd][$name] = $args[$key] = $this->normalize($name, $opts);
    +                }
    +            }
    +        }
    +        if ($cmd === 'ls' || $cmd === 'mkdir') {
    +            if (!empty($this->replaced[$cmd])) {
    +                // un-regist for legacy settings
    +                $elfinder->unbind($cmd, array($this, 'cmdPostprocess'));
    +                $elfinder->bind($cmd, array($this, 'cmdPostprocess'));
    +            }
    +        }
    +        return true;
    +    }
    +
    +    public function cmdPostprocess($cmd, &$result, $args, $elfinder, $volume)
    +    {
    +        if ($cmd === 'ls') {
    +            if (!empty($result['list']) && !empty($this->replaced['ls'])) {
    +                foreach ($result['list'] as $hash => $name) {
    +                    if ($keys = array_keys($this->replaced['ls'], $name)) {
    +                        if (count($keys) === 1) {
    +                            $result['list'][$hash] = $keys[0];
    +                        } else {
    +                            $result['list'][$hash] = $keys;
    +                        }
    +                    }
    +                }
    +            }
    +        } else if ($cmd === 'mkdir') {
    +            if (!empty($result['hashes']) && !empty($this->replaced['mkdir'])) {
    +                foreach ($result['hashes'] as $name => $hash) {
    +                    if ($keys = array_keys($this->replaced['mkdir'], $name)) {
    +                        $result['hashes'][$keys[0]] = $hash;
    +                    }
    +                }
    +            }
    +        }
    +    }
    +
    +    // NOTE: $thash is directory hash so it unneed to process at here
    +    public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume)
    +    {
    +        $opts = $this->getCurrentOpts($volume);
    +        if (!$opts['enable']) {
    +            return false;
    +        }
    +
    +        $name = $this->normalize($name, $opts);
    +        return true;
    +    }
    +
    +    protected function normalize($str, $opts)
    +    {
    +        $str = rtrim($str, '.');
    +        return $str;
    +    }
    +} // END class elFinderPluginWinRemoveTailDots
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.