A vulnerability exists in Webmin <= 1.680 (CVE-2014-2952) that allows authenticated users to delete arbitrary files on the host server as root. The problem exists in the cron module, specifically in creating a new environment variable (System > Scheduled Cron Jobs > Create a new environment variable), in the “user” parameter. Here’s the normal request to create an environment variable:

Using directory traversal and null byte injection techniques we’re able to force webmin to delete any file on the filesystem. Modifying our request a bit:

..and the response:

We get an error, but this request also immediately deletes /etc/passwd on the server. Why? Let’s take a look at the relevant code in /usr/share/webmin/cron/save_env.cgi:

We get a list of files in the cron directory, then call lock_file() on each file. Note that the filename is constructed from the value of the user parameter then passed unsanitized to lock_file(). The purpose of lock_file(), defined in web-lib-funcs.pl, is to create a file of the form /path/to/filename.lock to indicate the file is in use. We can see the lock file being created on line 5202 of /usr/share/webmin/web-lib-funcs.pl:

Looking further down lock_file():

On line 5231 it’s storing the filename in locked_file_list, and on 5232 it gets added to temporary_files as well. These data structures come into play when webmin throws the error we saw earlier. Taking a look at the end of the error() function in web-lib-funcs.pl, we see:

First let’s take a look at unlock_all_files():

It iterates through locked_file_list calling unlock_file() on each filename. Now on to unlock_file():

Line 5283 calls unlink() on the filename with “.lock” appended, to delete the lock file. However since we injected a null byte at the end of the filename, everything after the %00 gets ignored. This means the real file, ../../../../etc/passwd, gets passed to unlink() and deleted.

The damage is already done but even if the unlock_all_files() call were not present cleanup_tempnames() would do the same job. Let’s take a look:

Again we’re iterating through the list of files, in this case temporary_files, and calling unlink on the file. Even though the filenames stored in temporary_files were added with “.lock” appended, our null byte knocked that off and stored ../../../../etc/passwd instead, which of course causes /etc/passwd to be deleted.

Timeline:

2014-05-14: UT ISO reports the vulnerability to Webmin developer Jamie Cameron as well as CERT. CERT reserves CVE-2014-2952 for the vulnerability.

2014-05-14: Jamie patches Webmin on github.

2014-05-20: Webmin 1.690 is released with the fix.

2014-09-09: UT ISO publishes this writeup.

– jgor