Issue B. Arbitrary File Write in Input File Uploader

At 2014-05-05 13:06:46 Arturo Filastò wrote: Reported: 2014-04-10

Applies To: ooni-probe

Synopsis:

The ooni-probe web interface has a page for uploading test inputs. The server-side logic that handles file uploads is vulnerable, as it allows the uploaded file to be written anywhere in the filesystem.

Impact:

An attacker can create a new file with arbitrary contents anywhere on the ooni-probe server's filesystem. Usually it's possible to execute code remotely this way, e.g. by writing a script into /etc/cron.daily/.

Preconditions:

The ooni-probe web interface must be exposed to the attacker, or the attacker must be able to fool or force (e.g. by CSRF) a legitimate user into making the file upload request.

Feasibility:

If the attacker has access to the web interface, the attack is very easy to perform and can be automated.

Verification:

This issue was verified by inspecting the code, and also by using the TamperData extension for Firefox to upload a file with the name test/../../../../../../tmp/test.txt which successfully created a /tmp/test.txt file on the ooni-probe server.

When we verified this issue, the CSRF tokens were not working properly, so we had to modify the code to remove the @check_xsrf. If the CSRF token system was working properly, its presence would not have stopped the attack.

Technical Details:

The code that handles the file upload is in ooni-probe/ooni/api/spec.py. It is reproduced below.

    @check_xsrf
    def post(self):
        """
        Add a new input to the currently installed inputs.
        """
        input_file = self.request.files.get("file")[0]
        filename = input_file['filename']

        if not filename or not re.match('(\w.*\.\w.*).*', filename):
            raise InvalidInputFilename

        if os.path.exists(filename):
            raise FilenameExists

        content_type = input_file["content_type"]
        body = input_file["body"]

        fn = os.path.join(config.inputs_directory, filename)
        with open(os.path.abspath(fn), "w") as fp:
            fp.write(body)

The filename is checked against a regular expression. However, the regular expression matches filenames that contain any number of ../ sequences. An attacker can upload a file with these characters in the name, and the file will be written outside of the configured inputs_directory.

This issue was automatically migrated from github issue https://github.com/TheTorProject/ooni-probe/issues/318