Atak XSS oraz przejęcie sesji admina – Skrypt Boost Masterservera

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
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$data = $newServer->serverInfo($row['address']);
$data = $newServer->serverInfo($row['address']);
$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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
}
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; }
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
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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']}");
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']}");
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
echo '
'.$num.'
'.$row['hostname'].'
'.$row['address'].'
'.$row['map'].'
'.$newMain->showbar($row['players'], $row['maxplayers']).'
'.$thisdate.'
<center>'.$row['rounds'].'</center>';
echo ' '.$num.' '.$row['hostname'].' '.$row['address'].' '.$row['map'].' '.$newMain->showbar($row['players'], $row['maxplayers']).' '.$thisdate.' <center>'.$row['rounds'].'</center>';
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<script src="file.js"></script>
<script src="file.js"></script>
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
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
if(isset($_COOKIE["adminhash"]) AND $_COOKIE["adminhash"] != $auth[2] OR !isset($_COOKIE["adminhash"])) {
require_once "include/admin/admin_auth.php";
}
if(isset($_COOKIE["adminhash"]) AND $_COOKIE["adminhash"] != $auth[2] OR !isset($_COOKIE["adminhash"])) { require_once "include/admin/admin_auth.php"; }
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
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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");
}
}
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"); } }
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
echo '
'.$num.'
'.htmlentities( $row['hostname'] ).'
'.$row['address'].'
'.htmlentities( $row['map'] ).'
'.$newMain->showbar($row['players'], $row['maxplayers']).'
'.$thisdate.'
<center>'.$row['rounds'].'</center>';
echo ' '.$num.' '.htmlentities( $row['hostname'] ).' '.$row['address'].' '.htmlentities( $row['map'] ).' '.$newMain->showbar($row['players'], $row['maxplayers']).' '.$thisdate.' <center>'.$row['rounds'].'</center>';
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
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
setcookie("adminhash", $auth[2], time()+2592000, "/");
setcookie("adminhash", $auth[2], time()+2592000, "/");
setcookie("adminhash", $auth[2], time()+2592000, "/");
użyć
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
setcookie("adminhash", $auth[2], time()+2592000, "/", null, null, true );
setcookie("adminhash", $auth[2], time()+2592000, "/", null, null, true );
setcookie("adminhash", $auth[2], time()+2592000, "/", null, null, true );

12 komentarzy o “Atak XSS oraz przejęcie sesji admina – Skrypt Boost Masterservera

  1. A co z cssetti.pl, csadv.pl, cs-boost.pl?
    I jak sprawdzić czy dana strona ma ten „bug”?

  2. Podobno playboard twierdzi że tego buga nie ma i zamawiane przez nich instalacje są na nowym skrypcie ma ktoś do niego download?

  3. 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?

  4. Naturalne jest że po opublikowaniu tego instalka została poprawiona wiadomo też że niektóre serwisy były zabezpieczone wcześniej

Dodaj komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.