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

$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 '<tr><td>'.$num.'</td><td>'.$row['hostname'].'</td><td>'.$row['address'].'</td><td>'.$row['map'].'</td><td>'.$newMain->showbar($row['players'], $row['maxplayers']).'</td><td>'.$thisdate.'</td><td><center>'.$row['rounds'].'</center></td></tr>';

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.

<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

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 '<tr><td>'.$num.'</td><td>'.htmlentities( $row['hostname'] ).'</td><td>'.$row['address'].'</td><td>'.htmlentities( $row['map'] ).'</td><td>'.$newMain->showbar($row['players'], $row['maxplayers']).'</td><td>'.$thisdate.'</td><td><center>'.$row['rounds'].'</center></td></tr>';

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 );

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

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

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

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

Dodaj komentarz