When mime-type validation isn’t enough
Recently a client’s machine had been accessed through some holes in his application. We were given access to the source code and started to figure out how the hacker was able to get in and execute code to elevate his privileges, post financial transactions and reset accounts.
The first place we looked were places where images could be uploaded to the system as that is usually a very easy place to upload code. The file uploader checked for the presence of .gif/.jpg/.jpeg and checked the mime type, but, the check merely made sure that .jpg was contained within the filename, not that it was anchored to the right hand side. Looking through a number of directories where files could be written and be web accessible, we had a few possible locations to focus our efforts. Two sections of code were focused on and we came up with the following code:
00000000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 48 |......JFIF.....H| 00000010 00 48 00 00 ff db 00 43 00 01 01 01 01 01 01 01 |.H.....C........| 00000020 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 |................| 00000030 01 01 01 01 01 01 01 01 01 01 01 01 01 02 02 01 |................| 00000040 01 02 01 01 01 02 02 02 02 02 02 02 02 02 01 02 |................| 00000050 02 02 02 02 02 02 02 02 02 ff db 00 43 01 01 01 |............C...| 00000060 01 01 01 01 01 01 01 01 02 01 01 01 02 02 02 02 |................| 00000070 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 |................| * 00000090 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ff c2 |................| 000000a0 00 11 08 02 15 02 58 03 01 22 00 02 11 01 03 11 |......X.."......| 000000b0 01 ff c4 00 1e 00 00 00 06 03 01 01 00 00 00 00 |................| 000000c0 00 00 00 00 00 00 04 05 06 07 08 09 00 02 03 0a |................| 000000d0 3c 3f 70 68 70 20 65 63 68 6f 20 22 74 65 73 74 |< ?php echo "test| 000000e0 22 3b 3f 3e 0a |";?>.| 000000e5
You can recreate the exploit:
head -n 1 somefile.jpg > file.jpg.php echo '< ?php echo "hello";?>' >> file.jpg.php
The file was named file.jpg.php, uploaded through the application, the file was then written to the avatars directory and was web accessible. Since the file contained .jpg and had a proper jpeg header, it passed the two validation tests. The payload contained with the file shows ‘junk’ before the word test is printed.
A number of factors made this attack vector possible. A client could upload content that contained filenames that could be executed by .php/.cgi if they contained .gif/.jpg/.jpeg. In addition, the avatar directory (and one other) allowed execution of scripts. Using filesmatch or removing the mimetypes for anything but the static images allowed would have prevented the files from being executed.
In reality, the hole that was used was even easier to exploit as the application allowed preview of a work unit where the url wasn’t sanitized properly allowing XSS, however, this method could have been utilized.
Every time you deal with user supplied content, check, double-check and triple-check the server configuration, directory permissions, ability to traverse directories, etc. Ideally, making sure your server has minimal abilities in those directories is a step in the right direction.
Tags: upload validation