Threat Inteliigence / OSINT / NETSEC / NATSEC

YARA rules! – o regułach YARA i ich pisaniu

We wpisie dotyczącym wyszukiwania informacji o próbkach malwareu w otwartych źródłach wspomniałem krótko o wykorzystaniu reguł YARA i opisałem podstawy korzystania z nich w kontekście HybridAnalysis. Narzędzie to jest jednak o tyle istotne i uniwersalne w pracy analityka CTI, incident respondera czy threat huntera, że zdecydowanie warto poświęcić mu osobny wpis.

Zaczynając od początku YARA to narzędzie umożliwiające wyszukiwanie i klasyfikacje plików różnego rodzaju w oparciu o występujące w nich ciągi danych, a w korzystając z rozszerzonej funkcjonalności również cech pliku. Można więc powiedzieć, że jest to rozbudowana wersja znanego z systemów Linux narzędzia Grep ułatwiającego wyszukiwanie określonych treści w plikach. Przyjrzyjmy się więc jak zbudowana jest reguła YARA:

Zaczynamy więc od polecenia import, które umożliwia nam korzystanie z modułów rozszerzających funkcjonalność reguł o dodatkowe funkcje. W tym przypadku korzystamy z modułu PE dodającego funkcjonalność związane z analizą plików wykonywalnych (portable executable), jednak jego wykorzystanie zobaczymy dopiero w sekcji określających warunki reguły.

Właściwą reguły zaczynamy od jej nazwy, którą definiujemy po „rule” i otwieramy nawias klamrowy rozpoczynając treść reguły. Pierwszą częścią jest zazwyczaj „meta”. Pomimo, że sekcja ta jest nieobowiązkowa to jej dodanie jest zdecydowanie dobrą praktyką. Umieśćmy tam datę stworzenia reguły, nasze dane kontaktowe (pochwalmy się w końcu jacy z nas zdolni inżynierowie detekcji 🙂 ), krótki opis tego czemu służy reguła i hashe plików na podstawie jakich ją stworzyliśmy. W ten sposób znacznie ułatwimy pracę tym którzy będą wykorzystywać nasze dzieło do poszukiwania zagrożeń.

Kolejne dwie sekcje będą definiować właściwy zakres detekcji reguły. Pierwsza to strings gdzie określimy ciągi danych które chcemy wyszukiwać. YARA obsługuje trzy rodzaje danych: tekstowe, w formacie szesnastkowym, i wyrażenia regularne.

Dane tekstowe (strings) to najbardziej podstawowy format, jednak równocześnie ten z którego będziemy korzystać najczęściej. Wyszukiwanie nietypowych słów, zdań zawartych w kodzie czy charakterystycznych literówek bardzo często umożliwia wyszukiwanie powiązanych próbek malwareu i śledzić działania developerów. Dodanie wartości tekstowej jest bardzo proste – używamy znaku $ aby oznaczyć ciąg danych, po znaku umieszczamy wybraną nazwę którą będziemy się posługiwać oznaczając warunki, i w końcu po znaku = w cudzysłowiu podajemy wartość. Dodatkowo możemy skorzystać z modyfikatorów, które powiedzą YARA jak interpretować naszą wartość. Wymieniając ich funkcjonalność:

  • nocase – ignorowanie wielkości liter
  • ascii – dane kodowane w ascii
  • wide – dane kodowane w unicode
  • xor – przeprowadź na danych operację xor z kluczem o długości jednego bajtu
  • base64 – zastosuj kodowanie base64, możemy użyć również base64wide
  • fullword – jeżeli chcemy aby nasze dane poprzedzał i kończył znak niealfanumeryczny jak np.: „.”
  • private – dane nie będą wyświetlane w rezultatach

Kolejny typ danych to te w systemie szestnastkowym czyli popularne hexy. Składnia będzie tutaj bardzo, zastępujemy jednak cudzysłowy nawiasami klamrowymi. Ponieważ wzory hexów jakie znajdziemy w plikach czy zrzutach pamięci nie zawsze będą ładnie ułożone, możemy skorzystać z wielu pomocnych modyfikatorów danych. Spójrzmy na przykładową wartość:

{ F4 23 ( 62 B4 | 56 ) 45 C4 [2-4] B3 [8-] ?? D1 [-] }

Poza „zwykłymi” wartościami w hexach, umieściłem kilka specjalnych wpisów, przeanalizujmy je więc po kolei:

  • ( 62 B4 | 56 ) – alternatywne wartości, więc reguła zostanie spełniona jeżeli w tym miejscu wystąpi „62 B4” albo „56”.
  • [2-4] – w tym miejscu może wystąpić dowolny ciąg danych o długości 2 do 4 bajtów.
  • [8-] – podobnie jak wyżej tylko że szukamy od 8 do nieskończoności.
  • ?? – dowolna wartość szestnastkowa, możemy skorzystać również dla części wartości jak D? albo ?4.
  • [-] – znów dowolne wartości, ale o dowolnej długości – od zera do nieskończoności.

W przypadku wartości szestnastkowych możemy również użyć modyfikatora private aby wartość nie była wyświetlana w wynikach.

