VYPR
Critical severityNVD Advisory· Published Jun 14, 2021· Updated Aug 3, 2024

Multiple vulnerabilities leading to RCE

CVE-2021-32682

Description

elFinder is an open-source file manager for web, written in JavaScript using jQuery UI. Several vulnerabilities affect elFinder 2.1.58. These vulnerabilities can allow an attacker to execute arbitrary code and commands on the server hosting the elFinder PHP connector, even with minimal configuration. The issues were patched in version 2.1.59. As a workaround, ensure the connector is not exposed without authentication.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
studio-42/elfinderPackagist
< 2.1.592.1.59

Affected products

1

Patches

1
a106c350b7df

Merge pull request from GHSA-wph3-44rj-92pr

https://github.com/Studio-42/elFinderNaoki SawadaJun 13, 2021via ghsa
4 files changed · +195 77
  • php/elFinder.class.php+153 60 modified
    @@ -419,6 +419,21 @@ class elFinder
          */
         protected $removeContentSaveIds = array();
     
    +    /**
    +     * LAN class allowed when uploading via URL
    +     * 
    +     * Array keys are 'local', 'private_a', 'private_b', 'private_c' and 'link'
    +     * 
    +     * local:     127.0.0.0/8
    +     * private_a: 10.0.0.0/8
    +     * private_b: 172.16.0.0/12
    +     * private_c: 192.168.0.0/16
    +     * link:      169.254.0.0/16
    +     *
    +     * @var        array
    +     */
    +    protected $uploadAllowedLanIpClasses = array();
    +
         /**
          * Flag of throw Error on exec()
          *
    @@ -713,6 +728,10 @@ public function __construct($opts)
                 $this->itemLockExpire = intval($opts['itemLockExpire']);
             }
     
    +        if (!empty($opts['uploadAllowedLanIpClasses'])) {
    +            $this->uploadAllowedLanIpClasses = array_flip($opts['uploadAllowedLanIpClasses']);
    +        }
    +
             // deprecated settings
             $this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes';
             self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches';
    @@ -2524,16 +2543,87 @@ protected function abort($args = array())
             }
             $flagFile = elFinder::$connectionFlagsPath . DIRECTORY_SEPARATOR . 'elfreq%s';
             if (!empty($args['makeFile'])) {
    -            self::$abortCheckFile = sprintf($flagFile, $args['makeFile']);
    +            self::$abortCheckFile = sprintf($flagFile, self::filenameDecontaminate($args['makeFile']));
                 touch(self::$abortCheckFile);
                 $GLOBALS['elFinderTempFiles'][self::$abortCheckFile] = true;
                 return;
             }
     
    -        $file = !empty($args['id']) ? sprintf($flagFile, $args['id']) : self::$abortCheckFile;
    +        $file = !empty($args['id']) ? sprintf($flagFile, self::filenameDecontaminate($args['id'])) : self::$abortCheckFile;
             $file && is_file($file) && unlink($file);
         }
     
    +    /**
    +     * Validate an URL to prevent SSRF attacks.
    +     *
    +     * To prevent any risk of DNS rebinding, always use the IP address resolved by
    +     * this method, as returned in the array entry `ip`.
    +     *
    +     * @param string $url
    +     *
    +     * @return false|array
    +     */
    +    protected function validate_address($url)
    +    {
    +        $info = parse_url($url);
    +        $host = trim(strtolower($info['host']), '.');
    +        // do not support IPv6 address
    +        if (preg_match('/^\[.*\]$/', $host)) {
    +            return false;
    +        }
    +        // do not support non dot host
    +        if (strpos($host, '.') === false) {
    +            return false;
    +        }
    +        // do not support URL-encoded host
    +        if (strpos($host, '%') !== false) {
    +            return false;
    +        }
    +        // disallow including "localhost" and "localdomain"
    +        if (preg_match('/\b(?:localhost|localdomain)\b/', $host)) {
    +            return false;
    +        }
    +        // check IPv4 local loopback, private network and link local
    +        $ip = gethostbyname($host);
    +        if (preg_match('/^0x[0-9a-f]+|[0-9]+(?:\.(?:0x[0-9a-f]+|[0-9]+)){1,3}$/', $ip, $m)) {
    +            $long = (int)sprintf('%u', ip2long($ip));
    +            if (!$long) {
    +                return false;
    +            }
    +            $local = (int)sprintf('%u', ip2long('127.255.255.255')) >> 24;
    +            $prv1  = (int)sprintf('%u', ip2long('10.255.255.255')) >> 24;
    +            $prv2  = (int)sprintf('%u', ip2long('172.31.255.255')) >> 20;
    +            $prv3  = (int)sprintf('%u', ip2long('192.168.255.255')) >> 16;
    +            $link  = (int)sprintf('%u', ip2long('169.254.255.255')) >> 16;
    +
    +            if (!isset($this->uploadAllowedLanIpClasses['local']) && $long >> 24 === $local) {
    +                return false;
    +            }
    +            if (!isset($this->uploadAllowedLanIpClasses['private_a']) && $long >> 24 === $prv1) {
    +                return false;
    +            }
    +            if (!isset($this->uploadAllowedLanIpClasses['private_b']) && $long >> 20 === $prv2) {
    +                return false;
    +            }
    +            if (!isset($this->uploadAllowedLanIpClasses['private_c']) && $long >> 16 === $prv3) {
    +                return false;
    +            }
    +            if (!isset($this->uploadAllowedLanIpClasses['link']) && $long >> 16 === $link) {
    +                return false;
    +            }
    +            $info['ip'] = long2ip($long);
    +            if (!isset($info['port'])) {
    +                $info['port'] = $info['scheme'] === 'https' ? 443 : 80;
    +            }
    +            if (!isset($info['path'])) {
    +                $info['path'] = '/';
    +            }
    +            return $info;
    +        } else {
    +            return false;
    +        }
    +    }
    +
         /**
          * Get remote contents
          *
    @@ -2552,54 +2642,20 @@ protected function abort($args = array())
         protected function get_remote_contents(&$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null)
         {
             if (preg_match('~^(?:ht|f)tps?://[-_.!\~*\'()a-z0-9;/?:\@&=+\$,%#\*\[\]]+~i', $url)) {
    -            $info = parse_url($url);
    -            $host = trim(strtolower($info['host']), '.');
    -            // do not support IPv6 address
    -            if (preg_match('/^\[.*\]$/', $host)) {
    -                return false;
    -            }
    -            // do not support non dot host
    -            if (strpos($host, '.') === false) {
    -                return false;
    -            }
    -            // do not support URL-encoded host
    -            if (strpos($host, '%') !== false) {
    +            $info = $this->validate_address($url);
    +            if ($info === false) {
                     return false;
                 }
    -            // disallow including "localhost" and "localdomain"
    -            if (preg_match('/\b(?:localhost|localdomain)\b/', $host)) {
    -                return false;
    -            }
    -            // wildcard DNS (e.g xip.io)
    -            if (preg_match('/0x[0-9a-f]+|[0-9]+(?:\.(?:0x[0-9a-f]+|[0-9]+)){1,3}/', $host)) {
    -                $host = gethostbyname($host);
    -            }
    -            // check IPv4 local loopback, private network and link local
    -            if (preg_match('/^0x[0-9a-f]+|[0-9]+(?:\.(?:0x[0-9a-f]+|[0-9]+)){1,3}$/', $host, $m)) {
    -                $long = (int)sprintf('%u', ip2long($host));
    -                if (!$long) {
    -                    return false;
    -                }
    -                $local = (int)sprintf('%u', ip2long('127.255.255.255')) >> 24;
    -                $prv1 = (int)sprintf('%u', ip2long('10.255.255.255')) >> 24;
    -                $prv2 = (int)sprintf('%u', ip2long('172.31.255.255')) >> 20;
    -                $prv3 = (int)sprintf('%u', ip2long('192.168.255.255')) >> 16;
    -                $link = (int)sprintf('%u', ip2long('169.254.255.255')) >> 16;
    -
    -                if ($long >> 24 === $local || $long >> 24 === $prv1 || $long >> 20 === $prv2 || $long >> 16 === $prv3 || $long >> 16 === $link) {
    -                    return false;
    -                }
    -            }
                 // dose not support 'user' and 'pass' for security reasons
    -            $url = $info['scheme'].'://'.$host.(!empty($info['port'])? (':'.$info['port']) : '').$info['path'].(!empty($info['query'])? ('?'.$info['query']) : '').(!empty($info['fragment'])? ('#'.$info['fragment']) : '');
    +            $url = $info['scheme'].'://'.$info['host'].(!empty($info['port'])? (':'.$info['port']) : '').$info['path'].(!empty($info['query'])? ('?'.$info['query']) : '').(!empty($info['fragment'])? ('#'.$info['fragment']) : '');
                 // check by URL upload filter
                 if ($this->urlUploadFilter && is_callable($this->urlUploadFilter)) {
                     if (!call_user_func_array($this->urlUploadFilter, array($url, $this))) {
                         return false;
                     }
                 }
    -            $method = (function_exists('curl_exec') && !ini_get('safe_mode') && !ini_get('open_basedir')) ? 'curl_get_contents' : 'fsock_get_contents';
    -            return $this->$method($url, $timeout, $redirect_max, $ua, $fp);
    +            $method = (function_exists('curl_exec')) ? 'curl_get_contents' : 'fsock_get_contents';
    +            return $this->$method($url, $timeout, $redirect_max, $ua, $fp, $info);
             }
             return false;
         }
    @@ -2619,8 +2675,11 @@ protected function get_remote_contents(&$url, $timeout = 30, $redirect_max = 5,
          * @retval false  error
          * @author Naoki Sawada
          **/
    -    protected function curl_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp)
    +    protected function curl_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp, $info)
         {
    +        if ($redirect_max == 0) {
    +            return false;
    +        }
             $ch = curl_init();
             curl_setopt($ch, CURLOPT_URL, $url);
             curl_setopt($ch, CURLOPT_HEADER, false);
    @@ -2633,11 +2692,19 @@ protected function curl_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp
             curl_setopt($ch, CURLOPT_LOW_SPEED_LIMIT, 1);
             curl_setopt($ch, CURLOPT_LOW_SPEED_TIME, $timeout);
             curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    -        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    -        curl_setopt($ch, CURLOPT_MAXREDIRS, $redirect_max);
    +        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
             curl_setopt($ch, CURLOPT_USERAGENT, $ua);
    +        curl_setopt($ch, CURLOPT_RESOLVE, [$info['host'] . ':' . $info['port'] . ':' . $info['ip']]);
             $result = curl_exec($ch);
    -        $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
    +        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    +        if ($http_code == 301 || $http_code == 302) {
    +            $new_url = curl_getinfo($ch, CURLINFO_REDIRECT_URL);
    +            $info = $this->validate_address($new_url);
    +            if ($info === false) {
    +                return false;
    +            }
    +            return $this->curl_get_contents($new_url, $timeout, $redirect_max - 1, $ua, $outfp, $info);
    +        }
             curl_close($ch);
             return $outfp ? $outfp : $result;
         }
    @@ -2658,7 +2725,7 @@ protected function curl_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp
          * @throws elFinderAbortException
          * @author Naoki Sawada
          */
    -    protected function fsock_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp)
    +    protected function fsock_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp, $info)
         {
             $connect_timeout = 3;
             $connect_try = 3;
    @@ -2669,22 +2736,15 @@ protected function fsock_get_contents(&$url, $timeout, $redirect_max, $ua, $outf
             $getSize = null;
             $headers = '';
     
    -        $arr = parse_url($url);
    -        if (!$arr) {
    -            // Bad request
    -            return false;
    -        }
    +        $arr = $info;
             if ($arr['scheme'] === 'https') {
                 $ssl = 'ssl://';
             }
     
             // query
             $arr['query'] = isset($arr['query']) ? '?' . $arr['query'] : '';
    -        // port
    -        $port = isset($arr['port']) ? $arr['port'] : '';
    -        $arr['port'] = $port ? $port : ($ssl ? 443 : 80);
     
    -        $url_base = $arr['scheme'] . '://' . $arr['host'] . ($port ? (':' . $port) : '');
    +        $url_base = $arr['scheme'] . '://' . $info['host'] . ':' . $info['port'];
             $url_path = isset($arr['path']) ? $arr['path'] : '/';
             $uri = $url_path . $arr['query'];
     
    @@ -2765,7 +2825,11 @@ protected function fsock_get_contents(&$url, $timeout, $redirect_max, $ua, $outf
                             sleep(1);
                         }
                         fclose($fp);
    -                    return $this->fsock_get_contents($url, $timeout, $redirect_max, $ua, $outfp);
    +                    $info = $this->validate_address($url);
    +                    if ($info === false) {
    +                        return false;
    +                    }
    +                    return $this->fsock_get_contents($url, $timeout, $redirect_max, $ua, $outfp, $info);
                     }
                     break;
                 case 200:
    @@ -3831,7 +3895,8 @@ protected function archive($args)
             $targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
             $name = isset($args['name']) ? $args['name'] : '';
     
    -        if (($volume = $this->volume($targets[0])) == false) {
    +        $targets = array_filter($targets, array($this, 'volume'));
    +        if (!$targets || ($volume = $this->volume($targets[0])) === false) {
                 return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
             }
     
    @@ -4339,7 +4404,7 @@ protected function itemLocked($hash)
             if (!elFinder::$commonTempPath) {
                 return false;
             }
    -        $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock';
    +        $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . self::filenameDecontaminate($hash) . '.lock';
             if (file_exists($lock)) {
                 if (filemtime($lock) + $this->itemLockExpire < time()) {
                     unlink($lock);
    @@ -4368,7 +4433,7 @@ protected function itemLock($hashes, $autoUnlock = true)
                 $hashes = array($hashes);
             }
             foreach ($hashes as $hash) {
    -            $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock';
    +            $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . self::filenameDecontaminate($hash) . '.lock';
                 if ($this->itemLocked($hash)) {
                     $cnt = file_get_contents($lock) + 1;
                 } else {
    @@ -4519,6 +4584,16 @@ public static function getApiFullVersion()
             return (string)self::$ApiVersion . '.' . (string)self::$ApiRevision;
         }
     
    +    /**
    +     * Return self::$commonTempPath
    +     *
    +     * @return     string  The common temporary path.
    +     */
    +    public static function getCommonTempPath()
    +    {
    +        return self::$commonTempPath;
    +    }
    +
         /**
          * Return Is Animation Gif
          *
    @@ -5104,6 +5179,24 @@ public static function expandMemoryForGD($imgInfos)
             }
         }
     
    +    /**
    +     * Decontaminate of filename
    +     *
    +     * @param      String  $name   The name
    +     *
    +     * @return     String  Decontaminated filename
    +     */
    +    public static function filenameDecontaminate($name)
    +    {
    +        // Directory traversal defense
    +        if (DIRECTORY_SEPARATOR === '\\') {
    +            $name = str_replace('\\', '/', $name);
    +        }
    +        $parts = explode('/', trim($name, '/'));
    +        $name = array_pop($parts); 
    +        return $name;
    +    }
    +
         /**
          * Execute shell command
          *
    @@ -5279,4 +5372,4 @@ class elFinderAbortException extends Exception
     
     class elFinderTriggerException extends Exception
     {
    -}
    \ No newline at end of file
    +}
    
  • php/elFinderVolumeDriver.class.php+13 7 modified
    @@ -5451,7 +5451,7 @@ protected function remove($path, $force = false)
             $stat = $this->stat($path);
     
             if (empty($stat)) {
    -            return $this->setError(elFinder::ERROR_RM, $path, elFinder::ERROR_FILE_NOT_FOUND);
    +            return $this->setError(elFinder::ERROR_RM, $this->relpathCE($path), elFinder::ERROR_FILE_NOT_FOUND);
             }
     
             $stat['realpath'] = $path;
    @@ -6727,7 +6727,7 @@ protected function getArchivers($use_cache = true)
                 unset($o);
                 $this->procExec(ELFINDER_RAR_PATH, $o, $c);
                 if ($c == 0 || $c == 7) {
    -                $arcs['create']['application/x-rar'] = array('cmd' => ELFINDER_RAR_PATH, 'argc' => 'a -inul' . (defined('ELFINDER_RAR_MA4') && ELFINDER_RAR_MA4? ' -ma4' : ''), 'ext' => 'rar');
    +                $arcs['create']['application/x-rar'] = array('cmd' => ELFINDER_RAR_PATH, 'argc' => 'a -inul' . (defined('ELFINDER_RAR_MA4') && ELFINDER_RAR_MA4? ' -ma4' : '') . ' --', 'ext' => 'rar');
                 }
                 unset($o);
                 $this->procExec(ELFINDER_UNRAR_PATH, $o, $c);
    @@ -6737,17 +6737,17 @@ protected function getArchivers($use_cache = true)
                 unset($o);
                 $this->procExec(ELFINDER_7Z_PATH, $o, $c);
                 if ($c == 0) {
    -                $arcs['create']['application/x-7z-compressed'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a', 'ext' => '7z');
    +                $arcs['create']['application/x-7z-compressed'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a --', 'ext' => '7z');
                     $arcs['extract']['application/x-7z-compressed'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -y', 'ext' => '7z', 'toSpec' => '-o', 'getsize' => array('argc' => 'l', 'regex' => '/^.+(?:\r\n|\n|\r)[^\r\n0-9]+([0-9]+)[^\r\n]+$/s', 'replace' => '$1'));
     
                     if (empty($arcs['create']['application/zip'])) {
    -                    $arcs['create']['application/zip'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -tzip', 'ext' => 'zip');
    +                    $arcs['create']['application/zip'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -tzip --', 'ext' => 'zip');
                     }
                     if (empty($arcs['extract']['application/zip'])) {
                         $arcs['extract']['application/zip'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -tzip -y', 'ext' => 'zip', 'toSpec' => '-o', 'getsize' => array('argc' => 'l', 'regex' => '/^.+(?:\r\n|\n|\r)[^\r\n0-9]+([0-9]+)[^\r\n]+$/s', 'replace' => '$1'));
                     }
                     if (empty($arcs['create']['application/x-tar'])) {
    -                    $arcs['create']['application/x-tar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -ttar', 'ext' => 'tar');
    +                    $arcs['create']['application/x-tar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -ttar --', 'ext' => 'tar');
                     }
                     if (empty($arcs['extract']['application/x-tar'])) {
                         $arcs['extract']['application/x-tar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -ttar -y', 'ext' => 'tar', 'toSpec' => '-o', 'getsize' => array('argc' => 'l', 'regex' => '/^.+(?:\r\n|\n|\r)[^\r\n0-9]+([0-9]+)[^\r\n]+$/s', 'replace' => '$1'));
    @@ -6872,8 +6872,14 @@ protected function makeArchive($dir, $files, $name, $arc)
                         $files[$i] = '.' . DIRECTORY_SEPARATOR . basename($file);
                     }
                     $files = array_map('escapeshellarg', $files);
    -
    -                $cmd = $arc['cmd'] . ' ' . $arc['argc'] . ' ' . escapeshellarg($name) . ' ' . implode(' ', $files);
    +                $prefix = $switch = '';
    +                // The zip command accepts the "-" at the beginning of the file name as a command switch,
    +                // and can't use '--' before archive name, so add "./" to name for security reasons.
    +                if ($arc['ext'] === 'zip' && strpos($arc['argc'], '-tzip') === false) {
    +                    $prefix = './';
    +                    $switch = '-- ';
    +                }
    +                $cmd = $arc['cmd'] . ' ' . $arc['argc'] . ' ' . $prefix . escapeshellarg($name) . ' ' . $switch . implode(' ', $files);
                     $err_out = '';
                     $this->procExec($cmd, $o, $c, $err_out, $dir);
                     chdir($cwd);
    
  • php/elFinderVolumeLocalFileSystem.class.php+24 5 modified
    @@ -75,7 +75,6 @@ public function __construct()
             $this->options['alias'] = '';              // alias to replace root dir name
             $this->options['dirMode'] = 0755;            // new dirs mode
             $this->options['fileMode'] = 0644;            // new files mode
    -        $this->options['quarantine'] = '.quarantine'; // quarantine folder name - required to check archive (must be hidden)
             $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
             $this->options['followSymLinks'] = true;
             $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
    @@ -172,14 +171,20 @@ protected function configure()
                 }
             }
             // check quarantine path
    +        $_quarantine = '';
             if (!empty($this->options['quarantine'])) {
                 if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
    -                $hiddens['quarantine'] = $this->options['quarantine'];
    -                $this->options['quarantine'] = $this->_abspath($this->options['quarantine']);
    +                //$hiddens['quarantine'] = $this->options['quarantine'];
    +                //$this->options['quarantine'] = $this->_abspath($this->options['quarantine']);
    +                $_quarantine = $this->_abspath($this->options['quarantine']);
    +                $this->options['quarantine'] = '';
                 } else {
                     $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
                 }
    +        } else {
    +            $_quarantine = $this->_abspath('.quarantine');
             }
    +        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);
     
             parent::configure();
     
    @@ -223,6 +228,8 @@ protected function configure()
                         unset($hiddens['quarantine']);
                     }
                 }
    +        } else if ($_path = elFinder::getCommonTempPath()) {
    +            $this->quarantine = $_path;
             }
     
             if (!$this->quarantine) {
    @@ -346,7 +353,20 @@ protected function _basename($path)
          **/
         protected function _joinPath($dir, $name)
         {
    -        return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
    +        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
    +        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
    +        // realpath() returns FALSE if the file does not exist
    +        if ($path === false || strpos($path, $this->root) !== 0) {
    +            if (DIRECTORY_SEPARATOR !== '/') {
    +                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
    +            }
    +            // Directory traversal measures
    +            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
    +                $name = basename($name);
    +            }
    +            $path = $dir . DIRECTORY_SEPARATOR . $name;
    +        }
    +        return $path; 
         }
     
         /**
    @@ -1439,4 +1459,3 @@ protected function localFileSystemSymlink($target, $link)
             return $res;
         }
     } // END class 
    -
    
  • php/elFinderVolumeSFTPphpseclib.class.php+5 5 modified
    @@ -581,7 +581,7 @@ protected function _fclose($fp, $path = '')
          **/
         protected function _mkdir($path, $name)
         {
    -        $path = $this->_joinPath($path, $name);
    +        $path = $this->_joinPath($path, $this->_basename($name));
             if ($this->connect->mkdir($path) === false) {
                 return false;
             }
    @@ -601,7 +601,7 @@ protected function _mkdir($path, $name)
          **/
         protected function _mkfile($path, $name)
         {
    -        $path = $this->_joinPath($path, $name);
    +        $path = $this->_joinPath($path, $this->_basename($name));
             return $this->connect->put($path, '') ? $path : false;
     /*
             if ($this->tmp) {
    @@ -630,7 +630,7 @@ protected function _copy($source, $targetDir, $name)
         {
             $res = false;
     
    -        $target = $this->_joinPath($targetDir, $name);
    +        $target = $this->_joinPath($targetDir, $this->_basename($name));
             if ($this->tmp) {
                 $local = $this->getTempFile();
     
    @@ -661,7 +661,7 @@ protected function _copy($source, $targetDir, $name)
          */
         protected function _move($source, $targetDir, $name)
         {
    -        $target = $this->_joinPath($targetDir, $name);
    +        $target = $this->_joinPath($targetDir, $this->_basename($name));
             return $this->connect->rename($source, $target) ? $target : false;
         }
     
    @@ -706,7 +706,7 @@ protected function _rmdir($path)
         protected function _save($fp, $dir, $name, $stat)
         {
             //TODO optionally encrypt $fp before uploading if mime is not already encrypted type
    -        $path = $this->_joinPath($dir, $name);
    +        $path = $this->_joinPath($dir, $this->_basename($name));
             return $this->connect->put($path, $fp)
                 ? $path
                 : false;
    

Vulnerability mechanics

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

References

8

News mentions

0

No linked articles in our index yet.