Przejdź do treści
Portal poradnikowy

Klucze podpisu aplikacji Android wyciekają na GitHub — badacze znaleźli 5673 keystore’y i powiązali część z 278 prawdziwymi aplikacjami

Plik z kluczem podpisującym aplikację nie wygląda groźnie. Zwykle ma rozszerzenie .jks albo .keystore, leży gdzieś obok konfiguracji Gradle i przez lata może nie zwracać niczyjej uwagi. Problem zaczyna się wtedy, gdy razem z nim do publicznego repozytorium trafia hasło. Wtedy nie jest to już przypadkowo opublikowany element projektu, ale potencjalnie komplet danych pozwalających podszyć się pod autora aplikacji.

Badacze z University College Dublin, JD Group i Technical University of Munich przeanalizowali publiczne repozytoria GitHub i znaleźli 56 510 plików keystore. Po usunięciu duplikatów i sprawdzeniu materiału kryptograficznego zidentyfikowali 5673 unikalne, skompromitowane keystore’y zawierające 6602 klucze podpisujące. W aż 46 619 przypadkach, czyli 82,5 proc. znalezionych plików, w repozytorium znajdowało się również hasło zapisane jawnym tekstem.

Najważniejszy wynik pojawił się jednak później. Odciski certyfikatów pochodzących z wycieków porównano z podpisami ponad 4 tys. rzeczywistych aplikacji. 26 skompromitowanych certyfikatów udało się powiązać z 278 aplikacjami: 26 programami dystrybuowanymi przez sklepy z aplikacjami oraz 252 aplikacjami preinstalowanymi na urządzeniach producentów. Badacze nie ograniczyli się przy tym do stwierdzenia, że klucze „teoretycznie mogłyby być niebezpieczne”. Przygotowali działający proof of concept pokazujący, że wyciek może pozwolić na przygotowanie zmodyfikowanej aplikacji rozpoznawanej przez Androida jako aktualizacja programu podpisanego tym samym kluczem.

5673 keystore’y to nie 5673 przejętych aplikacji. Problem jest bardziej złożony

Najpierw trzeba oddzielić dwie liczby, które łatwo pomylić. 5673 oznacza liczbę unikalnych keystore’ów uznanych przez badaczy za skompromitowane, ponieważ mogli nie tylko znaleźć sam plik, lecz również odzyskać niezbędne do jego użycia hasło. Nie oznacza to, że każdy z tych kluczy podpisywał popularną aplikację znajdującą się obecnie na telefonach użytkowników.

Badacze zebrali łącznie 11 526 unikalnych kluczy prywatnych, rozpoznawanych na podstawie odcisków certyfikatów SHA-256. Następnie szukali haseł w tych samych repozytoriach. W ten sposób zawęzili zbiór do 5673 skompromitowanych keystore’ów i 6602 znajdujących się w nich kluczy podpisujących.

Tu pojawia się detal, który dobrze pokazuje, że problem nie wynika z zaawansowanego łamania kryptografii. Najczęściej spotykane hasła obejmowały:

  • android,

  • password,

  • 123456.

Co więcej, 5379 z 6602 kluczy, czyli 81,5 proc., korzystało z takiego samego hasła jak zawierający je keystore. To w praktyce eliminuje jedną z warstw ochrony, jaką daje możliwość stosowania osobnego hasła dla magazynu i osobnego dla klucza.

Badanie objęło następnie 4196 unikalnych aplikacji i 2225 różnych podpisów deweloperskich. Dopiero porównanie ich z odzyskanymi kluczami pozwoliło wykazać, że 26 zagrożonych certyfikatów było faktycznie używanych do podpisania 278 prawdziwych aplikacji. Certyfikaty można było powiązać łącznie z 75 repozytoriami.

Lista nie składa się z anonimowych projektów testowych. Wśród aplikacji ze sklepów pojawiają się między innymi pakiety należące do Baidu, Kwai, Xiaomi, Ctrip, VIPSHOP i EastMoney. Badacze wskazali m.in. com.baidu.BaiduMap, com.baidu.input, com.smile.gifmaker, ctrip.android.view czy com.xiaomi.smarthome.

