Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book

Jak literówka w pakiecie Go przetrwała trzy lata… w tle atak na łańcuch dostaw

20 lutego 2025, 09:27 | W biegu | komentarze 2

Badacze Socket.dev zaprezentowali odkryty atak na łańcuch dostaw celujący w projekty napisane w Golangu. Atak wykorzystywał literówkę (tzw. typosquatting) w znanej bibliotece BoltDB, obsługującej bazę danych typu klucz-wartość. Oryginalny projekt został zarchiwizowany jakiś czas temu i nie jest już rozwijany. Atakujący sklonowali repozytorium, nadali mu bardzo zbliżoną nazwę i dodali backdoora, który między innymi pozwalał na zdalne wykonanie poleceń, przesyłanych z wykorzystaniem serwera C&C.

TLDR:

  • Zespół bezpieczeństwa Socket.dev odnalazł fork zarchiwizowanego projektu BoltDB, który zawierał złośliwy kod łączący się do serwera C&C. 
  • Projekt trafił do cache serwującego kod bibliotek, a złośliwe zmiany zostały usunięte z repozytorium, aby utrudnić wykrycie ataku
  • Biblioteka, pod którą próbowano się podszyć – BoltDB, jest zależnością w prawie 9000 projektów

Opisujemy ten atak, ponieważ nie wszyscy zdają sobie sprawę z faktu istnienia serwerów pośredniczących, które służą za cache dla kodu przechowywanego w repozytoriach. A jest to funkcja, którą wykorzystano w tym ataku, w celu ukrycia zamiarów i utrudnienia odnalezienia backdoora przy pomocy inspekcji samego repozytorium. Zbackdoorowany kod znajduje się w cache, przez co widoczny jest dopiero w momencie czytania kodu źródłowego lokalnego projektu. 

Środowisko Golanga wykorzystuje między innymi Go Module Proxy, które ma pomóc developerom pobrać pakiet, nawet w przypadku gdy zostanie on usunięty z głównego repozytorium kodu (na GitHubie czy GitLabie). Zapewnia on też powtarzalne kompilacje (reproducible builds). Dzięki temu, programiści Go mogą oczekiwać identycznych efektów za każdym razem. 

Rysunek 1. Zasada działania Go Module Proxy przy wykorzystaniu polecenia go get. (źródło: practical-go-lessons.com)

Wykorzystanie cache można skonfigurować lokalnie przy pomocy zmiennej środowiskowej GOPROXY

Rysunek 2. Schemat wykorzystania informacji zawartych w zmiennej środowiskowej GOPROXY  (źródło: practical-go-lessons.com)

Domyślnie ustawiona jest wartość https[:]//proxy.golang.org,direct, co oznacza tyle, że w przypadku wywołania polecenia go get, najpierw sprawdzone zostanie proxy proxy.golang.org a w następnej kolejności pakiet zostanie pobrany bezpośrednio z repozytorium git (wartość direct oddzielona przecinkiem). Oczywiście mechanizm ten można wyłączyć ustawiając GOPROXY na off. Obecne ustawienia można sprawdzić przy pomocy komendy go env GOPROXY.

Nasuwa się pytanie, kiedy projekt trafia do cache – okazuje się, że w momencie gdy pierwszy raz pobierany jest z repozytorium (i pozostaje tam na zawsze). Ten właśnie fakt wykorzystali atakujący, zastawiając pułapkę na użytkowników biblioteki BoltDB. 

Złosliwy pakiet umieszczony został w repozytorium pod adresem github[.]com/boltdb-go/bolt, podczas gdy oryginał znajduje się pod adresem github[.]com/boltdb/bolt. Fałszywy moduł został rozszerzony o backdoora. Gdy jego kod znalazł się na serwerze cache, oryginalna zawartość repozytorium na portalu GitHub została zmieniona w taki sposób, aby wprowadzane zmiany nie były widoczne. W założeniu miało to utrudnić odnalezienie backdoora przez manualną inspekcję repozytorium. Wciąż jednak, użytkownicy pobierający kod przy pomocy polecenia go get, otrzymywali złośliwą wersję serwowaną z cache.

Kod backdoora był zaciemniony. Badacze podają przykłady złośliwych zmian, przytaczamy przykładowe ukrycie adresu IP:

const (
    MaxMemSize = 64966512577  // Obfuscated IP part
    MaxIndex   = 6179852731   // Obfuscated IP part
    MaxPort    = 2060272      // Obfuscated port
)

Listing 1. Zakamuflowany adres serwera C&C

Zmienne z listingu 1 zostały następnie zmienione przed użyciem przy pomocy następującego kodu:

func _r(s string) string {
    // String manipulation / obfuscation
    // Replaces '5' with '.' and removes '6' and '7' to disguise the C2 address

    ret := strings.ReplaceAll(s, "5", ".")
    ret = strings.ReplaceAll(ret, "6", "")
    ret = strings.ReplaceAll(ret, "7", "")
    return ret
}

Listing 2. Deobfuskacja adresu IP serwera C&C

W efekcie, malware zamieniał z pozoru nic nie mówiącą wartość „649665125776179852731:2060272

 na adres „49.12.198[.]231:20022„. 

Oczywiście strona pakietu wykorzystuje oryginalne materiały prawdziwego projektu, co powoduje, że trudniej odróżnić go od prawdziwego. Nic dziwnego, że fałszywa paczka była dostępna przez ponad trzy lata. W międzyczasie trafić mogla do wielu projektów.

Rysunek 3. Oryginalne readme (źródło: socket.dev)
Rysunek 4. Readme fałszywego projektu  (źródło: socket.dev)

W międzyczasie badacze odnaleźli też kolejne wcielenie tej samej biblioteki – github[.]com/bolt-db/bolt, jednak nie miała ona znamion złośliwej. Dla bezpieczeństwa, zgłoszono ją do usunięcia, aby nie wprowadzać niepotrzebnego zamieszania i zapobiec ewentualnym nadużyciom w przyszłości. 

Jak wyglądała faza aktywna  ataku? Programista który wykonał polecenie go get <złośliwy moduł> pobierał tak naprawdę zbackdoorowaną bibliotekę. W momencie jej uruchomienia, software łączył się z serwerem command and control i oczekiwał na polecenia przesyłane tym kanałem. Ich wyniki były zwracane z powrotem. 

Zapobieganie atakom tego typu wymaga od programistów szczególnej uwagi w momencie dodawania zależności. Pakiety (ich rzeczywisty kod) powinien zostać w miarę możliwości zweryfikowany. Warto wielokrotnie upewniać się, że dobrze wpisaliśmy nazwę wymaganej paczki. Nie należy też w ciemno ufać halucynacjom dużych modeli językowych.

~fc

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. kamil

    „MaxPort” – haha inwencja się skończyła :D

    Odpowiedz
  2. Adam

    Czy obrazy 3 i 4 nie są zamienione?

    Odpowiedz

Odpowiedz