Dawno nie było na blogu trochę bardziej technicznego wpisu poświęconego narzędziom bezpieczeństwa rozwijanym na zasadach open source. Z przyjemnością więc zapraszam do lektury artykułu, z którego dowiecie się, że wdrożenie SIEM-a i dodanie do niego własnej wtyczki parsującej logi to nie takie straszne zadanie jak mogłoby się wydawać na pierwszy rzut oka. Przy okazji poznacie też ciekawe narzędzie do tworzenia i testowania wyrażeń regularnych.
Wazuh – open source SIEM
Wazuh jest pochodną innego open source’owego projektu – OSSEC, który od lat cieszy się popularnością jako rozwiązanie HIDS (Host-based Intrusion Detection System). Wspominałem już o nim przy okazji artykułu o Security Onion, gdyż tam również zaimplementowany został do monitorowania bezpieczeństwa urządzeń końcowych. Security Onion jest jednak kombajnem, w którym próbowano upakować wiele różnych funkcji, m.in. sieciowy IDS – Surricata, Zeek NSM, a nawet system ticketowy do obsługi incydentów, co powoduje, że jest on dość ciężki w zarządzaniu i utrzymaniu. Wdrożony na jednym serwerze, nawet w małym środowisku szybko konsumuje zasoby, a jego skalowanie nie jest banalne.
Sam Wazuh natomiast ewoluował w ostatnich latach do narzędzia, które z powodzeniem możemy wykorzystać do monitorowania całego środowiska, identyfikacji zagrożeń, a nawet działania reaktywnego, co zbliżyło go nie tylko do rozwiązań klasy SIEM, ale również XDR. A co najważniejsze, wszystko to zamknięte w przejrzystym i spójnym interfejsie webowym i stosunkowo proste do wdrożenia.
Instalacją samego Wazuha nie będziemy się w tym artykule zajmować. Zainteresowanych odsyłam do oficjalnej dokumentacji, z której dowiecie się, że instalacja tego systemu to dosłownie kilka prostych kroków. Na jednym z wspieranych Linuksów (Amazon Linux, RHEL, CentOS lub Ubuntu) zajmie wam to może z 10-15 minut.
Kolejny krok wdrożenia to instalacja agentów Wazuha na urządzeniach końcowych i tutaj również z pomocą przychodzi oficjalna instrukcja instalacji. Jedyną wskazówką, której warto tutaj udzielić jest zadbanie o to by z urządzeń końcowych możliwe było połączenie do serwera Wazuh na portach 1514 i 1515 tcp. W domyślnej instalacji Wazuh nie wymaga od agentów uwierzytelniania więc trzeba też uważać by powyższych portów nie udostępnić przypadkowo do Internetu.
Wazuh i analiza logów
W domyślnej konfiguracji Wazuh za pomocą agentów zainstalowanych na urządzeniach końcowych potrafi monitorować m.in.:
- integralność plików systemowych
- znane podatności w zainstalowanym oprogramowaniu
- zgodność konfiguracji ze standardami i benchmarkami takimi jak PCIDSS czy CIS
- zdarzenia systemowe związane z bezpieczeństwem
- obecność rootkitów
- zmiany statusu portów sieciowych
- zajętość zasobów
Ale to wszystko to dopiero początek jego możliwości. Za pomocą agentów do Wazuha możemy przekierować dowolne logi, poddać je analizie i na podstawie zdefiniowanych reguł wywołać w razie potrzeby alerty.
W artykule pt. „WAF, czyli web application firewall na przykładzie ModSecurity” opisywałem jak i dlaczego warto włączyć monitorowanie ruchu wysyłanego w zapytaniach do serwera www. Załóżmy, że posiadamy serwer www z włączonym WAF-em ModSecurity i chcielibyśmy by informacje o zablokowanych połączeniach trafiły do naszego SIEM-a. Wazuh za pomocą wbudowanych dekoderów potrafi z automatu interpretować logi z setek różnych systemów, w tym serwera www Apache. Jedyne co musimy zrobić, to na serwerze webowym w pliku konfiguracyjnym agenta Wazuh wskazać ścieżkę do logów. Robimy to dodając do pliku /var/ossec/etc/ossec.conf poniższą sekcję pomiędzy tagami <ossec_config></ossec_config>:
<localfile>
<log_format>apache</log_format>
<location>/var/log/apache2/error_log</location>
</localfile>
oczywiście przy założeniu, że /var/log/apache2/error_log to miejsce, w którym Apache z modułem ModSecurity loguje odrzucane połączenia. Następnie pozostaje nam jeszcze zrestartować wazuch-agenta na serwerze webowym:
systemctl restart wazuh-agent
I już po paru minutach konsola naszego SIEM-a zacznie logować zdarzenia związane z zablokowanymi żądaniami do serwera www:
Powyżej widzimy informację o odrzuceniu przez WAF (ModSecurity) zapytania ze wskazanego adresu IP (którego geolokalizację przy okazji wskazuje nam Wazuh) z powodu braku nazwy hosta. Najczęściej świadczy to o tym, iż połączenie pochodzi od jakiegoś bota przeczesującego sieć po całych pulach IP, a nie klienta znającego adres domenowy strony. Takich zdarzeń serwery www logują tysiące, więc zasadnym jest, iż nie są one traktowane jako alerty na poziomie High lub Critical skutkujące wysyłką powiadomień. Pozwalają nam jednak uzmysłowić sobie ile różnych złośliwych zapytań i prób ataku przyjmują każdego dnia nasze serwery.
Fail2ban – blokowanie ataków brute force
W dalszej części artykułu pójdziemy krok dalej i stworzymy własny dekoder do logów, których Wazuch nie potrafi interpretować domyślnie. Zrobimy to na przykładzie logów narzędzia fail2ban. Pozwólcie więc, że najpierw przybliżę wam to rozwiązanie.
Fail2ban pozwala na monitorowanie zdarzeń logowania do innych usług sieciowych (np. ssh, http, ftp, smtp). Jeśli z jednego adresu IP następuje w krótkim czasie wiele nieudanych prób logowania, fail2ban dodaje ten adres do listy zbanowanych i automatycznie filtruje pochodzący od niego ruch za pomocą systemowego firewalla (iptables) dodając do niego odpowiednie reguły.
Instrukcja uruchomienia tej usługi na systemach Linux z rodziny Debian/Ubuntu ogranicza się do zainstalowania 2 pakietów:
apt install open-infrastructure-apache-tools fail2ban
z czego ten pierwszy jest nam potrzebny tylko jeśli chcemy monitorować zdarzenia logowania do serwera www na poziomie protokołu http (http-auth). Następnie tworzymy plik konfiguracyjny dla usługi, którą chcemy monitorować. Pozostańmy przy wspomnianym wyżej http i stwórzmy plik: /etc/fail2ban/jail.d/apache-auth.conf
[apache-auth]
enabled = true
filter = apache-auth
port = http,https
logpath = /var/log/apache2/error_log
findtime = 60m
bantime = 24h
maxretry = 10
Logpath to ścieżka do logów serwera Apache. Pozostałe parametry definiują: po ilu błędnych próbach logowania nastąpi ban (maxretry), na jak długo adres zostanie zbanowany (bantime) i w jakim oknie czasowym zliczane będą błędne próby logowania dla danego IP (findtime). W powyższym przykładzie jeśli nastąpi 10 nieudanych prób logowania w ciągu godziny, klient zostanie zbanowany na dobę. Ostatni krok to restart usługi fail2ban:
systemctl restart fail2ban
Po jakimś czasie od uruchomienia usługi możemy sprawdzić jaki jest aktualny status zbanowanych klientów:
> fail2ban-client status apache-auth
Status for the jail: apache-auth
|- Filter
| |- Currently failed: 0
| |- Total failed: 20
| `- File list: /var/log/apache2/error_log
`- Actions
|- Currently banned: 2
|- Total banned: 2
`- Banned IP list: 79.124.8.107 146.190.91.160
Zbanowane powyżej adresy IP powinny się zatem pojawić również w regułach firewalla:
> iptables -L
target prot opt source destination
REJECT all -- 146.190.91.160 anywhere reject-with icmp-port-unreachable
REJECT all -- 79.124.8.107 anywhere reject-with icmp-port-unreachable
Wróćmy jednak do Wazuha. Logi narzędzia fail2ban domyślnie znajdują się w pliku /var/log/fail2ban.log i to jego zawartość będziemy chcieli przekazać do analizy naszemu SIEM-owi.
Własny parser logów do Wazuha
Wspomniane wyżej logi z pliku /var/log/fail2ban.log mają następującą postać:
2024-09-25 15:38:51,178 fail2ban.filter [94458]: INFO [apache-auth] Found 188.166.238.42 - 2024-09-25 15:38:51
2024-09-22 22:07:27,415 fail2ban.actions [94458]: NOTICE [apache-auth] Ban 79.124.8.107
2024-10-01 21:29:46,821 fail2ban.actions [94458]: NOTICE [apache-auth] Unban 79.124.8.107
2024-10-01 21:30:13,294 fail2ban.actions [428]: NOTICE [apache-auth] Restore Ban 79.124.8.107
i nie są one niestety interpretowane przez standardowe dekodery. Aby Wazuh mógł je „zrozumieć” potrzebujemy dwóch rzeczy – dekodera, który wyciągnie z logów interesujące nas parametry (adres IP i związaną z nim akcję: „Ban”, „Unban” lub „Found”) oraz zestawu reguł, które na podstawie odczytanych z tych parametrów wartości zadecydują o wywołaniu odpowiedniego alertu.
Dekoder dla logów fail2ban
Własny dekoder możemy zdefiniować w pliku /var/ossec/etc/decoders/local_decoder.xml na serwerze Wazuch. Na potrzeby parsowania powyższych logów należy do tego pliku dopisać sekcję:
<decoder name="fail2ban">
<prematch type="pcre2">\[\d+\]:\s+(INFO|NOTICE)\s+\[\S+\]\s+(Found|Ban|Unban)</prematch>
</decoder>
<decoder name="fail2ban_actions">
<parent>fail2ban</parent>
<regex>(\S+) (\d+.\d+.\d+.\d+)</regex>
<order>actions,srcip</order>
</decoder>
W tagu <prematch> zawarliśmy wyrażenie regularne zgodne ze standardem pcre2, którego zadaniem jest wyłapać z wszystkich linii logów analizowanych przez Wazuha tylko te pasujące do naszego formatu fail2ban. W każdej z przytoczonych wyżej linii logów znajdował się fragment podobny do tego:
[94458]: NOTICE [apache-auth] Ban 79.124.8.107
Jeśli rozłożymy nasze wyrażenie regularne na części, to zobaczymy, że wyłapuje ono ciągi znaków, w których występują po sobie kolejno:
- \[ otwarty nawias kwadratowy
- \d+ dowolna ilość cyfr
- \]: zamknięty nawias kwadratowy i dwukropek
- \s+ dowolna ilość białych znaków
- (INFO|NOTICE) jedna z dwóch wartości: INFO lub NOTICE
- \s+ dowolna ilość białych znaków
- \[ otwarty nawias kwadratowy
- \S+ dowolny ciąg niebiałych znaków (np. apache-auth)
- \] zamknięty nawias kwadratowy
- \s+ dowolna ilość białych znaków
- (Found|Ban|Unban) jedna z wartości Found, Ban lub Unban
Narzędziem, które bardzo ułatwia tworzenie wyrażeń i natychmiastową ich weryfikację jest serwis https://regex101.com/. Możemy w nim wkleić interesujący nas fragment logów i wpisując wyrażenie regularne zweryfikować, które fragmenty tekstu zostaną w nim ujęte – znacznie skraca to czas eksperymentów:
Powyżej widzimy, że zdefiniowane wyrażenie regularne objęło interesujące nas linie logów informujące o nałożeniu i zdjęciu bana, a jednocześnie wykluczyło linię ze zdarzeniem „Restore Ban”, która pojawia się za każdym razem po restarcie usługi fail2ban i informuje jedynie o przywróceniu wcześniej zapamiętanych banów.
Tutaj jeszcze jedna istotna uwaga – jeśli w dekoderach Wazucha chcemy się posługiwać wyrażeniami regularnymi w standardzie pcre2 (najpopularniejsze, dające największe możliwości, ale jednocześnie wolniejsze w działaniu) musimy to wskazać dodając do tagów z wyrażeniami regularnymi wartość type=”pcre2″. Wazuch wspiera też wyrażenia w standardach OSRegex i OSMatch – więcej o tym w dokumentacji. Wspominam o tym, ponieważ przez długi czas walczyłem z błędami dekodera, który uznawał moje wyrażenia regularne za błędne właśnie przez brak informacji o standardzie pcre2.
Przejdźmy jednak do dalszego procesowania logów.
W kolejnym fragmencie dekodera umieszczonego w pliku /var/ossec/etc/decoders/local_decoder.xml występowały dwie poniższe linie:
<regex>(\S+) (\d+.\d+.\d+.\d+)</regex>
<order>actions,srcip</order>
Pierwsza z nich ma za zadanie z wyłapanych już za pomocą poprzedniego wyrażenia linii logów wyłuskać jedynie wartość zdarzenia (Found, Ban lub Unban) oraz związany z nim adres IP. Zatem przy pomocy kolejnego wyrażenia regularnego w tagu <regex> wyszukujemy następujących po sobie:
- \S+ dowolnego słowa
- spacji
- \d+.\d+.\d+.\d+ czterech ciągów cyfr rozdzielonych kropkami
Następnie wartościom wziętym w nawias (\S+) oraz (\d+.\d+.\d+.\d+) kolejna linia z tagiem <order> przypisuje nazwy parametrów, kolejno: actions i srcip – tymi nazwami będziemy się posługiwać za moment definiując reguły. Najgorsze (zabawę z wyrażeniami regularnymi) mamy już za sobą.
Reguły dla dekodera fail2ban
Ostatnim krokiem koniecznym do zrozumienia naszych logów przez Wazuha jest porównanie wartości wyciągniętych z nich zmiennych actions i srcip ze zdefiniowanymi regułami i podjęcie decyzji o wywołaniu odpowiedniego alertu. Reguły podobnie jak dekodery definiujemy w lokalnym pliku serwera. Tym razem będzie to plik /var/ossec/etc/rules/local_rules.xml. Zdefiniujmy w nim trzy reguły odpowiadające poszczególnym akcjom:
<group name="fail2ban_actions">
<rule id="100002" level="12">
<decoded_as>fail2ban</decoded_as>
<field name="actions">^Ban</field>
<description>Fail2ban - IP banned</description>
</rule>
<rule id="100003" level="7">
<decoded_as>fail2ban</decoded_as>
<field name="actions">Unban</field>
<description>Fail2ban - IP unbanned</description>
</rule>
<rule id="100004" level="6">
<decoded_as>fail2ban</decoded_as>
<field name="actions">Found</field>
<description>Fail2ban - IP logged</description>
</rule>
</group>
Każda definiowana samodzielnie reguła musi posiadać swój identyfikator z przedziału 100000-120000 (pozostałe są zarezerwowane dla reguł systemowych). Tag <decoded_as> informuje, że reguła będzie stosowana do danych pochodzących z konkretnego dekodera – w tym przypadku fail2ban. Następnie na podstawie wartości zmiennej actions każdemu ze zdarzeń (Ban, Unban, Found) za pomocą tagu <description> przypisywany jest odpowiedni opis, który pojawi się w interfejsie Wazuha. Akcja Found oznacza, że dany adres IP pojawił się w logach fail2ban i jest zliczany, ale nie przekroczył jeszcze wartości maxretry powodującej bana.
Zauważyliście, że w regule 100002 pole actions ma wartość „^Ban” zamiast „Ban”? To nie pomyłka, znak „^” oznacza, że ciąg znaków „Ban” ma występować na początku tego pola. Gdyby nie ten znak do pierwszej reguły łapałaby się również akcja „Unban”, w której zawiera się słowo „ban”.
Wartość pola level przy każdej z reguł oznacza natomiast poziom krytyczności, który zostanie przypisany zdarzeniu. Domyślnie Wazuh klasyfikuje poziomy następująco:
- Critical Severity – poziomy od 15 wzwyż
- High Severity – poziomy 12-14
- Medium Severity – poziomy 7-11
- Low Severity – poziomy 0-6
Zdefiniowane reguły przydzielą więc zdarzenie Found do grupy Low, Unban do grupy Medium i Ban do grupy High. Zdarzenia z grup High i Critical powodują wyzwolenie powiadomień za pomocą skonfigurowanych w Wazuchu kanałów (np. email, sms, webhook) więc po jakimś czasie dojdziecie pewnie do wniosku, że zdarzeniu Ban można spokojnie obniżyć poziom z 12 na 11 🙂
Po zdefiniowaniu nowych dekoderów i reguł w plikach local_decoder.xml i local_rules.xml możemy je przetestować za pomocą narzędzia wazuh-logtest dostępnego na serwerze w domyślnej lokalizacji: /var/ossec/bin/wazuh-logtest – wystarczy je wywołać, a następnie wklejać pojedyncze linie logów czekając na ich interpretację:
> /var/ossec/bin/wazuh-logtest
Starting wazuh-logtest v4.9.0
Type one log per line
2024-09-22 22:07:27,415 fail2ban.actions [94458]: NOTICE [apache-auth] Ban 79.124.8.107
**Phase 1: Completed pre-decoding.
full event: '2024-09-22 22:07:27,415 fail2ban.actions [94458]: NOTICE [apache-auth] Ban 79.124.8.107'
timestamp: '2024-09-22 22:07:27,415'
**Phase 2: Completed decoding.
name: 'fail2ban'
actions: 'Ban'
srcip: '79.124.8.107'
**Phase 3: Completed filtering (rules).
id: '100002'
level: '12'
description: 'Fail2ban - IP banned'
groups: '['fail2ban_actions']'
firedtimes: '1'
mail: 'True'
**Alert to be generated.
Wklejona linia logu została w fazie 2 rozpoznana przez dekoder fail2ban, a w fazie 3 na podstawie wartości pola actions zastosowana została reguła 100002 przypisująca zdarzeniu odpowiedni opis i poziom 12 powodujący jednocześnie wyzwolenie alertu.
Wszystko wygląda w porządku więc pozostał nam już tylko restart wazuh-managera żeby wczytać z plików konfiguracyjnych nowe dekodery i reguły:
systemctl restart wazuh-manager
Od tego momentu Wazuh będzie już rozumiał logi w formacie fail2ban. Żeby mu je dostarczyć musimy jeszcze tylko, podobnie jak w przypadku logów apacha na początku artykułu, dodać do pliku konfiguracyjnego wazuh-agenta (/var/ossec/etc/ossec.conf) kolejną sekcję wskazującą lokalizację logów:
<localfile>
<log_format>syslog</log_format>
<location>/var/log/fail2ban.log</location>
</localfile>
i zrestartować wazuh-agenta
systemctl restart wazuh-agent
Dla pewności – zmian w pliku konfiguracyjnym i restartu agenta dokonujemy oczywiście na serwerze, na którym uruchomiona jest usługa fail2ban.
Filtr dla własnego dekodera
Na koniec jeszcze mała podpowiedź – na liście zdarzeń Wazuha (panel Explore -> Discover) możemy wyszukiwać interesujące nas wpisy posługując się nazwą utworzonego przez siebie dekodera. Wystarczy w tym celu zdefiniować odpowiedni filtr:
Można też użyć jako filtru identyfikatora konkretnej reguły. Jeśli np. chcielibyśmy wyszukać tylko zdarzenia dotyczące ustanowienia bana, zdefiniowalibyśmy filtr „Field: rule.id Operator: is Value: 100002„.
Po odfiltrowaniu interesujących naz zdarzeń, w wynikach zaznaczone zostaną poszukiwane wartości (na poniższym obrazku nazwa dekodera)
Do czego może nam się przydać logowanie takich zdarzeń? Zwróćcie uwagę na podkreślone powyżej na czerwono informacje o źródle pochodzenia ataków. Jeśli z naszego serwisu www nie korzystają klienci w Chinach, to może warto takie połączenia w ogóle odfiltrować? Przy dużej skali może to mieć nawet wpływ na koszty utrzymania usługi w chmurze, gdzie często płacimy za ruch wymieniany z naszymi serwerami. To oczywiście tylko jeden z przykładów do czego mogą nam się przydać informacje zawarte w logach.
Mam nadzieję, że tym wpisem zachęciłem Was do zainteresowania się możliwościami systemu Wazuh. Pamiętajcie, że przykład z fail2banem dotyczył niestandardowego formatu logów. W ogromnej większości przypadków Wazuh zrozumie wasze logi bez konieczności zagłębiania się w wyrażenia regularne.
Zostaw e-mail aby otrzymać powiadomienia o nowościach oraz dostęp do wszystkich bonusowych materiałów przygotowanych wyłącznie dla subskrybentów.
Ładnie opisane, dziękuję za artykuł. Wbrew popularnej opinii, fail2ban nie jest tylko od aktywowania regułek firewalla. Po kolejnym powtórzeniu zdarzenia w jakimkolwiek logu czwgokolwiek, może wykonać dowolny kod, opisany w pliku action. To jest o wiele bardziej uniwersalne narzędzie, niż się wydaje.
Można nim też łapać 404 na zapytaniach zawierających gitconfig, phpmyadmin, czy wp-admin, ponieważ są to na pewno skanery podatności.
Dzięki za komentarz. Na pewno jeszcze wrócę do tego tematu, bo jest u mnie teraz na tapecie. Będzie też o wykorzystaniu active response Wazuha i integracji wszystkiego z Zeek-iem. Wkręciłem się ostatnio w monitorowanie 🙂