Trzeba jednak bardzo ostrożnie traktować liczby opisujące skalę użytkowników. Autorzy zestawiali dane o pobraniach i instalacjach z wielu sklepów, w tym chińskich platform Tencent, Baidu, Wandoujia oraz sklepów producentów Huawei, Xiaomi, Oppo i Vivo. Suma pobrań nie jest liczbą unikalnych użytkowników. Ten sam człowiek może instalować aplikację kilka razy, zmieniać telefon albo pobierać kolejne wersje. Dlatego astronomiczne wartości liczone w dziesiątkach miliardów instalacji są przede wszystkim miarą skali dystrybucji, a nie populacji faktycznie zagrożonych osób.

Istotne jest również ograniczenie samego badania. Analiza dotyczyła przede wszystkim publicznych repozytoriów GitHub, a nie całego internetu. Nie obejmuje pełnej historii GitLaba, Bitbucketa, Gitee, prywatnych serwerów Git ani kluczy wykradzionych w wyniku włamań. Autorzy nie próbowali też łamać haseł metodą brute force. Wynik 5673 należy więc traktować jako udokumentowany zbiór przypadków spełniających przyjęte kryteria, a nie ostateczną liczbę wszystkich wycieków kluczy Androida.

Dlaczego wyciek klucza podpisującego jest znacznie poważniejszy niż wyciek zwykłego hasła API

Android wykorzystuje podpis aplikacji między innymi do sprawdzania, czy nowa wersja programu rzeczywiście pochodzi od podmiotu, który podpisał poprzednią wersję. To ważne rozróżnienie: klucz podpisujący nie służy tylko do opublikowania APK. Jest kryptograficzną tożsamością aplikacji.

Jeżeli napastnik zdobędzie używany klucz prywatny i może nim podpisać zmodyfikowany pakiet, powstaje możliwość przygotowania aplikacji posiadającej prawidłową tożsamość podpisu. Nie oznacza to automatycznie, że napastnik wrzuci taki plik do Google Play i zaktualizuje miliony telefonów. Sklepy mają własne mechanizmy publikacji, konta deweloperskie, procedury weryfikacji i zabezpieczenia.

Problem robi się znacznie poważniejszy poza kontrolowaną ścieżką Google Play: przy sideloadingu APK, alternatywnych sklepach, firmowej dystrybucji aplikacji oraz oprogramowaniu preinstalowanym przez producentów urządzeń.

Badacze pokazali praktyczny wariant ataku na aplikację Baidu Input Method. Zmienili pakiet, dodali kod zapisujący wprowadzany przez użytkownika tekst, zwiększyli numer wersji i podpisali całość przejętym kluczem. W eksperymencie spreparowany pakiet mógł zastąpić legalną wersję aplikacji.

W przypadku zwykłej aplikacji konsekwencją może być przejęcie jej uprawnień i danych, do których ma dostęp. W przypadku klawiatury ryzyko jest oczywiste: taki program znajduje się pomiędzy palcem użytkownika a polami tekstowymi, więc potencjalnie widzi wiadomości, wyszukiwane hasła czy inne wprowadzane informacje.

Jeszcze gorszy wariant dotyczy aplikacji systemowych i privileged apps instalowanych fabrycznie przez producentów smartfonów. Badacze znaleźli 252 preinstalowane aplikacje powiązane ze skompromitowanymi certyfikatami. Część takich programów pracuje z uprawnieniami niedostępnymi dla aplikacji instalowanych normalnie przez użytkownika.

Zespół zwrócił uwagę między innymi na aplikacje Baidu instalowane przez różnych producentów jako element systemu. Wybrane programy miały dostęp do informacji o urządzeniu i liście zainstalowanych aplikacji. Dla normalnego programu podobny dostęp został mocno ograniczony w Androidzie 10 i 11, natomiast komponenty systemowe mogą działać według innych zasad.

To właśnie tutaj wyciek klucza potrafi przeskoczyć z kategorii „problem dewelopera” do kategorii problem całego urządzenia.

Autorzy opisali również możliwość wywołania trwałego konfliktu uprawnień w przypadku spreparowanej aplikacji uprzywilejowanej. Android może wykryć niezgodność między deklarowanymi uprawnieniami a systemową allowlistą, co w przedstawionym scenariuszu prowadzi do awarii podczas uruchamiania urządzenia.

Jest jeszcze jeden szczególnie niepokojący wątek. Badacze odnaleźli klucz związany z Baidu CarLife, platformą integrującą smartfon z samochodowym systemem multimedialnym. CarLife była instalowana w systemach przeznaczonych dla ponad 1100 modeli pojazdów 99 producentów, w tym marek Toyota, Ford, Hyundai, Audi, BMW, Volkswagen i Mercedes. Pokazuje to, że problem z kluczem Androida nie musi kończyć się na smartfonie. Ten sam model podpisywania oprogramowania działa również w urządzeniach samochodowych, IoT i innych systemach opartych na Androidzie.

