Skip to content

v4.1.2 - security release

Latest

Choose a tag to compare

@misterunknown misterunknown released this 20 Aug 08:54
· 13 commits to master since this release

Security release

This release fixes four path-traversal issues in the IFM API. All of them
escape the root_dir sandbox, and the shipped default configuration has
"auth" => 0, so on a default install they are reachable without
authentication
. Upgrading is strongly recommended.

Thanks to @de3erve for reporting the first two, with clear write-ups and
working proofs of concept.

Fixed

1. Arbitrary file read via getContentGHSA-7jm7-c928-f7f3 (high)

getContent validated the requested filename with isFilenameAllowed(), which
only rejects dotfiles, .ht* and self-overwrite and never checks for path
separators. The value was passed straight to file_get_contents() without being
joined to root_dir, so an absolute path such as /etc/passwd was read and
returned verbatim. Requires edit => 1 (the default).

getContent now uses isFilenameValid(), and file_exists() was tightened to
is_file() so directories are no longer accepted as edit targets.

2. Arbitrary file and directory deletion via deleteFilesGHSA-gcr2-fx44-5px4 (critical)

The same validator mix-up in deleteFiles, but reaching unlink() and the
recursive rec_rmdir(). Any file, or any entire directory tree, writable by the
web server process could be destroyed from outside the sandbox. Requires
delete => 1 (the default).

3. Parent-directory deletion via .. (no separate advisory)

Found while fixing #2, and the reason the one-line validator swap suggested in
the advisory is not enough on its own. isFilenameValid() rejected / and
\0, but not the dot segments — and ".." contains neither. It was blocked
only incidentally by isFilenameAllowed()'s leading-dot rule, which is
disabled when showhiddenfiles => 1. With that setting, a bare
filenames[]=.. reached rec_rmdir() and recursively deleted the parent of the
current directory, while confusingly still reporting an error to the caller.

isFilenameValid() now rejects non-strings, "", "." and ".." up front.

4. root_dir prefix bypass in isPathValid() (no separate advisory)

The containment check was 0 === strpos($rpDir, $rpConfig) with no trailing
separator, so a root_dir of /var/www also accepted /var/www-evil. Relevant
to shared-hosting setups with sibling directories sharing a name prefix. The
comparison now appends a trailing DIRECTORY_SEPARATOR.

As a result, isFilenameAllowed() is no longer called by any handler — it is
reachable only through isFilenameValid().

Impact

On a default install, any unauthenticated visitor could read any file readable
by the web server process, and delete any file or directory tree writable by it,
anywhere on the host. In shared-hosting or multi-tenant setups this crosses
tenant boundaries — exactly what root_dir exists to prevent.

Upgrading

Drop-in replacement: replace your ifm.php / cdn.ifm.php with the version
from this release. No configuration changes are required and there are no
breaking API changes.

If you cannot upgrade immediately, setting "edit" => 0 and "delete" => 0
mitigates issues 1–3, and enabling authentication ("auth" => 1) limits all
four to authenticated users.

Verification

Each issue was reproduced against v4.1.1 and re-tested after the fix: all four
succeed before and are rejected afterwards, with no side effect on disk.

Full changelog: v4.1.1...v4.1.2