1. Uploading Malicious File Contents
1.1. Problem
You want to test how your application handles files with malicious
content. The content might be malicious because of its size, because it
is not the required type, or because it actually causes the application
to crash when it is processed.
1.2. Solution
See Example 1.
Example 1. Uploading a file through Perl
#!/usr/bin/perl use LWP::UserAgent; use HTTP::Request::Common qw(POST);
$UA = LWP::UserAgent->new(); $page = "http://www.example.com/upload.jsp";
$req = HTTP::Request::Common::POST( "$page", Content_Type => 'form-data', Content => [ myFile => [ 'C:\TEMP\myfile.pdf', "AttackFile.pdf", "Content-Type" => "application/pdf" ], Submit => 'Upload File', ] );
$resp = $UA->request($req);
|
1.3. Description
The code from Example 1
does the minimum possible work to upload a file named C:\TEMP\myfile.pdf (that lives on your local
hard disk) and put it at the URL shown in the $page variable. It is clear from Example 1 that there are several
opportunities for malicious attack.
The first obvious thing to try when testing for security this way is
to provide contents of files that will cause difficulties at the server.
If the requirements for your application say that files must be smaller
than 100 kilobytes, your typical boundary-case testing would involve
uploading 105 kilobyte files, 99 kilobyte files, and probably 0 byte
files. You should also upload some extremely large files, too. A badly
designed application might keep unacceptable files in some temporary location, even after it has sent a message to the user saying “file too
large.” This means you could crash the application by filling its
temporary storage, even though the files appear to be ignored.
From a security point of view, good tests will send files whose
contents are not what they appear. Imagine a web application that
unpacks uploaded ZIP files, for example. You could take a file like a
spreadsheet or an executable, rename it to end in .zip, and then upload it. This would surely
cause a failure of some kind in your application.
Some file formats have old, well-known attacks. For ZIP files
there are attacks called “zip bombs” or “zip of death” attacks where a correctly formatted ZIP
file that is very small (for example, 42 kilobytes) would expand to over
4 gigabytes if fully unzipped. You can find an example file by searching
on Google for “zip of death.”
Other data formats have similar possible bugs. It is possible to
craft various image files that contain size information indicating that
they are one size (e.g., 6 megabytes) but actually only contain a
fraction of that data—or much more than that data.
2. Uploading Files with Malicious Names
2.1. Problem
The file uploading standard (RFC 1867) allows a user to send the
file’s name along with the file’s content. Applications must be very
careful when accepting a file’s name, since that could easily be a
source of malicious input.
2.2. Solution
See Example 2.
Example 2. Sending many different illegal filenames using Perl
#!/usr/bin/perl use LWP::UserAgent; use HTTP::Request::Common qw(POST);
$UA = LWP::UserAgent->new(); $page = "http://www.example.com/upload.aspx";
# this file is 255 A's, follwed by .txt $file259chars = "A" x 255 . ".txt"; @IllegalFiles = ( "a:b.txt", # Colon not allowed on most OSes "a;b.txt", # Semicolon deprecated on most OSes # > 64 characters doesn't work on older file systems "123456789012345678901234567890123456789012345678900123456.txt", "File.", # Windows may discard final period "CON", # Reserved name in Windows "a/b.txt", # does this create a file named b.txt? "a\\b.txt", # again, what does this do? "a&b.txt", # ampersand can be interpreted by OS "a\%b.txt", # percent is variable marker in Windows $file259chars );
foreach $fileName (@IllegalFiles) { $req = HTTP::Request::Common::POST( "$page", Content_Type => 'form-data', Content => [ myFile => [ 'C:\TEMP\TESTFILE.TXT', $fileName, "Content-Type" => "image/jpeg" ], Submit => 'Upload File', ] );
$resp = $UA->request($req); }
|
2.3. Description
Perl is the best way to perform this kind of test for several
reasons. We can indicate the file’s name programmatically and use
filenames that our own operating system would never allow. It’s not
possible to get a web browser to perform most of these tests, because
you cannot name files with these names and then ask your web browser to
upload them. You could intercept file-upload requests using WebScarab or
TamperData and then manually change the filename, but that is tedious
and time-consuming and does not yield any better results.
The tests shown in Example 2 use filenames that
should be illegal because of the operating system’s constraints. For
example, slash and backslash are not allowed in filenames in most
operating systems. This will cause the application to crash when trying
to store the file. Again, typical testing should cover many of these
cases. Variations on these test cases, however, might create security
vulnerabilities.
There are more significant failures than simply failing to create
a file with the given name. Some characters, like ampersand and
semicolon, might be allowed in a filename (e.g., Unix operating systems
allow semicolon), but then they can be leveraged into a command
injection attack later. Imagine that an attacker can store a file named
test.txt;ping home.example.com. This
filename is acceptable in Unix. If the application, however, uses that
filename later in an unsafe way (in a shell script, command, Perl
script, or other program), it might be interpreted as a command. The hacker can upload her file,
and then watch her network to see if she receives a ping from the victim
server. If she does, she knows the filenames are handled unsafely. Her
next file upload could execute a more malicious command. Search “command
injection attack pattern” on Google for more information on this
attack.
To take this even farther, consider putting cross-site scripting attack strings and SQL injection
attack strings into filenames. If the file’s name is displayed in a web
page or becomes part of a SQL query, the attacker may be able to use
file upload as a means of cross-site scripting or SQL injection.