PHP 5.3.0 "multipart/form-data" denial of service

2009.11.27
Credit: Bogdan Calin
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-Other


CVSS Base Score: 5/10
Impact Subscore: 2.9/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: None
Integrity impact: None
Availability impact: Partial

Description ------------ PHP version 5.3.1 was just released. This release contains a patch for a denial of service condition we've reported on 27 October 2009. The problem is related with PHP's handling of RFC 1867 (Form-based File Upload in HTML). When you send a POST request to a PHP script with the content-type of "multipart/form-data" and include a list of files in that request, PHP will create a temporary file for each file from the request. PHP will create those files regardless if the script can handle file uploading or not. After the script was executed, the temporary files will be deleted. The problem is that you can include a very large number of files in the request. PHP will need to create those files before the script is executed and delete them afterwards. The denial of service condition appears when you create a bunch of requests, each containing a large number (15000+) of files. When you send these requests to the web server, the web server collapses and stops responding because it has to process (create & delete) an insane number of files in a very short period of time. Any website that runs PHP and where file uploading is enabled (which is the default configuration) is vulnerable. You don't need to have a file upload script. PHP does include 2 configuration settings that are related to this situation: upload_max_filesize and post_max_size. However, these are not enough to protect us against this denial of service attack. Workarounds ------------ Currently, I'm aware of three workarounds for this problem: 1. Disable file uploads If you don't need file uploading, you can disable this feature from php.ini. file_uploads = Off 2. Install PHP 5.3.1 If you cannot disable file uploading on your website, it's recommended to install the latest version of PHP. PHP 5.3.1 includes a patch for this problem: - Added "max_file_uploads" INI directive, which can be set to limit the number of file uploads per-request to 20 by default, to prevent possible DOS via temporary file exhaustion. 3. Install Suhosin PHP extension The Suhosin PHP extension has an option named "suhosin.upload.max_uploads". This option defines the maximum number of files that may be uploaded with one request and by default is set to 25. Suhosin PHP extension should not be confused with the Suhosin Patch which does not protect against this attack. Quote from the hardened-php website: "Suhosin comes in two independent parts, that can be used separately or in combination. The first part is a small patch against the PHP core, that implements a few low-level protections against bufferoverflows or format string vulnerabilities and the second part is a powerful PHP extension that implements all the other protections." It's recommended to apply one of the workarounds described above as soon as possible. Below are some conclusions I've gathered from testing this on different systems. Conclusions and real life results ---------------------------------- This attack can make the web server unresponsive in a short period of time (under 2 minutes) with a very small number of requests. Also, this attack doesn't leave any obvious tracks in the logs (only a bunch of POST requests) and can be executed through a proxy server. Some operating systems will handle this condition very badly. For example in one case (a FreeBSD 7.1), the network stack completely crashed and the server was unreachable from the local network. I had to manually restart it from the console. On Linux (Ubuntu), the web server will not be reachable for hours after being attacked for 1-2 minutes. Real life results: 1. PHP on Linux (Ubuntu 8.10) ============================= PHP Version 5.2.6-2ubuntu4.3 Timeline: 14:50 - started the attack 14:51 : web server is no longer responsive. load average: 102.02, 30.68, 10.68 14:52 : web server is not responsive. load average: 129.95, 49.29, 18.05 14:52 - attack is aborted 14:53 - web server is not responsive. load average: 143.58, 67.90, 26.41 14:54 - web server is not responsive. load average: 149.60, 89.58, 37.93 16:05 - web server is not responsive. load average: 151.64, 120.91, 60.94 I wanted to check how many temporary files were created: $ls -la /tmp/php* | wc -l -bash: /bin/ls: Argument list too long 0 I've created a script to count the files: $php count_files_from_dir.php /tmp/php* 2.419.649 So, one hour later, the web server is not responsive and there are 2.419.649 temporary files. If you restart the web server, these files are not deleted. 2. PHP on FreeBSD 7.2 ====================== PHP Version 5.2.9 Timeline: 14:00 - attack is started. 14:01 - web server is no longer responsive (Chrome message: Error 101 (net::ERR_CONNECTION_RESET): Unknown error.)) load average: 87:22, 22.61, 9.9 14:02 - attack is aborted. 14:06 - web server is no longer responsive. load averages: 45.42, 42.35, 22.59 14:11 - web server is not responsive. load averages: 26.77, 35.78, 23.49 The system is slowed down to a crawl. Basically you cannot even write a command in a remote PUTTY session. 14:17 - web server is not responsive. The console is continuously displaying kernel error messages like: swap_pager_getswapspace(2): failed swap_pager_getswapspace(16): failed swap_pager_getswapspace(3): failed ... pid 61248 (httpd), uid 80 inumber 5 on /var: out of inodes pid 61251 (httpd), uid 80 inumber 5 on /var: out of inodes pid 61146 (httpd), uid 80 inumber 5 on /var: out of inodes pid 61103 (httpd), uid 80 inumber 5 on /var: out of inodes pid 61103 (httpd), uid 80 inumber 5 on /var: out of inodes pid 61063 (httpd), uid 80 inumber 5 on /var: out of inodes pid 61101 (httpd), uid 80 inumber 5 on /var: out of inodes ... 14:23 - web server is responsive. load averages: 0.79, 29.10, 37.13 I was trying to count the number of temporary files from the server: $ls -la /var/tmp/php* | wc -l -bash: /bin/ls: Argument list too long 0 $ls -la /var/tmp/php Display all 117490 possibilities? (y or n) So, there are 117490 temporary files left on the server. One another FreeBSD 7.1 server I've had a very weird situation: After one minute the network stack crashed and the server was unreachable from the local network. I had to manually restart the network interface from the console. The message log contains error messages like: Oct 23 10:55:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. Oct 23 10:56:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. Oct 23 10:57:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. Oct 23 10:58:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. Oct 23 10:59:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. Oct 23 11:00:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. Oct 23 11:01:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. Oct 23 11:02:17 daemon kernel: Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable. 3. PHP on Windows: XAMPP ========================= XAMPP for Windows setup filename: xampp-win32-1.7.2.exe PHP Version 5.3.0 Timeline: 12:30 - started the attack 12:30 + few seconds: CPU usage => 100% In a few seconds, the web server is not responding anymore, 65535 temporary files are created and no more files could be created anymore. On XAMPP for Windows, PHP is creating the temporary files in C:\xampp\tmp (if your XAMPP installation was in C:\xampp\) The files are named phpXXXX.tmp (where X's charset is 'a'-'z', 'A'-'Z', '0'-'9'). Example: php1A00.tmp This 4 char random number is a limitation of PHP on Windows. PHP on Unix is using 6 chars for its temporary filenames so it doesn't reach this condition. 12:31 - attack is aborted 12:39 - CPU usage is still 100%, web server is not responsive. 13:08 - CPU usage is still 100%, web server is responsive. 14:08 - CPU usage is 97% 14:34 - CPU usage is 97% Two hours later the CPU usage didn't get back to normal. However, the web server is responding. After I manually restart the Apache process, CPU usage gets back to normal. However, those 65535 temporary files were not deleted. 4. PHP on OpenBSD 4.6 ====================== PHP Version 5.2.10 Timeline: 12:00 - started the attack 12:00 + few seconds: CPU usage => 100% 12:01 - attack is aborted 12:01 - web server is no longer responsive. load averages: 120.42, 50.35, 20.59 12:04 - web server is no longer responsive. load averages: 147.17, 80.74, 36.46 The system is slowed down to a crawl. 12:06 - web server is responsive. load averages: 122.59, 96.03, 48.31 So, at this point the web server is working again but the system is still slowed down to a crawl. But it can serve web pages. 12:07 - web server is responsive. load averages: 63.67, 85.01, 47.26 12:10 - web server is responsive. load averages: 6.56, 52.75, 40.03 12:12 - web server is responsive. load averages: 0.55, 16.36, 26.50 The system is back to normal. OpenBSD recovered very well from this attack, the effect only lasted for a few minutes. Why is that? Because of the Suhosin PHP extension. OpenBSD has this extension enabled by default. LFI2RCE -------- In some cases, this attack can be used to convert a local file inclusion exploit to remote code execution. Most operating systems don't delete the temporary files created by this attack even after you restart the web server. Therefore, a large number of temporary files are left in the temporary directory (usually /tmp for Unix systems). You can try to guess the name of one of these filenames and include it. For this to work, all the uploaded files should contain some PHP script like: <?php eval($_REQUEST[x]); ?>. On Windows systems there are only 4 characters used for generating temporary files (phpXXXX.tmp). After the web server is responsive again, there are 65.535 temporary files in the temporary directory. Therefore, it's possible to guess the name of one of those files and include it. On Unix, 6 characters are used for the temporary filenames and therefore it's almost impossible to guess the name of the temporary filename. Or at least, it could take a very long time. As a funny note, I managed to exploit this on a web server with 800.000 temporary files. After randomly guessing for 5 minutes I managed to guess the name of one of the temp files and execute PHP code. Proof of concept ----------------- I'm not going to publish the proof of concept Python script. If you have a valid reason why you would need the proof of concept, you can contact me at this email address (bogdan [at] acunetix.com). -- Bogdan Calin - bogdan () acunetix com CTO Acunetix Ltd. - http://www.acunetix.com Acunetix Web Security Blog - http://www.acunetix.com/blog

References:

http://www.php.net/releases/5_3_1.php
http://www.php.net/ChangeLog-5.php
http://www.openwall.com/lists/oss-security/2009/11/20/2
http://www.openwall.com/lists/oss-security/2009/11/20/7
http://seclists.org/fulldisclosure/2009/Nov/228
http://news.php.net/php.announce/79


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top