TL; DR
Artefakt Maven Central opublikowany jako io.github.davidtimur:c2-lab wydał dziewięć wydań, z których osiem wykonywało ładunek zdalnego dostępu podczas kompilacji dowolnego projektu podrzędnego, który umieszczał plik JAR na ścieżce procesora adnotacji. Żaden kod aplikacji nie musiał go importować. Żadna linia kodu źródłowego nie musiała się do niego odwoływać. Nie było żadnego skryptu instalacyjnego, żadnego odpowiednika po instalacji ani żadnego haka cyklu życia narzędzia, który mógłby je sprawdzić.
Wektor wykonania to pojedynczy plik o rozmiarze 46 bajtów wewnątrz pliku jar: rejestracja dostawcy usług Java, która określa klasę implementującą javax.annotation.processing.ProcessorKompilator Java automatycznie wykrywa takie rejestracje. Po ich znalezieniu, javac Tworzy i uruchamia klasę jako normalny element kompilacji. Oznacza to, że środowiskiem wykonawczym ładunku jest maszyna kompilacji, w chwili, gdy maszyna kompilacji wykonuje jedyną czynność, do której istnieje.
W dziewięciu wersjach kanał poleceń i kontroli był przebudowywany trzy razy: najpierw przez adres URL wywołania zwrotnego dostarczony przez operatora, następnie przez powłokę odwrotną przez tunel TCP ngrok, a następnie przez kanał odpytywania HTTP, którego ścieżki zmieniały się jeszcze dwukrotnie. W wersji finalnej zainstalowano menedżera zaufania TLS bez operacji jako domyślną maszynę wirtualną Java (JVM). akceptowanie dowolnego certyfikatu na czas kompilacji.
Artefakt miał napis „C2 Lab Payload” w swoim własnym POM oraz licencję MIT. Był aktywny w Maven Central przez cały okres, w którym go obserwowaliśmy. został od tego czasu usunięty wraz z całym io.github.davidtimur grupa.
Anatomia: kompilator jako silnik wykonawczy
Framework przetwarzania adnotacji Javy umożliwia bibliotekom generowanie kodu w czasie kompilacji – mechanizm ten jest obecny w Lombok, Dagger i wielu innych narzędziach do ORM i serializacji. Procesor ogłasza się za pomocą pliku tekstowego w pliku jar:
META-INF/usługi/javax.annotation.processing.Processor
Zawartość tego pliku w każdej wersji, której dotyczy problem, w wersji dosłownej i kompletnej:
io.github.davidtimur.c2lab.C2Procesor
Plik jest identyczny bajtowo we wszystkich wersjach od 1.0.1 do 1.0.8, md5 7d2a08a5c8869a47eea9fa62487dfbe4. Wersja 1.0.0 go nie zawiera.
Kiedy javac Po uruchomieniu skanuje ścieżkę procesora adnotacji w poszukiwaniu tych wpisów usług i wczytuje znalezione dane. Nic w kompilowanym projekcie nie musi zawierać wzmianki o procesorze, adnotacji ani konfiguracji. Obecność na ścieżce jest wystarczająca. Ta właściwość odróżnia JavacDoor od wzorców łańcucha dostaw, wokół których zbudowana jest większość narzędzi:
| Wzór | Cyngiel | Widoczne jako |
|---|---|---|
| hak instalacyjny npm | npm install | scripts.postinstall w manifeście |
| Ładunek w czasie importu Pythona | pierwszy import modułu | instrukcja na poziomie modułu w źródle |
| Drzwi Javac | javac w każdym projekcie downstream | nazwa pliku rejestracji usług |
Hak instalacyjny to deklaracja w manifeście, a manifest jest pierwszą rzeczą, jaką ktokolwiek czyta. Ładunek danych w czasie importu znajduje się przynajmniej w czytelnym źródle. Rejestracja usługi nie jest ani jednym, ani drugim: to nazwa pliku i jeden wiersz nazwy klasy, a zachowanie jest zapisane w skompilowanym kodzie bajtowym, znajdującym się w innym katalogu.
Klasa ładunku sama przeprowadza rozpoznanie hosta poprzez pobieranie danych. Pule stałych skompilowanych klas zawierają / Bin / sh, whoami, uname -a, Pwd na ścieżce Unix i tasklist na ścieżce Windows, wraz z przekierowanie strumienia błędów Aby scalić wyjście błędu procesu potomnego z przechwyconym strumieniem. Dwa szablony JSON przesyłają wyniki z hosta — sygnał rejestracyjny:
{"host":"%s","os":"%s","user":"%s","dir":"%s"}
zamieszkany z hosta polecenie i os.name, user.name, użytkownik.dir właściwości systemu i wywołanie zwrotne wyniku:
{"version":"%s","host":"%s","time":"%s","output":"%s"}
Znaczniki postępu pozostawione w wynikach kompilatora są niezwykle szczere: [C2] wykonanie w czasie kompilacji zakończone, [C2] wysłano wywołanie zwrotne → HTTP , [C2] muszla połączona z .
Dziewięć wydań, trzy generacje C2
Wydania nie są dziewięcioma kopiami jednego ładunku. Stanowią dziennik iteracji, a ich odczytanie w kolejności pokazuje, że kanał jest odbudowywany, podczas gdy wektor dostarczania pozostaje niezmieniony.
| Wydanie | Wykonuje się automatycznie podczas kompilacji | Kanał |
|---|---|---|
1.0.0 | nie (brak pliku usługi) | Tylko adres URL wywołania zwrotnego dostarczony przez operatora |
1.0.1 | tak (wprowadzono wektor) | Dostarczony przez operatora adres URL wywołania zwrotnego |
1.0.2 | tak | Odwrócona powłoka, tunel TCP ngrok |
1.0.3 | tak | Kanał HTTP, /register /cmd /out |
1.0.4 | tak | Kanał HTTP, /register /cmd /out |
1.0.5 | tak | Kanał HTTP, /register /poll /out |
1.0.6 | tak | Kanał HTTP, /register /poll /out |
1.0.7 | tak | Kanał HTTP, /register /cmd |
1.0.8 | tak | Kanał HTTP + walidacja TLS wyłączona |
Warto zwrócić uwagę na trzy szczegóły w tej tabeli, ponieważ każdy z nich zmienia sposób, w jaki należy odczytywać zbiór artefaktów.
Wektor wskazuje 1.0.1, a nie 1.0.2. Wersja 1.0.0 zawiera tę samą logikę rozpoznania i wywołań zwrotnych, ale nie zawiera pliku usługi ani importów do przetwarzania adnotacji; działa tylko wtedy, gdy coś ją wywoła. Od wersji 1.0.1 plik usługi jest obecny, a skompilowane klasy importują. javax.annotation.processing.SupportedSourceVersionKażda ocena porównująca dwie wersje biletowane — 1.0.0 i 1.0.2 — prawidłowo wyciąga wniosek, że coś się zmieniło, ale błędnie określa, gdzie.
Odwrotna powłoka istnieje w dokładnie jednym wydaniu. Wersja 1.0.2 zawiera 0.tcp.ngrok[.]io, linia logarytmiczna [C2] powłoka połączona z 0.tcp.ngrok[.]io:19823, interaktywny baner powłoka c2i terminator ramki __KOŃCZYĆ SIĘ__Wersje 1.0.3 i nowsze nie zawierają żadnego z tych elementów i zamiast tego docierają do hosta HTTPS. Żądanie usunięcia, zawierające jedynie nazwy wydań objętych biletem, wskazywałoby na nieaktywny punkt końcowy TCP, a jednocześnie pomijałoby aktywny kanał HTTP, obecny w sześciu późniejszych wersjach.
W wersji końcowej usunięto weryfikację transportu. Wersja 1.0.8 dodaje klasę implementującą javax.net.ssl.X509TrustManager którego metody sprawdzania certyfikatów nic nie robią, zawsze prawdziwy weryfikator nazw hostów zarejestrowany przez ustawDomyślnyWeryfikatorNazwyHostaI zaufajWszystkim Procedura, która instaluje oba certyfikaty jako domyślne dla maszyny wirtualnej Java (JVM). W efekcie przez resztę kompilacji maszyna wirtualna Java akceptuje dowolny certyfikat z dowolnego hosta — nie tylko dla własnego ruchu danych, ale także dla wszystkiego, co kompilacja wykonuje później przez TLS.
Kanał HTTP we wszystkich sześciu wersjach, które go wykorzystują, jest obsługiwany przez pojedynczego hosta: tableful-fervor-crazed.ngrok-free[.]devKażde żądanie zawiera nagłówek ngrok-skip-browser-warning, który blokuje wyświetlanie stron interstitialnych, które tunele ngrok udostępniają przeglądarkom. Zestawy ścieżek zmieniają się w zależności od wersji — /na zewnątrz znika w wersji 1.0.7 i /cmd naprzemiennie z /głosowanie — ale gospodarz nigdy się nie zmienia.
Samowykluczenie mówi
Każdy plik POM od wersji 1.0.1 ustawia argument kompilatora dla własnej kompilacji artefaktu:
-proc:none
Ta flaga wyłącza przetwarzanie adnotacji. Jej efekt jest tutajcise: gdy projekt zawierający procesor zostanie skompilowany, procesor nie zostanie uruchomiony.
Ta flaga ma zupełnie zwyczajne zastosowania. Projekt, który dostarcza procesor adnotacji, często musi unikać stosowania go do siebie podczas bootstrapu, a dokumentacja narzędzia do kompilacji zaleca właśnie to. Sama w sobie niczego nie dowodzi.
W połączeniu z tym, co robi procesor, opisuje to jednak specyficzną asymetrię: kod jest wykonywany na komputerach wszystkich, którzy kompilują go na podstawie artefaktu, a nie na komputerze, który go buduje. Flaga pojawia się w tej samej wersji, która wprowadza plik usługi — 1.0.1 — i w każdej kolejnej. Korelacja między „wersją, w której rozpoczyna się wykonywanie w czasie kompilacji” a „wersją, w której wykonywanie w czasie kompilacji jest lokalnie wyłączone” jest najprzydatniejszym sygnałem analitycznym w zestawie artefaktów i jest widoczna w pliku POM w postaci zwykłego tekstu bez konieczności dekompilowania czegokolwiek.
Zauważamy efekt i na tym kończymy. Nic w artefaktach nie wskazuje na powód ustawienia flagi.
Warto wspomnieć o jeszcze jednym metadanym, głównie po to, by się go pozbyć. POM nazywa projekt „C2 Lab Payload”, opisuje go jako „artefakt ładunku C2 Lab Payload” i udziela licencji MIT. Samooznaczenie tego rodzaju jest czasami oferowane jako dowód, że pakiet jest ćwiczeniem badawczym.cise, a nie zagrożenie na żywo, i czasami ta interpretacja jest prawidłowa — zadeklarowany kanarek bez dostępnej infrastruktury to inny obiekt niż ten. Nie ma to tutaj zastosowania. Funkcjonalny implant opublikowany w publicznym repozytorium, dostępny dla każdego konsumenta, z infrastrukturą wychodzącą, która została przebudowana trzykrotnie w dziewięciu wydaniach, jest funkcjonalnością na żywo, niezależnie od tego, jak nazywają ją jego metadane. Nazwa w pliku POM nie zmienia niczego w kwestii tego, co dzieje się na komputerze, który ją kompiluje.
Wskaźniki dla maszyn budujących
Jeśli host kompilacji wykonał kompilację przy użyciu tego artefaktu, dowód znajduje się w dziennikach kompilacji i danych telemetrycznych sieci, a nie w trwałym implancie na dysku — ładunek jest uruchamiany w procesie kompilatora i wychodzi wraz z nim.
W pamięci podręcznej jar lub lokalnego repozytorium
META-INF/services/javax.annotation.processing.Processornazywaniaio.github.davidtimur.c2lab.C2Processor- Plik serwisowy md5
7d2a08a5c8869a47eea9fa62487dfbe4 - Zajęcia pod
io/github/davidtimur/c2lab/:C2Processor,C2Task,Task, a w wersji 1.0.8 klasa wewnętrznaTask$1
W wyjściu kompilacji
[C2] compile-time execution complete[C2] callback sent → HTTP[C2] callback failed:[C2] shell connected to- Baner interaktywny
c2-shell; terminator ramki__END__
Telemetria w trakcie procesu
javacjako rodzic/bin/sh -c(Unix) lub interpreter poleceń systemu Windows- Polecenia dla dzieci
whoami,uname -a,pwd(Unix) lubtasklist(Windows) nadrzędne w kroku kompilacji
W telemetrii sieciowej
- Wychodzący TCP do
0.tcp.ngrok[.]io:19823(wersja 1.0.2) - HTTPS do
tableful-fervor-crazed.ngrok-free[.]dev, ścieżki/register,/cmd,/poll,/out(wersje 1.0.3 do 1.0.8) - Nagłówek żądania
ngrok-skip-browser-warning: true - Dopasowanie treści żądań
{"host":...,"os":...,"user":...,"dir":...}or{"version":...,"host":...,"time":...,"output":...}
W konfiguracji
- Zmienne środowiskowe
CALLBACK,CALLBACK_URL; właściwość systemucallback.url
Metadane wydawcy
- Zarządzanie
io.github.davidtimur; adres wydawcydavudboi999@gmail[.]com; klucz podpisuE520C345EF94423D
Kompilacja z wersją 1.0.8 wymaga dodatkowego sprawdzenia. Ponieważ ta wersja instaluje domyślnie menedżera zaufania o liberalnym dostępie dla całej maszyny wirtualnej Java (JVM), każde połączenie TLS nawiązane później w tej samej maszynie wirtualnej Java (rozwiązywanie zależności, przesyłanie artefaktów, etap wdrażania) było kontynuowane bez weryfikacji certyfikatu. Ruch z tego okna nie powinien być uznawany za uwierzytelniony.
Dlaczego skompilowane artefakty wymagają innego skanowania
JavacDoor jest przydatnym przypadkiem testowym, ponieważ obala dwa powszechnie przyjęte założenia naraz, a żadna z usterek nie jest specyficzna dla narzędzi konkretnego dostawcy.
Pierwsze założenie jest takie, że niebezpieczny kod ujawnia się w manifeście. Duża część narzędzi łańcucha dostaw jest zorganizowana wokół cyklu życia hooks, ponieważ w przypadku npm i PyPI to właśnie tam zazwyczaj odbywa się akcja. JavacDoor nie ma haka. Jego wyzwalaczem jest plik rejestracji usług, którego nazwa to interfejs Java, a zawartość to nazwa klasy. Aby przechwycić to statycznie, musisz traktować META-INF/usługi/javax.annotation.processing.Processor jako samodzielny punkt wejścia do realizacji, na równi z po instalacji skrypt — a następnie podążaj za nazwaną klasą do bajtkodu. Ekosystemy mają własne mechanizmy automatycznego wykrywania tego typu, a każdy z nich stanowi punkt wejścia, niezależnie od tego, czy narzędzia go wyliczają.
Drugim założeniem jest to, że ciągi znaków znajdują się w plikach źródłowych. W przypadku pliku JAR punkty końcowe, polecenia powłoki, szablony JSON i znaczniki dziennika znajdują się w stałych pulach .klasa Pliki. Narzędzia do przeszukiwania tekstu metodą greps nic nie znajdują — nie dlatego, że ciągi są zaciemnione, ale dlatego, że znajdują się w ustrukturyzowanym kontenerze binarnym, którego skanowanie tekstu nie analizuje. Każdy wskaźnik sieciowy w tym poście pochodzi z analizy puli stałych. Jednym z nich jest w pełni sformatowany https:// Adres URL jest widoczny w pliku klasy; tekstowe skanowanie czytelnej zawartości pliku JAR nadal nie dałoby rezultatu. Nie ma tu żadnego kodowania do obejścia, jedynie format kontenera do odczytania.
Obie luki mają ten sam kształt: format artefaktu był traktowany jako zbiór plików, a nie struktura o zdefiniowanej semantyce. Rozwiązanie jest mało efektowne — należy przeanalizować kontener, wyliczyć punkty wejścia automatycznego wykrywania ekosystemu i śledzić je w skompilowanym kodzie. W przypadku wektorów generowanych w czasie kompilacji, trzecie sprawdzenie jest tanie i zaskakująco diagnostyczne: należy porównać, co artefakt robi konsumentom, z tym, z czego sam się wyłącza. Artefakt, który rejestruje procesor w czasie kompilacji i jednocześnie wyłącza przetwarzanie w czasie kompilacji dla własnego procesu kompilacji, powiedział ci coś o sobie w dwóch linijkach tekstu POM.
Zespoły korzystające obecnie z artefaktów Maven powinny kierować się trzema praktycznymi wskazówkami:
- Traktuj ścieżkę procesora adnotacji jako granicę wykonywania. Zależności, które tam trafią, uruchamiają kod w kompilacji. Jeśli kompilacja nie wymaga przetwarzania adnotacji, -proc:none jest tak samo użyteczny w celach obronnych, jak najwyraźniej był tutaj użyteczny lokalnie; tam, gdzie to robi, przypina zestaw procesora jawnie, zamiast dziedziczyć go ze ścieżki klas kompilacji.
- Podprocesy kompilatora dziennika. Krok kompilacji, który uruchamia powłokę, jest anomalią w większości projektów i można go łatwo ostrzec.
- Nie traktuj metadanych jako zeznań. „Lab”, „test”, „payload” i „PoC” w nazwie lub opisie pakietu nie stanowią ograniczeń zakresu. Ograniczeniami są natomiast osiągalność i zachowanie.
Artefakt i cała jego grupa zostały usunięte z Maven Centralny po naszym raporcie; zarówno ścieżka artefaktu, jak i ścieżka grupy zwracają teraz 404, a indeks Centralny nie zgłasza pasujących współrzędnych. To zamyka ten artefakt. Nie zamyka to wektora, co jest udokumentowaną funkcją kompilatora Java i dostępną dla każdego, kto publikuje plik jar.
Referencje
W tym poście nie cytowano żadnych źródeł zewnętrznych. Wszystkie ustalenia to statyczna analiza dziewięciu opublikowanych plików jar, pobranych z Maven Central przed usunięciem. Żaden kod z artefaktów nie został w żadnym momencie wykonany.







