NFsec Logo

RapidFuzz i CharMap jako pomoc przy sprawdzaniu phishingu

15/01/2025 w Bezpieczeństwo Brak komentarzy.  (artykuł nr 919, ilość słów: 1148)

R

apidFuzz jest cenioną za uniwersalność i szybkość biblioteką języka Python, która oferuje zestaw funkcji pomagających w obliczaniu różnic między ciągami znaków oraz dopasowywania rozmytego (ang. fuzzy matching). W swojej implementacji posiada algorytmy obliczające odległość (miarę różnicy między dwoma ciągami znaków) takie jak: Levenshteina, Damerau-Levenshteina, Hamminga, czy Jaro-Winklera oraz algorytmy obliczające współczynnik podobieństwa ciągów znaków (np. porównujące liczby pasujących do siebie znaków na tle całkowitej liczby znaków w obu ciągach). Możemy wykorzystać ją w swoim procesie przetwarzania danych, aby automatycznie identyfikować: dopasowania, duplikaty, literówki; usprawniać oczyszczanie danych lub szukać w innych bazach danych podobnych wzorców.

Jako przykład użycia posłużymy się tym ostatnim przypadkiem. Na postawie listy ostrzeżeń CERT Polska, w której zawarte są głównie domeny wykorzystywane do phishingu będziemy sprawdzać, czy inne domeny pojawiające się w przestrzeni internetowej pasują przynajmniej w 90% do już znanych wzorców. Jeśli nie – będziemy sprawdzać czy istnieją takie same wzorce za pomocą algorytmu, który nazwałem “mapą znaków”. Bardzo często dzieje się tak, że prowadzona kampania wymierzona w daną markę posiada wiele domen o podobnym schemacie np.:

00lx-shopping.2323256.xyz
00lx-shopping.3122121.xyz
00lx-shopping.385430.xyz

delivery-dpd.64684512.xyz
delivery-dpd.64781200.xyz
delivery-dpd.6560000.xyz

kup-online33212.pl
kup-online33882.pl
kup-online55003.pl

Dlatego wyłapywanie ich za pomocą algorytmów dostępnych w i poza RapidFuzz może znacznie przyśpieszyć proces automatycznego ich zgłaszania do wielu zainteresowanych odbiorców. Pierwszą funkcją jaką użyjemy w celach demonstracyjnych jest WRatio (ang. weighted ratio) – oblicza ona wyważony współczynnik na podstawie innych algorytmów dostępnych w bibliotece. Do “przemielenia” wielu domen użyjemy modułu procesowania, który porównuje ciągi znaków z listami innych ciągów. Jest to generalnie bardziej wydajne niż używanie bezpośrednio algorytmów punktujących w pętli.

def find_similar_score(entries: list[str]) -> bool:
    report: list[tuple[str, float, int]] = process.extract(
        query=fqdn,
        choices=entries,
        limit=999999,
        score_cutoff=90
    )

Na razie będą interesowały nas tylko wyniki, które są równe lub powyżej 90% (score_cutoff) i chcemy przeszukiwać całą bazę (limit), która aktualnie liczy 257.646 wpisów. Nie musimy definiować algorytmu punktującego, ponieważ w standardzie jest on ustawiony na WRatio; nie ustawiamy też żadnego procesora usuwającego znaki specjalne oraz przeprowadzającego konwersję wszystkich znaków na małe litery. Kilka przykładów użycia:

agresor@darkstar:~$ ./phisherfisher allegrolokalnie.pl62985oferta-iphone.pl
INFO: Domain score found 1 similar phishing domain(s). Example:
      allegrolokalnie.pl19293oferta-iphone.pl with score 92.30.

agresor@darkstar:~$ ./phisherfisher o1x.99415487.xyz
INFO: Domain score found 4 similar phishing domain(s). Example:
      o1x.99415487.xyz with score 100.0.

agresor@darkstar:~$ ./phisherfisher www.inpost.polska24-dostawa24.shop
INFO: Domain score found 4 similar phishing domain(s). Example:
      www.i-inpost.polska24-dostawa24.shop with score 97.14.

agresor@darkstar:~$ ./phisherfisher allegrolokalnie.p24-v09a091.shop
INFO: Domain score found 1 similar phishing domain(s). Example:
      allegrolokalnie.p24-v9a01.pl with score 90.0.

W bardzo wielu przypadkach RapidFuzz spisuje się wzorowo, ale faktem jest, że porównujemy domeny, które nie zawsze muszą być semantycznie poprawne i mogą być tak różne od siebie pod względem użytych znaków w całym ciągu, że trudno mu wskazać jednoznacznie podobieństwo mimo, że po wzrokowej analizie widać, że to ten sam lub powracający schemat. Na przykład:

