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