Klucz trafił do GitHuba. Co deweloper powinien zrobić najpierw

Najgorsza reakcja po znalezieniu keystore’a w publicznym repozytorium to usunąć plik, wyczyścić aktualną gałąź i uznać sprawę za zamkniętą.

Jeżeli sekret był publiczny, trzeba założyć, że został skopiowany. Git ma historię, istnieją forki i klony repozytoriów, działają boty indeksujące sekrety. Usunięcie oryginalnego pliku ogranicza dalszą ekspozycję, ale nie przywraca poufności klucza.

Priorytety powinny wyglądać inaczej.

1. Najpierw ustal, jaki klucz właściwie wyciekł.

W projektach korzystających z Google Play App Signing trzeba odróżnić upload key od właściwego app signing key.

To nie jest semantyka. Konsekwencje są zupełnie inne.

Jeżeli wyciekł jedynie upload key, Google pozwala wygenerować nowy klucz i złożyć w Play Console wniosek o jego reset. Właściwy klucz, którym Google podpisuje APK dostarczane użytkownikom, pozostaje przechowywany w infrastrukturze Play App Signing.

Jeżeli aplikacja nie korzysta z takiego rozdzielenia i ujawniony został jej faktyczny prywatny klucz podpisujący, sytuacja jest trudniejsza. Trzeba sprawdzić obsługiwany mechanizm key rotation, wersje Androida znajdujące się w bazie użytkowników oraz sposób dystrybucji aplikacji w poszczególnych sklepach. Nie istnieje jedna operacja, która automatycznie unieważni stary podpis na każdym urządzeniu i w każdym kanale dystrybucji.

2. Traktuj sekret jako przejęty, a nie „potencjalnie widoczny”.

Jeżeli keystore i hasło znajdowały się w publicznym repozytorium, nie ma sensu próbować ustalać, czy ktoś rzeczywiście je pobrał. Brak śladu użycia klucza nie jest dowodem, że nikt go nie skopiował.

Do analizy powinny trafić:

  • historia repozytorium i wszystkie branche,

  • forki projektu,

  • artefakty CI/CD,

  • stare obrazy kontenerów,

  • pliki konfiguracyjne Gradle,

  • kopie .properties,

  • logi pipeline’ów,

  • release’y oraz archiwa projektu,

  • inne aplikacje podpisywane tym samym kluczem.

Ostatni punkt często jest pomijany. Jeden keystore może być używany do kilku aplikacji. Rotacja wykonana tylko w projekcie, w którym wykryto wyciek, nie rozwiązuje wtedy problemu.

3. Usuń klucz z procesu developerskiego, nie tylko z GitHuba.

Plik .jks na laptopie osoby przygotowującej release oraz hasło w gradle.properties to wygodny układ do momentu pierwszego incydentu.

Dla nowych aplikacji publikowanych w Google Play rozsądniejszym wariantem jest rozdzielenie klucza uploadowego od właściwego klucza podpisującego i korzystanie z Play App Signing. Google przechowuje wówczas app signing key po swojej stronie, a utrata upload key nie oznacza utraty kryptograficznej tożsamości aplikacji.

W środowisku firmowym klucz powinien być wyciągnięty z normalnego repozytorium i procesu pracy programisty. Dostęp powinien być ograniczony do procesu wydawniczego, najlepiej chronionego wieloskładnikowo, a hasła i sekrety pobierane dopiero podczas pipeline’u.

Nie oznacza to, że wrzucenie pliku do GitHub Actions Secrets automatycznie rozwiązuje cały problem. Sekret nadal może wyciec przez błędnie skonfigurowany workflow, logowanie zmiennych, artefakty buildu lub niekontrolowane środowisko runnera. Secret manager zmniejsza powierzchnię ataku, ale nie naprawia wadliwego procesu release.

4. Sprawdź, gdzie jeszcze ten sam certyfikat jest używany.

To szczególnie ważne dla producentów urządzeń i firm utrzymujących kilka aplikacji.

Podpis może tworzyć relację zaufania pomiędzy programami, a aplikacje systemowe mogą posiadać uprawnienia wynikające z podpisu albo miejsca instalacji. Kompromitacja wspólnego certyfikatu oznacza więc potencjalnie problem większy niż jedna paczka APK.