agresor@darkstar:~$ ./phisherfisher o1x.79715482.xyz
INFO: Based on score, no similar phishing domain(s) found.

Oczywiście możemy obniżyć próg detekcji np. do 80%:

agresor@darkstar:~$ ./phisherfisher o1x.79715482.xyz
INFO: Domain score found 12 similar phishing domain(s). Example:
      o1x.539715481.xyz with score 84.85.

Jednak ciągłe obniżanie progu spowoduje, że dojdziemy do ściany tworząc niskiej jakości dopasowania. I tutaj wkracza mapa znaków aka CharM(ap), czyli prosty konwerter ciągu znaków na rodzaje znaków użyte w owym ciągu:

def create_map(item: str) -> str:
    return ''.join('a' if c.isalpha() else
                   'd' if c.isdigit() else
                   c for c in item)

Dzięki temu domena, z którą RapidFuzz sobie “nie poradził” zamienia się w:

o1x.79715482.xyz = ada.dddddddd.aaa

i zostaje porównana do innych domen, które mają taką samą mapę:

agresor@darkstar:~$ ./phisherfisher o1x.79715482.xyz
INFO: Based on score, no similar phishing domain(s) found.
INFO: Subdomain charm found 84 similar phishing domain(s). Example:
      o1x.05412458.xyz.

o1x.79715482.xyz = ada.dddddddd.aaa
o1x.05412458.xyz = ada.dddddddd.aaa

W pierwszym kroku nie robimy tego na wszystkich domenach, tylko na zbiorze o takich samych subdomenach:

def find_similar_subdomain_charm(entries: list[str]) -> bool:
    report = [entry for entry in entries
              if re.search(f'^{subdomain}\.', entry)
              and create_map(fqdn) == create_map(entry)]

Patrząc na charakterystykę phishingu puszczanego na różne polskie serwisy można dojść do wniosku, że subdomeny są stałym elementem w ramach różnych kampanii. Dlatego zamiast przeszukiwać cały zbiór od razu możemy na początku skupić się na subdomenie, która podwyższa wiarygodność detekcji:

agresor@darkstar:~$ ./phisherfisher vimted.99415483.xyz
INFO: Based on score, no similar phishing domain(s) found.
INFO: Subdomain charm found 16 similar phishing domain(s). Example:
      vimted.07865460.xyz.

agresor@darkstar:~$ ./phisherfisher ddpd.98941248.xyz
INFO: Based on score, no similar phishing domain(s) found.
INFO: Subdomain charm found 18 similar phishing domain(s). Example:
      ddpd.06591548.xyz.

agresor@darkstar:~$ ./phisherfisher impost.68941248.xyz
INFO: Based on score, no similar phishing domain(s) found.
INFO: Subdomain charm found 53 similar phishing domain(s). Example:
      impost.05184122.xyz.

Algorytmem “ostatniego ratunku” jest hybryda RapidFuzz oraz CharM(ap). Zamiast przefiltrować wszystkie domeny tylko samym CharM(ap) pozwalamy RapidFuzz na przeszukanie również ich lekkich modyfikacji. Dzięki temu, gdy dopasowanie 1 = 1 (100%) zawiedzie w CharM(ap) będziemy mogli spojrzeć na bliskie alternatywy obniżając dopasowanie do 95%, ale tylko jeśli ich TLD są identyczne:

def find_similar_domain_charm(entries: list[str]) -> bool:
    report = [(entry, score) for entry in entries
              if (score := fuzz.WRatio(create_map(fqdn), create_map(entry),
                                       score_cutoff=95)) > 95
              and re.search(f'\.{tld}$', entry)]

Przykłady:

agresor@darkstar:~$ ./phisherfisher allegr00lokalnie.47906408.xyz
INFO: Based on score, no similar phishing domain(s) found.
INFO: Based on subdomain charm, no similar phishing domain(s) found
INFO: Domain charm found 70 similar phishing domain(s). Example:
      all-egr0lokalnie.52312226.xyz with score 96.55.

agresor@darkstar ./phisherfisher allegr0loka1nie.pl-ogloszenie-firmowe-736832.icu
INFO: Based on score, no similar phishing domain(s) found.
INFO: Based on subdomain charm, no similar phishing domain(s) found
INFO: Domain charm found 1 similar phishing domain(s). Example:
      allegrolokalnie.pl-ogloszenie-firmowe-521092.icu with score 95.83.

Cały kod skryptu znajduje się na GitHub.

Więcej informacji: RapidFuzz, Lista Ostrzeżeń przed niebezpiecznymi stronami

Kategorie K a t e g o r i e : Bezpieczeństwo

Tagi T a g i : , , , , , , , , , , ,

Komentowanie tego wpisu jest zablokowane.