While performing a penetration test for a customer, I stumbled across a command execution vulnerability in Usermin that is pretty trivial to identify and to exploit. The interesting part is that this vulnerability survived for almost 13 years.
Introduction
According to the Usermin Homepage:
Usermin is a web-based interface for webmail, password changing, mail filters, fetchmail and much more. It is designed for use by regular non-root users on a Unix system, and limits them to tasks that they would be able to perform if logged in via SSH or at the console.
Therefore, Usermin can be seen as a web alternative for interactive machine access as a specific system user. But often times in real environments, Usermin is used to limit the user's rights to perform specific actions via the web, for example as a webmailer only. In this case, arbitrary command execution is definitely not a desired feature.
Nonetheless, enter CVE-2015-2079, which affects Usermin versions 0.980 (dating back to 2002-12-16) until 1.650 (latest unpatched version of 2015-02-16) by exploiting a specific behavior of Perl's open()
function.
Summary
An authenticated user of Usermin can specify the path to an arbitrary file on the server that should be attached to any new email as a signature via the Signature file configuration in the Other file option.
This is due to the function get_signature
in usermin/mailbox/mailbox-lib.pl, which calls open()
without any prior validation:
For what it's worth, that alone poses a vulnerability. But according to an old bug report dating back to 2005, this is not a bug but a feature:
This is not really a bug, as normal Unix file permissions still apply, so really critical files like /etc/shadow cannot be used as a signature. Also, the feature for attaching server-side files could be used in the same way
Besides that, due to some specific behavior of the Perl function used to open the user specified file, it is possible to provide and execute shell commands.
Vulnerability Details
Perl's open()
function can not just open regular files. If it gets called with just two arguments (i. e., open FILEHANDLE,EXPR
), the second argument allows to specify additional behavior via prefixes and suffixes.
For example, the open mode can be specified with the prefix <
for reading, >
for writing to, or >>
for appending to the file. And with the prefix or suffix |
it is possible to start interprocess communication:
Perl's basic open() statement can also be used for unidirectional interprocess communication by either appending or prepending a pipe symbol to the second argument to open().
Since Usermin's call to open()
uses the two arguments form, we can provide a shell command enclosed in pipes as the Signature file to execute the provided command whenever the user composes a new email and output is shown within the message text window or when the user edits the signature.
Example request as proof-of-concept with a sig_file_free
parameter of value |uname -a|
which gets sent to /uconfig_save.cgi by a POST request to usermin like this:
Now that the config file of the subsequent user is changed (see sig_file in ~/.usermin/mailbox/config), a GET request to /mailbox/reply_mail.cgi "includes" the evaluation of uname -a
in the response in the textarea:
Similarily, the code can be triggered via a GET request to /mailbox/edit_sig.cgi:
There is also a handy proof-of-concept metasploit module for linux for your convenience.
The Fix
The Usermin developers fixed this vulnerability in version 1.660. Although the Usermin changelog does not mention it, the diff between the Usermin versions 1.650 and 1.660 shows that they chose to use the Webmin's custom open_readfile()
function instead of Perl's built-in open()
function, which basically prepends the given file path with <
to open the file in read mode.
However, reading arbitrary files on the server is still possible.
Timeline
- 2015-02-23: Identification of vulnerability
- 2015-02-24: Details sent to Usermin developer
- 2015-02-24: Acknowledge from Usermin developer, will be fixed in next version
- 2015-02-25: Assignment of CVE-2015-2079 via cve-assign@mitre.org
- 2015-03-16: Request for status update from Usermin developer
- 2015-03-16: Answer: fixed version "Probably a couple of weeks" away
- 2015-05-08: Request for status update from Usermin developer
- 2015-05-08: Answer: fixed version aimed for next week
- 2015-05-13: Patched Usermin version 1.660 released (without mentioning the bug)
- 2015-05-20: Full disclosure 7 days after patch release