Badanie pokazało dokładnie taki efekt: jeden z przejętych certyfikatów pojawiał się w 17 aplikacjach Baidu, a inny został znaleziony w 27 aplikacjach związanych m.in. z komponentami CTS na urządzeniach wielu producentów.

Na końcu pozostaje rzecz najbardziej niewygodna: rotacja klucza nie usuwa z internetu starego sekretu. Ona jedynie ogranicza jego przyszłą użyteczność tam, gdzie platforma i urządzenia poprawnie obsługują przejście na nową tożsamość. Dlatego pierwszym zadaniem po incydencie nie powinno być „posprzątanie GitHuba”, lecz ustalenie zasięgu klucza: jakie aplikacje podpisywał, w jakich sklepach były publikowane i na jakich urządzeniach są nadal zainstalowane.

Autorzy badania zgłosili potwierdzone przypadki właścicielom aplikacji i przed publikacją wstrzymywali szczegóły przez co najmniej 90 dni. W momencie przygotowywania pracy tylko jeden z deweloperów aplikacji z opisanej grupy zakończył rotację klucza. Repozytoria związane z potwierdzonymi przypadkami przestały być publicznie dostępne, ale samo ukrycie repozytorium nie oznacza unieważnienia skopiowanego wcześniej klucza.

I to jest najważniejsza lekcja z całej sprawy. Deweloper Androida powinien traktować release signing key podobnie jak klucz główny do infrastruktury produkcyjnej, a nie jak kolejny plik potrzebny Gradle do zbudowania paczki. Kod można poprawić kolejnym commitem. Ujawnionego klucza prywatnego nie da się „odujawnić”.

FAQ

Co dokładnie znaleźli badacze na GitHubie?
Zebrali 56 510 plików .jks i .keystore, z których po deduplikacji potwierdzili 5673 unikalne skompromitowane keystore’y zawierające 6602 klucze podpisujące. W 82,5 proc. znalezionych plików keystore repozytorium zawierało również hasło zapisane jawnym tekstem.

Czy 278 aplikacji oznacza, że wszystkie można obecnie łatwo przejąć?
Nie. Liczba oznacza aplikacje, których certyfikaty udało się powiązać ze skompromitowanymi kluczami w badanym zbiorze. Skuteczny atak zależy dodatkowo od sposobu dystrybucji, wersji aplikacji i Androida, mechanizmów integralności oraz tego, czy deweloper zdążył już przeprowadzić rotację klucza. Sami badacze zaznaczają, że nie istnieje uniwersalny, automatyczny mechanizm pozwalający masowo wykorzystać każdy znaleziony przypadek.

Czy użytkownik Androida powinien coś zrobić?
Najważniejsze jest instalowanie aktualizacji z oficjalnego kanału używanego przez producenta aplikacji i unikanie przypadkowych plików APK oferowanych jako „nowa wersja” programu. Szczególna ostrożność ma sens przy aplikacjach instalowanych poza Google Play oraz przy telefonach korzystających z alternatywnych sklepów.

Co zrobić, jeśli do GitHuba trafił mój upload key używany z Play App Signing?
Wygenerować nowy upload key i rozpocząć procedurę resetu klucza w Google Play Console. Reset upload key nie zmienia app signing key, którym Google podpisuje aplikację dostarczaną użytkownikom.

Czy wystarczy usunąć keystore z repozytorium?
Nie. Jeżeli plik był publiczny, należy uznać go za skopiowany. Trzeba ustalić wszystkie aplikacje i procesy korzystające z tego klucza, rozpocząć rotację tam, gdzie jest obsługiwana, usunąć sekret z historii oraz pipeline’ów i przenieść podpisywanie do kontrolowanego procesu wydawniczego.

Czy samo znalezienie pliku .jks oznacza kompromitację klucza?
Nie zawsze. Keystore może być zabezpieczony hasłem, którego w repozytorium nie ma. Autorzy badania klasyfikowali przypadek jako potwierdzoną kompromitację dopiero wtedy, gdy mogli odzyskać hasło z projektu lub wskazanego przez konfigurację pliku. Nie wykonywali łamania haseł brute force, dlatego rzeczywista liczba potencjalnie dostępnych kluczy może być większa.

Redakcja

Uwielbiam odkrywać ciekawe tematy i przekładać zdobytą wiedzę na przydatne poradniki. Piszę przede wszystkim z myślą o czytelnikach, którzy cenią konkret, prosty język i praktyczne rozwiązania.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *