Podczas testów korzystałem z wersji ściągniętej z
http://playboard.eu/threads/master-server-boost-webstats.141/
Kilka przykładów stron korzystających z tego skryptu
http://playboard.eu/boost/
http://plsetti.pl/boost/
W pliku cron.php skrypt pobiera informacje o serwerach przy pomocy
$data = $newServer->serverInfo($row['address']);
Sama metoda nie jest szczególnie interesująca , jedyne co nas interesuje to to że informacje czyli hostname i mapa są zwracane bez żadnej filtracji.
static function serverInfo($server) {
list($ip,$port) = explode(":", $server);
$fp = @fsockopen('udp://'.$ip, $port);
if($fp) {
stream_set_timeout($fp, 2);
fwrite($fp,"\xFF\xFF\xFF\xFFTSource Engine Query\0\r");
$temp = fread($fp, 4);
$status = socket_get_status($fp);
if($status['unread_bytes']>0) {
$temp = fread($fp, $status['unread_bytes']);
$version = ord(self::getChar($temp));
$array = array();
$array['status'] = "1";
if($version == 109) {
$array['ip'] = self::getString($temp);
$temp = substr($temp, 1);
$array['hostname'] = self::getString($temp);
$temp = substr($temp, 1);
$array['mapname'] = self::getString($temp);
$temp = substr($temp, 1);
self::getString($temp);
$temp = substr($temp, 1);
self::getString($temp);
$temp = substr($temp, 1);
$array['players'] = ord(self::getChar($temp));
$array['maxplayers'] = ord(self::getChar($temp));
} elseif($version == 73) {
self::getChar($temp);
$array['hostname'] = self::getString($temp);
$temp = substr($temp, 1);
$array['mapname'] = self::getString($temp);
$temp = substr($temp, 1);
self::getString($temp);
$temp = substr($temp, 1);
self::getString($temp);
$temp = substr($temp, 3);
$array['players'] = ord(self::getChar($temp));
$array['maxplayers'] = ord(self::getChar($temp));
}
} else {
$array['hostname'] = 'Brak nazwy serwera';
$array['mapname'] = '-';
$array['players'] = '0';
$array['maxplayers'] = '0';
$array['status'] = '0';
}
}
return $array;
}
W kolejnej linii wrzucamy dane do mysql
mysql_query("UPDATE `servers` SET `hostname` = '".mysql_real_escape_string($data['hostname'])."', `players` = '".mysql_real_escape_string($data['players'])."', `maxplayers` = '".mysql_real_escape_string($data['maxplayers'])."', `map` = '".mysql_real_escape_string($data['mapname'])."', `status` = '".intval($data['status'])."' WHERE `id` = {$row['id']}");
dane są filtrowane poprzez mysql_real_escape_string wiec nie możemy używać znaków \x00, \n, \r, \, ’, ” i \x1a.
Przy wyświetlaniu dane ponownie nie są filtrowane.
echo '
'.$num.'
'.$row['hostname'].'
'.$row['address'].'
'.$row['map'].'
'.$newMain->showbar($row['players'], $row['maxplayers']).'
'.$thisdate.'
'.$row['rounds'].'';
Dzięki temu wszystkiemu jeśli w nazwie serwera lub w nazwie mapy wrzucimy odpowiedni kod zostanie on zinterpretowany przez przeglądarke i wykonany.
np.
pomimo pominięcia ” w nazwie pliku ( tego znaku nie możemy używać ) plik zostanie poprawnie wczytany a kod w nim wykonany
Co dalej ?
Warto przyjrzeć się jak jest sprawdzane logowanie do panelu admina
if(isset($_COOKIE["adminhash"]) AND $_COOKIE["adminhash"] != $auth[2] OR !isset($_COOKIE["adminhash"])) {
require_once "include/admin/admin_auth.php";
}
całość opiera się na pliku cookies z hashem.
Ustawianie cookies odbywa się bardzo prosto
if(!empty($_POST['login']) AND !empty($_POST['password'])) {
if($_POST['login'] == $auth[0] AND md5($_POST['password']) == $auth[1]) {
setcookie("adminhash", $auth[2], time()+2592000, "/");
header("Location: admin.php");
} else {
$showmsg = $newMess->into_msg("2", "Nieprawidłowy login lub hasło!", "2");
}
}
widać tutaj że w cookie adminhash ustawiamy wartość hashu sprawdzaną podczas logowania się do panelu.
W kodzie wyżej jest jeden błąd pozwalający na wykradnięcie hashu i wykorzystanie go do zalogowania się do panelu admina bez znajomości hasła czy loginu.
Jak te błędy poprawić ?
W sumie jest to bardzo proste. Aby załatać 1 błąd wystarczy podczas wyświetlania danych użyć htmlentities
np.
echo '
'.$num.'
'.htmlentities( $row['hostname'] ).'
'.$row['address'].'
'.htmlentities( $row['map'] ).'
'.$newMain->showbar($row['players'], $row['maxplayers']).'
'.$thisdate.'
'.$row['rounds'].'';
drugi błąd polega na tym że na cookies nie jest ustawiana flaga httponly dzięki czemu javascript ma do nich dostęp
wystarczy zamiast
setcookie("adminhash", $auth[2], time()+2592000, "/");
użyć
setcookie("adminhash", $auth[2], time()+2592000, "/", null, null, true );
http://serwery.csx-scripts.pl/
Odpada, inny skrypt, logowanie do panelu admina jest możliwe tylko z 1 IP
A co z cssetti.pl, csadv.pl, cs-boost.pl?
I jak sprawdzić czy dana strona ma ten „bug”?
Podobno playboard twierdzi że tego buga nie ma i zamawiane przez nich instalacje są na nowym skrypcie ma ktoś do niego download?
Viva usunąłem link.
Gdzie tak twierdzi 😉 ?
csadv.pl, cs-boost.pl korzystają z tego skryptu cssetti.pl nie
Właściciel plsetti.pl/boost i bodajże csadv twierdzą że nie ma już tego bugu, mógłbyś sprawdzić lub pokazać mniej więcej jak zobaczyć czy to działa?
To od samego początku nie działało 🙂 przynajmniej u mnie.
Naturalne jest że po opublikowaniu tego instalka została poprawiona wiadomo też że niektóre serwisy były zabezpieczone wcześniej
jest możliwość przerobienia skryptu master boost pod cs:go ?
Tak oczywiście
a byś był miły i przerobił go ?
Możesz dać cały ten plik z naprawiony bugiem ?
Jet opisane na końcu jak to poprawić 😉