An application’s interaction with the file system is always highly security sensitive since minor functional bugs can easily be the source of exploitable vulnerabilities. This observation is especially true in the case of web file managers, whose role is to replicate the features of a complete file system and expose it to the client’s browser in a transparent way.
elFinder is a popular web file manager often used in CMS and frameworks, such as WordPress plugins (wp-file-manager) or Symfony bundles, to allow easy operations on both local and remote files. In the past, elFinder has been part of active in-the-wild attacks targeting unsafe configuration or actual code vulnerabilities. Thus, elFinder is published with a safe default configuration to prevent any malicious use by attackers.
As part of our regular assessment of widely deployed open-source projects, we discovered multiple new code vulnerabilities in elFinder. In the following case study of common code vulnerabilities in web file managers, we describe five different vulnerability chains and demonstrate how they could be exploited to gain control of the underlying server and its data. We will also discuss some of the patches that were later implemented by the vendor to show how to prevent them in your own code.
We worked on the development branch, commit f9c906d. Findings were also confirmed on release 2.1.57; all affect the default configuration (unless specified otherwise in this article) and do not require prior authentication. As we mentioned, the exploitation of these vulnerabilities can let an attacker execute arbitrary PHP code on the server where elFinder is installed, ultimately leading to its compromise.
The findings we discuss in this blog post (all assigned to CVE-2021-32682) and successfully exploited to gain code execution are:
- Deleting Arbitrary Files
- Moving Arbitrary Files
- Uploading PHP Files
- Argument Injection
- Race Condition
All these bug classes are very common in software that exposes filesystems to users, and are likely to impact a broad range of products, not only elFinder.
elFinder released version 2.1.59 to address all the bugs we responsibly disclosed. There is no doubt these vulnerabilities will also be exploited in the wild, because exploits targeting old versions have been publicly released and the connectors filenames are part of compilations of paths to look for when trying to compromise websites. Hence, we highly recommend that all users immediately upgrade elFinder to the latest version.
To give a better understanding of the code snippets we will use to demonstrate our findings, we will first describe how elFinder’s routing works. Like in many modern PHP applications, the connector (e.g.
connector.minimal.php) is the only entry point. It declares configuration directives and closures and then instantiates both
elFinder (the core) and
elFinderConnector (the interface between
elFinder and the transport channel, here HTTP).
elFinder::$commands contains every valid action and the expected arguments:
The user can call any of these commands by providing the
cmd parameter with the required command parameter via
POST. In each command handler, parameters are accessed using
To allow remote filesystems (FTP, Dropbox, etc.) to be used with local ones, elFinder implements a filesystem abstraction layer (
elFinderVolumeDriver) on top of which all drivers are built. Files are then referenced by their volume name (e.g.
t1_ is the trash,
l1_ the default local volume) and the URL-safe Base64 of their name.
Let’s first dig into an arbitrary file deletion bug chain, composed of two distinct issues.
The PHP core does not provide an effective way to run background threads, or perform synchronization and inter-process communication. elFinder tries to balance this by heavily using temporary files and post-request hooks. For instance, users can
abort ongoing actions by calling the method of the same name:
Here, a code vulnerability is present at
: a user-controlled parameter is concatenated into a full path without prior checks. For
, it can end up creating an empty file with a fully controllable name, and in
 it can be used to remove an arbitrary file. SonarCloud issues for both bugs are available:  and .
There is a catch: the filename resulting from
 will be prefixed by
elfreq. In a path traversal attack, POSIX systems will fail path resolution if any predecessor in the path does not exist or is not a directory. For instance, resolving
/tmp/i_am_a_file/../ will respectively fail with
ENOTDIR. This prerequisite makes the exploitation of these two vulnerabilities impossible as-is, and will require another bug, such as the ability to create an arbitrary directory.
An attacker could then look into the command
mkdir and discover a primitive that allows this exact behaviour. Here is its top-level handler, before it goes through the filesystem abstraction layer:
A generic implementation is present in
elFinderVolumeDriver to handle both the volume and path that should be created. It will call the volume-specific implementation at
 with the volume absolute path on the filesystem as the first parameter and the target name as the second parameter:
