Translate: 
EnglishFrenchGermanItalianPolishPortugueseRussianSpanish

Uwierzytelnianie NTLM i Single Sign On: czyli jak w PHP zalogować się do ActiveDirectory bez znajomości loginu i hasła

Przeglądając gotowe biblioteki PHP do uwierzytelniania w domenie ActiveDirectory zauważyłem, że żadne z tych rozwiązań nie pozwala na zalogowanie użytkownika domenowego bez konieczności podania przez niego loginu i hasła.

Funkcjonalność automatycznego logowania do domeny wbudowana jest oczywiście tylko w w przeglądarce Internet Explorer, ale powiedzmy sobie szczerze: w dużych firmach i korporacjach jest to nadal jedyna słuszna aplikacja do surfowania po Int(ra|er)necie.

Co ciekawe, w innych językach programowania (Java, C#) nie ma najmniejszego problemu, by użytkownik domenowy mógł wejść poprzez Internet Explorer do chronionego serwisu, istnieje tam wiele gotowych rozwiązań operujących na uwierzytelnianiu NTLM.

Po dwóch dniach spędzonych na czytaniu dokumentacji Microsoftu, Samby, oraz po sniffingu sieciowym, udało mi się zaimplementować działające rozwiązanie.

W niniejszym artykule postaram się przybliżyć schemat automatycznego logowania skryptów PHP do domeny ActiveDirectory za pomocą… ataku Man-in-the-middle.

Trochę teorii

Oficjalnie dostęp do zasobów WWW w domenie ActiveDirectory możliwy jest na dwa różne sposoby: poprzez uwierzytelnianie NTLM w przypadku aplikacji Internet Explorer, oraz Basic Authentication u pozostałych przeglądarek. Z moich obserwacji wynika jednak, że większość przeglądarek potrafi logować się metodą NTLM, lecz w przeciwieństwie do IE nie robią tego automatycznie bez informowania o tym użytkownika.

W tym artykule skupię się na mechanizmie zastosowanym w przeglądarkach z rodziny Internet Explorer. Te jako jedyne logują się do domeny przy pomocy loginu i hasła pobranego bezpośrednio z systemu operacyjnego Windows, bez konieczności ręcznego wpisywania tych danych przez użytkownika. Mechanizm ten jest na tyle transparentny, że użytkownik końcowy nie wie nawet, kiedy znajduje się na zabezpieczonej poprzez NTLM stronie WWW.

Jak każde rozwiązanie, tak i to ma swoje plusy i minusy. Niewątpliwym plusem jest łatwość poruszania się po firmowym intranecie bez konieczności ciągłego wpisywania swoich danych logowania. Wadą zaś jest to, że przeglądarka IE może niechcący wysłać dane użytkownika do „podstawionego”, wrogiego hosta w sieci wewnętrznej.

Jak działa NTLM?

Uwierzytelnianie NTLM jest kilkustopniowe i składa się z następujących operacji:

  1. Nawiązanie połączenia HTTP serwer <-> klient (ważne: w trybie keep-alive)
  2. Przeglądarka IE prosi o zasób HTTP, przedstawiając przy tym swoją wersję w nagłówku User-Agent
  3. Serwer WWW rozpoznaje przeglądarkę IE i informuje ją, że do obejrzenia zasobu potrzebne jest uwierzytelnianie NTLM lub Basic
  4. IE wysyła do serwera komunikat inicjujący uwierzytelnianie oraz negocjujący właściwości połączenia (wiadomość typu 1)
  5. Serwer odsyła wiadomość w której znajduje się challenge code oraz wynegocjowane opcje połączenia (wiadomość typu 2)
  6. IE odsyła ostatnią wiadomość uwierzytelniającą, zawiera ona w sobie dane logowania, takie jak login, nazwę stacji roboczej, nazwę domeny oraz skrót hasła wygenerowanego przy pomocy challenge code otrzymanego w poprzedniej wiadomości (jest to wiadomość typu 3)
  7. Po zweryfikowaniu danych serwer zwraca zawartość zasobu HTTP

W procesie uwierzytelniania ważne jest to, by pamiętać o podtrzymywaniu połączenia HTTP z serwerem ActiveDirectory przy pomocy mechanizmu keep-alive. Zerwanie połączenia przed wykonaniem wszystkich powyższych kroków lub wysyłanie kolejnych wiadomości w ramach oddzielnych requestów HTTP skończy koniecznością powtórzenia wszystkich etapów logowania.

Ważną informacją jest też to, że ani serwer WWW, ani przeglądarka klienta, nie sprawdzają w żaden sposób, czy nie padły ofiarą ataku Man-in-the-middle.

To właśnie powyższa cecha NTLM pozwoli nam na zalogowanie się do domeny ActiveDirectory, bez konieczności poznania loginu i hasła.

Jak to wykorzystać?

Do przeprowadzenia w PHP uwierzytelniania metodą Man-in-the-middle potrzebujemy trzech podstawowych rzeczy:

  1. komputera pracującego w sieci lokalnej w domenie ActiveDirectory (np. typowa stacja robocza)
  2. nazwę hosta dla komputera z pkt. 1, która musi składać się wyłącznie z liter alfanumerycznych (z ewentualnymi podkreślnikami), np: WORKSTATION12.
  3. adres serwera używającego uwierzytelniania NTLM (najprościej, gdy będzie to serwer WWW, np. strony intranetowej)

Łatwo się domyśleć, że skrypt PHP zostanie uruchomiony na komputerze z punktu 1. Ograniczenia w jego nazwie wynikają z zabezpieczeń związanych ze strefą intranetową: Uwierzytelnianie Single sign on dostępne jest tylko dla hostów z sieci lokalnej, nie posiadających w nazwie znaków innych niż alfanumeryczne.

W celu przeprowadzenia pomyślnego uwierzytelniania, strona PHP musi zostać odwiedzona przez komputer z przeglądarką IE, znajdujący się w tej samej domenie ActiveDirectory.

Pewnym utrudnieniem może być fakt, że uwierzytelnianie jest kilkustopniowe. Typowy skrypt PHP nie potrafi utrzymać stałego połączenia HTTP z serwerem domenowym w trakcie obsługi kilku, niezależnych od siebie zapytań przeglądarki. Po każdym takim zapytaniu skrypt zostaje przerwany, a uruchomienie kolejnego wymagałoby powtórzenia wszystkich etapów logowania do domeny.

Problem ten jednak można obejść w dosyć prosty sposób: skrypt PHP obsługujący zapytanie przeglądarki IE nie powinien łączyć się bezpośrednio z serwerem domenowym, musi do tego celu wykorzystać inny proces PHP (nazwijmy go TaskRobot), z którym może się komunikować poprzez bazę danych lub inny mechanizm (apc/memcached).

Głównym zadaniem takiego procesu będzie utrzymywanie stałego połączenia z serwerem i pośredniczenie w komunikacji ze skryptami obsługującymi przeglądarkę klienta.

Jak to działa w praktyce?

Znając już całą teorię napisanie gotowego skryptu nie powinno nastręczać trudności. Wystarczy, że skrypt PHP będzie służył jako pośrednik i przekazywał wiadomości uwierzytelniające pomiędzy serwerem a przeglądarką klienta. W ten sposób badając obustronną komunikację łatwo uzyskujemy takie dane jak login, domenę, oraz nazwę stacji roboczej klienta (pochodzą one z wiadomości nr. 3). Dodatkowo utrzymując stałe połączenie z serwerem domenowym, skrypt PHP może wykonywać na nim operacje w imieniu zalogowanego użytkownika.

Poniżej demonstruję screeny z mojego frameworka używającego logowania do domeny (kliknij aby powiększyć):

Przydatne struktury danych

Poniżej przedstawiam zbiór kilku funkcji PHP, pomocnych w odszyfrowaniu odpowiedzi serwera WWW oraz przeglądarki Internet Explorer:

Wymuszenie procesu uwierzytelniania NTLM na przeglądarce:

header('HTTP/1.0 401 Unauthorized');        
header('WWW-Authenticate: Negotiate');
header('WWW-Authenticate: NTLM');
header('Connection: keep-alive');

Weryfikacja odpowiedzi przeglądarki (wiadomość nr. 1):

$ntlmBase = trim($_SERVER['HTTP_AUTHORIZATION']);
if(!preg_match('|^NTLM [a-zA-Z0-9+=/]+$|', $ntlmBase)) {
      throw new Exception('Unknown NTLM schema');
}
 
$ntlmMessage = base64_decode(substr($ntlmBase, 5));
if(substr($ntlmMessage, 0, 8) != "NTLMSSP\0") {
      throw new Exception('Unknown NTLM schema');
}
 
$struct = unpack('A8protocol/C1type', $ntlmMessage);
$ntlmType = $struct['type'];
 
if(!($ntlmType > 0 && $ntlmType < 4)) {
      throw new Exception('Unknown NTLM schema');
}

Odczyt danych o zalogowanym użytkowniku (wiadomość nr. 3):

$struct = unpack('A8protocol/C1type/C3zero/v2lmResponseLength/v1lmResponseOffset/C2zero/v2ntResponseLength/v1ntResponseOffset/C2zero/v2domainLength/v1domainOffset/C2zero/v2loginLength/v1loginOffset/C2zero/v2hostLength/v1hostOffset/C6zero/v1msgLength/C2zero/v1flags/C2zero', $ntlmMessage);
$login = iconv('UTF-16LE', 'UTF-8', substr($ntlmMessage, $struct['loginOffset'], $struct['loginLength1']));
$domain = iconv('UTF-16LE', 'UTF-8', substr($ntlmMessage, $struct['domainOffset'], $struct['domainLength1']));
$host = iconv('UTF-16LE', 'UTF-8', substr($ntlmMessage, $struct['hostOffset'], $struct['hostLength1']));

Podsumowanie

Logowanie typu Single sign on przy pomocy PHP ma dwa skrajnie różne zastosowania: łatwe postawienie serwisu WWW na platformie LAMP w ramach domeny ActiveDirectory, lub przeprowadzenie dosyć nietypowego ataku Man-in-the-middle.

Główny wniosek wysunięty z niniejszego artykułu powinien być zaś jeden: nie używać Internet Explorera do automatycznego logowania w obrębie domeny firmowej.

Potencjalny napastnik może wykorzystać mechanizm NTLM do próby złamania samego hasła użytkownika. Choć hasło jest hashowane przy pomocy stringu challenge code, to przy dostatecznie wielu próbkach danych istnieje ryzyko jego odkrycia (spreparowana strona WWW może uwierzytelniać przeglądarkę wielokrotnie, np. dla każdego obrazka oddzielnie).

Tagi:

Dodaj odpowiedź