Coordinated Disclosure Timeline
- 2022-01-07: Report sent to security at apache.org.
- 2022-01-09: Receipt acknowledged.
- 2022-01-26: Fixed in v3.6.2.
- 2022-02-08: Security advisory published. CVE-2022-22931
Summary
Partial path traversal allows to break out of expected folder and access another user’s mailbox.
Product
Apache James Server
Tested Version
Linux
Details
Issue 1: Path traversal in MaildirStore.java (GHSL-2022-002
)
validateWithinFolder
validates if this.rootFolder
path starts with the expected maildirRoot
. getCanonicalPath
transforms the path into a canonical form preventing such attack types as ..
in path segments. It also gets rid of the terminating slash symbol in case of directory path.
public MaildirFolder validateWithinFolder(File maildirRoot) throws MailboxNotFoundException {
try {
if (!rootFolder.getCanonicalPath().startsWith(maildirRoot.getCanonicalPath())) {
throw new MailboxNotFoundException(rootFolder.getCanonicalPath() + " jail breaks out of " + maildirRoot.getCanonicalPath());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
If the result of maildirRoot.getCanonicalPath()
is not slash terminated it allows for partial path traversal.
Consider "/var/store/maildir/domain/admin".startsWith("/var/store/maildir/domain/adm")
. The check is bypassed although it is not the adm
directory.
The terminating slash may be removed in various places. On Linux println(new File("/var/"))
returns /var
, but println(new File("/var", "/"))
- /var/
, however println(new File("/var", "/").getCanonicalPath())
- /var
.
The validateWithinFolder
function is used for example in loadMailbox where it is checked if the mailboxPath
is under expected user mail directory, which is typically in the following form ../var/store/maildir/%domain/%user
.
MaildirFolder folder = new MaildirFolder(getFolderName(mailboxPath), mailboxPath, locker)
.validateWithinFolder(getMaildirRoot())
.validateWithinFolder(new File(userRoot(session.getUser())));
Impact
The partial path traversal allows the user named a
to load mailboxes of all users whose name starts from a
.
Issue 2: Path traversal in SieveFileRepository.java (GHSL-2022-003
)
private void enforceRoot(File file) throws StorageException {
try {
if (!file.getCanonicalPath().startsWith(root.getCanonicalPath())) {
throw new StorageException(new IllegalStateException("Path traversal attempted"));
}
} catch (IOException e) {
throw new StorageException(e);
}
}
enforceRoot
similarly to validateWithinFolder
doesn’t take into account that root
may not be terminated with the slash even though it is hardcoded with it:
private static final String SIEVE_ROOT = FileSystem.FILE_PROTOCOL + "sieve/"
Impact
The partial path traversal allows allow one user to read scripts of the other.
CVE
- CVE-2022-22931
Credit
These issues were discovered and reported by GHSL team member @JarLob (Jaroslav Lobačevski).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2022-002
or GHSL-2022-003
in any communication regarding these issues.