It is defined as follows:
elFinderVolumeLocalFileSystem::_joinPath() is doing a mere concatenation of the two values, leading to a path traversal vulnerability. This gives a primitive to create arbitrary, empty folders on the local filesystem. While not being a vulnerability in itself, it will allow the exploitation of the aforementioned behaviour.
It is also worth noting the presence of a full path disclosure in the
rm command, disclosing the absolute path of a given file on the local filesystem:
The impact of this vulnerability is quite dependent on the environment: it could be chained with other elFinder bugs, used to trigger interesting behaviors in other applications (e.g. remove WordPress’ wp-config.php file to gain code execution) or used to affect existing security measures (e.g. removing
This vulnerability has been fixed by improving the implementation of
elFinderVolumeLocalFileSystem::_joinPath() to assert that the final path won’t be outside of the base one. Several calls to
basename() across the codebase were also added as a hardening measure.
elFinderVolumeLocalFileSystem::_joinPath() method is used in other actions, such as
rename: it combines a volume base directory and a user-provided destination name. It is thus vulnerable to the bug we just described.
The following snippet is the actual implementation of
elFinderVolumeLocalFileSystem::rename(), after executing all the code responsible for decoding the paths and ensuring that the destination extension is allowed:
While the destination extension is still strictly limited by MIME checks, this primitive can be enough for an unauthenticated attacker to gain command execution on the server, depending on the environment, by overriding files like
composer.json, etc. This bug has been fixed with the same patch as the previous bug we discussed.
As for most PHP applications, the biggest threat faced by elFinder is that an attacker could be able to upload PHP scripts to the server, since nothing (except quite a hardened web server configuration) would prevent them from accessing it directly to execute its contents. The maintainers initially tried to defend against that by crafting a block-list that associated dangerous MIME types to the relevant extensions:
In our test environment (Apache HTTP 2.4.46-1ubuntu1 on Ubuntu 20.10), the default configuration declares that
.phar files should be treated as
application/x-httpd-php () and be interpreted:
This configuration was also observed on Debian’s stable release. While another pass of MIME type detection is performed on the contents of the file, this can be easily circumvented as the PHP interpreter allows statements anywhere in the interpreted files (e.g.
<?php can be placed after some dummy data).
The fix is straightforward: it declares that
.phar files are associated with the MIME
text/x-php, which are disallowed by default.
Among the default features that make elFinder so powerful, users can select multiple files and archive them using external tools such as
7z. This functionality is exposed under the action named
Note that users can create archives even if their upload is forbidden, by calling the
archive command on existing files. The implementation is specific to the virtual filesystem in use. We will focus solely on the default one, since it is inherited by
elFinderVolumeLocalFileSystem which crafts the full command line (
) and executes it with the default shell (
Here, the value of
$name comes from the user-controlled parameter
$_GET['name']. While properly escaped with
escapeshellarg() to prevent the use of command substitution sequences, the program will try to parse this value as a flag (
--foo=bar) and then as a positional argument. It is also worth noting that the user's value is suffixed with
.zip in the case in which the ZIP archiver is selected.
zip implements an integrity test feature (
-T) that can be used along with
-TT to specify the test command to run. In the present case, it gives the attacker a way to execute arbitrary commands using this parameter injection.
To be able to exploit this vulnerability, the attacker needs to create a dummy file (e.g.
a.txt), archive it to create
a.zip and then invoke the
archive action with both the original file and the archive as targets, using a name like
The resulting command line will be
zip -r9 -q '-TmTT="$(id>out.txt)foooo".zip' './a.zip' './a.txt', thus executing
id and logging its standard output into
out.txt — this file will be available with the other documents in elFinder’s interface.
When it came time to fix this bug,
zip wasn't very friendly. The usual method based on POSIX’s
-- (see our previous article about a parameter injection in Composer for an in-depth explanation) can’t be applied here, since
zip will exit with the following error:
The maintainers then decided to prefix the archive name with ./ to prevent any risk of parameter injection. They also decided to harden the calls to the other archivers (
rar, etc.) in the same patch.
Let’s have a look at our last finding of this case study. While this vulnerability in the quarantine feature cannot be exploited in the default configuration since archives can’t be uploaded; the feature could have been responsible for future security issues because of its design.
The rationale behind the quarantine is that archives may contain unwanted files (mostly PHP scripts) that should not be extracted in the current folder without first running security checks (e.g. with MIME validation). So instead, elFinder chose to extract archives into a folder named
.quarantine, placed under the
files/ folder, and
elFinderVolumeLocalFileSystem::_extract() generates a random directory name for each archive extraction (at
This can be confirmed dynamically thanks to
strace or the
inotify suite, for instance here with an archive containing a PHP file:
This trace can be understood as:
- A folder named
- A file named
win.phpis created within
- Data is written into
If the server is configured to list directories, this behavior can easily be exploited, since dangerous files (e.g.
.php) can be accessed right before the MIME validation step and their removal. The race condition window is however too small to think of an attack involving brute force if the random directory name can’t be found that way.
An attacker could discover that the
duplicate action can be used on the internal folders, like
.quarantine, and copy any file regardless of its contents. While being a harmless functional bug on its own, it can be chained with the quarantine feature to duplicate the folder containing our extracted archive just before its deletion. The duplicated folder is then visible in the interface, and allows an attacker to get around the random name to access the malicious script, ultimately granting arbitrary code execution.
As a fix, the maintainers decided to move the
.quarantine folder outside of files/. The
elFinderVolumeLocalFileSystem abstraction layer is not aware of anything outside of this folder, preventing any unintended action on
|2021-03-22||These 5 issues are reported to maintainers|
|2021-06-10||The maintainers acknowledge all our findings|
|2021-06-13||elFinder 2.1.59 is released, fixing the bugs we reported|
|2021-06-13||CVE-2021-32682 and CVE-2021-23394 are assigned|
In this case study we looked at critical code vulnerabilities that are commonly found in web file managers. We presented several of our real-world findings in the latest version of elFinder available at the time, including their potential impact and how they were fixed by the vendor. It allowed us to demonstrate that innocuous bugs can often be combined to gain arbitrary code execution. We believe it is important to document and report these vulnerabilities to break future bug chains and reduce the risk of similar issues.
We also learned that working with paths is not easy and that extra measures should be taken: performing additional checks in the “low-level” functions, using
dirname() with confidence (and knowing their limits!) and always validating user-controlled data. Such bugs are very common in web file managers, and you should always have such bugs in mind when working with them.
While we don’t plan to release any exploits for these bugs, we would still like to bring your attention to the fact that arbitrary code execution was easily demonstrated and attackers won’t have much trouble replicating it. We urge you to immediately upgrade to elFinder 2.1.59. We also advise enforcing strong access control on the connector (e.g. basic access authentication).
Finally, we would like to thank the maintainers of elFinder for acknowledging our advisory and fixing these vulnerabilities in a timely and professional manner.