W końcu możemy skorzystać z wyrażeń regularnych aby określić bardziej złożony ciąg danych. Jest to narzędzie o zdecydowanie największych możliwościach ale musimy też pamiętać, że wyszukiwanie danych na podstawie wyrażeń regularnych będzie najbardziej obciążające dla zasobów. Aby przedstawić składnie popularnych „regexów” trzeba by całego osobnego artykułu, spójrzmy jednak na przedstawiony przykład aby zrozumieć zasadę w kontekście YARA:

$s3 = /[0-9]?[a-z]{3}/i

Wyrażenie regularne umieszczamy pomiędzy znakami /, tutaj dodaliśmy jeszcze modyfikator „i” oznaczający, że nie będzie brana pod uwagę wielkość liter. Jeżeli chodzi o samą treść wyrażenia to:

[0-9]? – cyfra pomiędzy 0 a 9, która może wystąpić ale nie musi – co zostało określone przez znak „?”

[a-z]{3} – 3 litery pomiędzy „a”, a „z”.

Przykładowo wyrażenie to zostanie spełnione przez ciąg „4fSa”. Zwróćmy uwagę na różną wielkość liter, która może wystąpić ze względu na modyfikator „i” dodany w ciągu $s3.

Gdy już zdefiniowaliśmy interesujące dla nas dane, zajmijmy się warunkami które sprawią, że określony plik zostanie uznany za zgodny z regułą. Wróćmy do trzeciej sekcji, „conditions”:

Zaczynając od pierwszego warunku, to wykorzystujemy tutaj moduł PE zaimportowany w pierwszej linijce pliku. Z modułu tego wykorzystujemy funkcje pozwalającą określić liczbę sekcji w pliku wykonywalnym i ustalamy że chcemy wyszukiwać takich gdzie liczba ta będzie równa trzem.

Dalej wykorzystujemy możliwość YARA do odczytywania danych w określonych offsetach i oznaczając zerowy offset szukamy znaków 5A (Z) i 4D (M) na początku plików. Dla wielu analityków literki tę będą na pewno doskonale znamy, przypomnijmy jednak, że MZ na początku pliku jest oznaczeniem plików wykonywalnych. Zwróćmy jeszcze uwagę na kolejność literek – podaliśmy w końcu najpierw Z, a następnie M. Wynika to z tego, że standardowo będziemy tutaj korzystać z zapisu little endian, czyli od najmniej do najbardziej istotnych bajtów. Moglibyśmy zmienić format na big endian dodając „be” do uint16.

Kolejny warunek dotyczy rozmiaru pliku. Za pomocą zmiennej filesize określamy, że szukamy plików większych niż 500 kb.

I w końcu zajmujemy się określeniem jak mają być traktowane ciągi danych, które wprowadziliśmy w części strings. Opcji jest tutaj wiele, moglibyśmy wymienić poszczególne z nich wskazując na nazwy, które nadaliśmy albo, jak w tym przypadku, wskazując że wystarczy dowolna z nich. Przeciwieństwem byłoby użycie zwrotu all of them. Jeżeli konwencja nazewnicza którą przyjeliśmy pozwala na to, możemy również skorzystać z „*” aby odwołać się do grupy wartości. W naszym przypadku moglibyśmy użyć „$s*” co oznaczałoby dowolną z przypisanych przez nas wartości.

Teraz spójrzmy jak w praktyce możemy wykorzystać YARA aby skanować pliki. Źródeł gotowych reguł jest wiele – od firm w sektorze bezpieczeństwa, przez niezależnych badaczy do organizacji rządowych. Do grupy tych ostatnich należy Niemiecki Federalny Urząd Ochrony Konstytucji. I tak w Styczniu tego roku poinformował on o aktywności powiązanej z Chinami grupy APT27 przeciwko Niemieckim przedsiębiorstwom. Wraz z ostrzeżeniem opublikowane zostały właśnie reguły YARA umożliwiające sprawdzenie czy pliki implantów znajdują się w naszym środowisku. Kopiujemy więc reguły i zapisujemy np.: jako APT27.yar.

W realnych warunkach skanowalibyśmy systemy plików w naszym środowisku, jednak dla naszego przykładu ściągniemy po prostu próbkę z przykładu, dostępną na MalwareBazaar tutaj. Następnie korzystamy z narzędzia YARA, podajemy plik reguły i obszar do skanowania:

Jak widzimy plik DLL został złapany przez regułę „vftrace_loader” umieszczoną w naszym pliku .yar.

Tak wyglądają więc podstawy pisania i wykorzystania reguł YARA do klasyfikacji i odnajdywania próbek malwareu. Możliwości i liczba funkcji dostępnych w tym narzędziu jest oczywiście dużo większa, zachęcam do zapoznania się z dokumentacją jak i zwyczajnie regułami tworzonymi przez innych i dostępnymi w Internecie. Osobnym tematem jest też analiza plików i odnajdywanie fragmentów, które można umieszczać w regułach aby wykrywać malware z danej rodziny pomimo różnic pomiędzy próbkami. Z pewnością będziemy więc do tematu powracać w przyszłych postach na counterintelligence.pl 🙂

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.

pl_PLPolish