Podatność umożliwiająca zalogowanie się na konto dowolnego użytkownika występowała w kilkunastu systemach administracji publicznej, w tym ZUS i CEZ. Wymagania? Dostęp do internetu, znajomość PESEL-u ofiary i posiadanie odpowiednich narzędzi. Przeprowadzenie ataku zajmowało mniej niż minutę, a sam atak omijał również dwuskładnikowe uwierzytelnianie. Nie, to nie jest clickbait. To ostatnia i najbardziej druzgocąca część mojego badania w obszarze e-podpisów, w którym najwidoczniej nie testuje się krytycznych integracji oprogramowania.
Przypomnijmy, w poprzednim artykule z tej serii próbowałem uzyskać nieautoryzowany dostęp do serwisów administracji publicznej przez funkcję logowania “certyfikatem kwalifikowanym”, używając do tego niepoprawnego podpisu cyfrowego, złożonego w oparciu o autentyczny certyfikat.
Ku wielkiemu zaskoczeniu, zastosowanie tej techniki dawało możliwość zalogowania się w niektórych systemach administracji publicznej, w tym niemal we wszystkich publicznych usługach Ministerstwa Sprawiedliwości.
Zdecydowana większość systemów innych urzędów obroniła się przed testowanym atakiem. Tym razem rozważymy więc alternatywny pomysł.
Seria “Badanie e-podpisów”
Artykuł stanowi część cyklu opisującego mój hobbystyczny projekt badania bezpieczeństwa oprogramowania związanego z obsługą podpisów kwalifikowanych.
- Zdalne wykonanie kodu w SzafirHost – [CVE-2026-26928] [Badanie e-podpisów, cz. 1]
- Hakowanie e-Sądu YubiKeyem – [Badanie e-podpisów, cz. 2]
- Ominięcie uwierzytelniania w ZUS-ie i systemach e-Zdrowia, czyli o krok od cyberchaosu – [CVE-2026-9058] [Badanie e-podpisów, cz. 3]
| Streszczenie całej serii w postaci niewymagającej od czytelnika posiadania rozległej wiedzy technicznej znajduje się w odrębnym artykule: “Krytyczna podatność umożliwiająca całkowite ominięcie logowania w ZUSie, e-Sądzie i systemach e-Zdrowia”. |
Rozwinięcie ataku: tym razem fałszywy certyfikat, prawdziwy podpis
Jednym z tych, którzy obronili się przed poprzednią próbą ataku, był system “e-Gate – Obsługa Podpisów Elektronicznych w Ochronie Zdrowia”. Szybko stał się on moim ulubionym obiektem testowym, ponieważ jako jedyny wyświetlał komunikaty błędów informujące o dokładnym powodzie, dla którego podpisany plik został odrzucony.

Ten komunikat dosłownie prowadzi mnie za rękę w procesie decydowania o dalszych krokach. Skoro przypadek niepoprawnego podpisu cyfrowego jest wykrywany, to postaramy się zaadresować ten konkretny problem. Zróbmy tak, aby podpis był poprawny i zgodny z pozostałymi danymi.
Tutaj powstaje istotne ograniczenie – poprawny podpis cyfrowy uzyskam jedynie w dwóch przypadkach:
- używając czyjejś karty do podpisu kwalifikowanego – odpada, naturalnie nie posiadam dostępu do cudzych kart,
- używając certyfikatu wraz z kluczem, które wydałem sam sobie – podpis cyfrowy będzie wtedy kryptograficznie poprawny, ale i tak powinien zostać odrzucony jako niezaufany, jako że nie jestem podmiotem uprawnionym do wydawania takich certyfikatów.
Czy tym razem teoria zgodzi się z praktyką? Spróbujemy to ustalić.
Alternatywne PKI
Na początek potrzebujemy stworzyć własną, alternatywną infrastrukturę klucza publicznego (ang. Public Key Infrastructure, PKI). Brzmi bardzo groźnie, ale sprowadza się do poprawnego użycia kilku komend linuksowych, które w efekcie wygenerują niezbędne klucze i certyfikaty, a finalnie zapiszą to wszystko w plikach na dysku.
Wygenerowałem swoje własne certyfikaty “root CA” oraz “intermediate CA” (tzn. głównego i pośredniego urzędu certyfikacji), dla pewności ostrożnie dostrajając konfigurację tak, aby była jak najbliżej prawdziwych certyfikatów. Użyłem identycznych nazw, poustawiałem atrybuty na takie same wartości i dołączyłem te same rozszerzenia X.509. Zasada była prosta – im bardziej wiarygodnie to wszystko wygląda, tym większa szansa na to, że nic mnie nie zablokuje i atak zakończy się sukcesem.
Po kilku godzinach walki z OpenSSL-em moje dzieło było gotowe, a certyfikaty rzeczywiście wyglądały niemal jak te prawdziwe. Jedyny szkopuł w tym, że nie znajdowały się na jakiejkolwiek liście zaufanych certyfikatów.

W międzyczasie rozwinąłem również koncepcję “fałszywej karty”. Tym razem zamiast YubiKeya tę rolę będzie pełnił SoftHSM2 – program, który symuluje fizyczny klucz kryptograficzny, ale tak naprawdę wszystkie operacje wykonuje na lokalnym komputerze. Po takiej modyfikacji, cała infrastruktura do przeprowadzenia ataku mieści się w ramach zwykłej maszyny wirtualnej z systemem Windows 11, nie wymagając jakiegokolwiek dodatkowego zewnętrznego sprzętu.
Moje fałszywe centrum certyfikacji już za chwilę wystawi pierwszy certyfikat “kwalifikowany”. Nie chcę jednak moimi testami wywołać zbyt dużego zamieszania ani tym bardziej naruszyć bezpieczeństwa informacji. Fałszywy certyfikat będzie więc wystawiony na moje prawdziwe dane osobowe. Jeśli atak się powiedzie, to zaloguję się na swoje własne konto, do którego i tak miałbym prawowity dostęp.
Warto tutaj wspomnieć, że jako atakujący mogłem oczywiście wystawić taki certyfikat na dane dowolnej osoby – potrzebowałem jedynie znać jej imię, nazwisko i numer PESEL. Przy okazji dodam, że mechanizm “zastrzeżenia PESEL-u” nie ma tutaj żadnego zastosowania – powstał on w innym celu, a dokumenty można podpisywać elektronicznie, nawet mając aktywne zastrzeżenie.
Co na to system e-Gate?
Początkowo był nieporuszony, bo oprogramowanie w ogólne “nie widziało” mojego fałszywego certyfikatu i nie pozwalało go wybrać.
Pomogła zmiana dynamicznej konfiguracji, która była ładowana po stronie klienta:
- dopisanie moich fałszywych certyfikatów do listy zaufanych CA,
- ustawienie, że dopuszczalne jest złożenie podpisu z użyciem dowolnego certyfikatu (niekoniecznie uznawanego za kwalifikowany).
Tym razem udało się podjąć próbę zalogowania, chociaż bezskuteczną, to już z innym komunikatem błędu niż wcześniej.

Wyświetlany komunikat był dosyć specyficzny, więc zacząłem szukać, czy to nie jest przypadkiem rezultat weryfikacji z jakiegoś konkretnego programu. Nie myliłem się, desktopowy program Szafir 2.0.0 na identyczny plik XML reaguje błędem o identycznej treści.

Software zachował się bezpiecznie, ale to jeszcze nie jest moje ostatnie słowo.
Skupmy się na rozwiązaniu konkretnego problemu. Oprogramowanie do weryfikacji podpisów twierdzi, że mojego fałszywego certyfikatu “intermediate CA” nie ma w swojej wewnętrznej bazie danych, przez co nie może go przeanalizować i rozstrzygnąć o poprawności podpisu.
Pomoc w rozwiązaniu tego problemu zapewnia oficjalna specyfikacja “Electronic Signatures and Infrastructures (ESI); XAdES Baseline Profile”:
a) The generator shall include the signing certificate as content of ds:KeyInfo/X509Data/X509Certificate element.
b) In order to facilitate path-building, generators should include in the same ds:KeyInfo/X509Data element as in note a) all certificates not available to verifiers that can be used during path building. (…)
Zwyczajowo, podpisany plik zawiera jedynie certyfikat osoby składającej podpis (tzw. leaf certificate), ale specyfikacja twierdzi, że nic nie stoi na przeszkodzie, aby załączyć tam cały łańcuch certyfikatów, uwzględniając nasze “intermediate CA” oraz “root CA”.
Otwieram podpisany dokument XML w notatniku i ordynarnie doklejam do niego tagi <ds:X509Certificate/> z dodatkowymi certyfikatami CA. Ponowna weryfikacja i…

Ups. Co prawda, doklejenie do pliku dodatkowych certyfikatów jest zgodne ze specyfikacją, ale ma to służyć jedynie jako pomoc czy też podpowiedź dla oprogramowania sprawdzającego podpis. Szafir wziął jednak temat zbyt dosłownie i… automatycznie zaimportował te certyfikaty do swojej bazy danych zaufania.
No dobrze, a dlaczego otrzymaliśmy informację, że jest to podpis niekwalifikowany? W bardzo dużym skrócie, weryfikacja techniczna została pomyślnie zaliczona, ale moich podrobionych certyfikatów CA nie ma na unijnej liście dostawców usług zaufania. Tego mechanizmu nie udało mi się nijak ominąć, ale być może wystarczą dotychczasowe osiągnięcia?
Sprawdźmy, co tym razem powie e-Gate…


Wygląda jakby faktycznie zadziałało! System powiedział, że mój podpis jest poprawny, potwierdził moją tożsamość i przekierował mnie dalej, a widoczny na zrzucie ekranu błąd to już jedynie kwestia tego, że nie jestem lekarzem.
Automatyzacja ataku
Atak działa, ale ma jeden zasadniczy minus: tym razem trzeba się bardzo dużo naklikać, aby w odpowiednim momencie przechwycić niektóre zapytania HTTP z/do serwera atakowanej aplikacji i przeprowadzić “w locie” niezbędne modyfikacje, chociażby dokleić dodatkowe certyfikaty do podpisanego pliku XML. Do ręcznego manipulowania zapytaniami HTTP używałem Burp Suite, którego jednak nie jestem zbyt zaawansowanym użytkownikiem, bo skomplikowane ataki zazwyczaj implementuję bezpośrednio w jakimś języku skryptowym.
Na całe szczęście, istnieje jeden komponent, przez który przepływają wszystkie niezbędne informacje, w dodatku nie trzeba go dekompilować, bo jest napisany w JavaScripcie, a kod nie jest zaciemniony i bardzo łatwo go zmodyfikować. Tym komponentem jest oczywiście wtyczka do przeglądarki “Szafir SDK Web”. Spora część portali administracji publicznej oferujących logowanie podpisem kwalifikowanym żąda instalacji właśnie tej wtyczki, a za jej pośrednictwem wywołuje proces podpisywania dokumentów (więcej kontekstu na ten temat znajduje się w poprzednich artykułach z tej serii).
We wtyczce wprowadziłem relatywnie proste modyfikacje – za każdym razem, gdy strona internetowa próbuje za pośrednictwem wtyczki komunikować się z aplikacją do składania podpisów, wysyłam żądanie do mojego serwera w Pythonie, który może zdecydować o zmodyfikowaniu w locie przesyłanych danych. Analogicznie, gdy komunikacja odbywa się w drugą stronę (z “podpisywarki” do aplikacji internetowej).
Będąc tak przygotowanym, mogę wygodnie eksperymentować, modyfikując chociażby parametry wywołania, konfigurację “podpisywarki”, a także zawartość wynikowego pliku XAdES po złożeniu podpisu – bez manualnej interwencji.
Finalne starcie
Pora na ostateczne starcie, a cel będzie niebagatelny: system Zakładu Ubezpieczeń Społecznych. Dlaczego akurat ZUS? Bo to system przechowujący bardzo ważne dane, a podczas wcześniejszych analiz i eksperymentów znalazłem w nim dosyć osobliwą rzecz.

Co prawda, ścieżka ukazana na zrzucie ekranu stanowi jedynie komentarz i w żaden sposób nie wpływa na działanie mechanizmu składania podpisów kwalifikowanych, ale myślę, że sam fakt istnienia tego komentarza w produkcyjnym kodzie wyraża więcej niż tysiąc słów. Heurystyka jest prosta, jeżeli coś wygląda na zrobione “po łebkach” albo niezgodnie z branżowymi standardami, to tym bardziej warto się temu przyjrzeć.
Nie przedłużajmy już więcej, pora przełknąć tę pigułkę…
Co tu się stało?!
Bazując na moich obserwacjach, przypuszczam, że zarówno aplikacje ZUS-u, jak i CEZ-u weryfikują podpisy kwalifikowane, używając do tego programu Szafir, ale pod postacią biblioteki programistycznej (“headless”).
Pomimo że programiści tych systemów zadbali o walidację podpisów, to wpadli w pułapkę wynikającą z istnienia w Szafirze bardzo dziwnej funkcji “certyfikatów niekwalifikowanych”. Aplikacje zwracały uwagę jedynie na to, czy weryfikacja podpisu zakończyła się sukcesem. Okazało się, że to nie wystarczy, bo powinny również osobno sprawdzać, czy była to “weryfikacja kwalifikowana”. Można zatem było system oszukać podpisem technicznie prawidłowym, choć fikcyjnym.
Ten przypadek pokazuje nam, jak ważna jest precyzyjna dokumentacja, a przede wszystkim intuicyjne projektowanie komponentów bezpieczeństwa. Samego zamysłu istnienia “weryfikacji niekwalifikowanej” w Szafirze działającej w taki sposób, mimo najszczerszych chęci, nie jestem w stanie zrozumieć. W domyślnych ustawieniach zawsze da się utworzyć XML, który zweryfikuje się poprawnie, choćby w oparciu o samodzielnie wystawione certyfikaty typu “self-signed”. To dlaczego w ogóle istnieje taka funkcja i co ona weryfikuje? Czy jej istnienie zostało opisane w dokumentacji dla integratorów? Na te pytania niestety nie znam odpowiedzi, chociaż się jej domyślam.
Ba, nawet standard RFC 2459 (specyfikujący infrastrukturę klucza publicznego) wspomina o tym, że od użytkowników końcowych PKI nie można wymagać zwracania szczególnej uwagi ani wykonywania więcej niż minimalnej koniecznej konfiguracji. Nie oszukujmy się, większość ludzi (w tym programistów) nie interesuje się różnego rodzaju infrastrukturami klucza publicznego, nie zna niskopoziomowych detali i słusznie oczekuje, że jest to intuicyjny “black box”, który mogą łatwo wdrożyć, zapewniając sobie i innym bezpieczeństwo. W każdym innym przypadku to bezpieczeństwo nie jest zapewniane systemowo, a przez to ostatecznie pojawiają się błędy o drastycznych skutkach.
Potencjalne skutki
Przypomnijmy, atak w ulepszonej formule pozwolił na zaatakowanie centralnego systemu logowania e-Gate autorstwa Centrum e-Zdrowia, a także systemu eZUS. Ponadto, opisany tutaj atak działa przeciwko wszystkim serwisom, które były wymienione w poprzednim artykule z serii “Badanie e-podpisów”.
Jedynym wymaganiem do przeprowadzenia tego ulepszonego wariantu ataku była znajomość imienia, nazwiska i numeru PESEL ofiary, a ten ostatni można zdobyć niesamowicie łatwo, używając całego wachlarza sposobów, których opis znajduje się poza zakresem tego artykułu. Posiadając zaledwie te trzy informacje, można było wystawić fałszywy certyfikat na tożsamość ofiary, a następnie zalogować się do systemów w jej imieniu.
Co to oznacza w praktyce? W kontekście Centrum e-Zdrowia, jeśli znałbym numer PESEL jakiegoś lekarza posiadającego dostęp do Rejestru Asystentów Medycznych, mógłbym zalogować się w jego imieniu, następnie mianować samego siebie Asystentem Medycznym i… wypisywać recepty, skierowania i zwolnienia lekarskie na odpowiedzialność tego lekarza, bez jego wiedzy.
Dopisanie kogoś do Rejestru Asystentów Medycznych wymaga, co prawda, złożenia podpisanej dyspozycji, ale autoryzację można przeprowadzić Profilem Zaufanym albo… przez system e-Gate, który też udało mi się zhakować w ten sam sposób.
Jeszcze gorzej sprawa wygląda z systemem eZUS. Dostęp do czyjegoś konta oznacza dostęp do szerokiego zakresu jego danych osobowych, w tym jego pełnej historii zatrudnienia, a także umożliwia obliczenie jego pensji – aktualnej i historycznych.
Gdybyśmy natomiast zalogowali się na konto osoby upoważnionej do reprezentacji firmy przed ZUS-em (zapewne księgowego albo prezesa), moglibyśmy w całej firmie wprowadzić nieoczekiwaną jawność wynagrodzeń – pobierając dane i pensje wszystkich pracowników. Istotne jest też, że pracodawca w tym systemie widzi dosyć szeroki zakres danych, bo jest tam chociażby adres zamieszkania, zameldowania, numer PESEL i numer dowodu osobistego każdej osoby zgłoszonej do ubezpieczeń, w tym dane osób wyrejestrowanych nawet na kilka lat wstecz.
No i jeszcze pora na łyżkę miodu w beczce dziegciu – żaden z opisanych ataków nie był skuteczny przeciwko Krajowemu Systemowi e-Faktur (KSeF). Ale to nie jest jedyny system, który przetwarza duże ilości wrażliwych danych, w tym danych przedsiębiorców.
Czy podpisy kwalifikowane są bezpieczne?
Sama w sobie koncepcja podpisów kwalifikowanych stanowi fundament bardzo bezpiecznych rozwiązań, jeżeli mielibyśmy to oceniać pod względem kryptograficznym i faktycznym. Opisane podatności powstały, ponieważ zawiodły konkretne implementacje i integracje.
Prawowici użytkownicy podpisów kwalifikowanych wydanych przez uprawniony podmiot nie muszą podejmować żadnych działań, ponieważ bezpieczeństwo ich podpisów nie zostało naruszone. W szczególności, nie ma konieczności wymiany istniejących urządzeń do podpisu.
Obsługa podatności
Wszystkie odkryte podatności zostały załatane po tym, jak zgłosiłem je do producenta oprogramowania – KIR S.A., a także do zespołu CERT Polska.
Przez mnogość zaangażowanych komponentów i niestandardowość technik, zrozumiałe wytłumaczenie podatności na warstwie technicznej nie było trywialne. W sumie wysłałem w tej sprawie 4 raporty techniczne o łącznej długości 45 stron, nie licząc późniejszych uzupełnień w trakcie komunikacji mailowej.
Incydent był obsługiwany przez CERT Polska, CSIRT GOV oraz Ministerstwo Cyfryzacji. Jako zgłaszający przekazywałem wszystkie informacje bezpośrednio do CERT Polska, który dystrybuował je dalej do pozostałych zaangażowanych organów.
2026-01-23 – Odkrycie pierwszego wariantu podatności (“YubiKey”).
2026-01-24 – Raport do KIR S.A. i CVD CERT.PL.
2026-01-24 – Potwierdzenie otrzymania przez KIR S.A.
2026-01-25 – Odkrycie drugiego wariantu podatności (niezaufane certyfikaty).
2026-01-26 – Raport do KIR S.A. i CVD CERT.PL.
2026-01-26 – Potwierdzenie otrzymania przez KIR S.A. i CVD CERT.PL.
2026-01-27 – Spotkanie osobiste w siedzibie KIR S.A. – techniczne objaśnienie podatności w obu wariantach.
Obsługa podatności – praca.gov.pl
2026-02-19 [T+24 dni] – Logowanie podpisem kwalifikowanym wyłączone, komunikat o tymczasowym wyłączeniu logowania podpisem kwalifikowanym.
2026-03-09 [T+42 dni] – Komunikat o permanentnym wyłączeniu logowania podpisem kwalifikowanym “z przyczyn niezależnych od Ministerstwa”.
Obsługa podatności – ZUS
2026-03-17 [T+50 dni] – Ogłoszenie ZUS o wyłączeniu logowania podpisem od 30 marca 2026 r.
2026-03-31 [T+64 dni] – Usunięcie przycisku do logowania podpisem kwalifikowanym na frontendzie, przy czym API do logowania podpisem kwalifikowanym nadal było dostępne i podatne.
2026-04-01 [T+65 dni] – Wzmianka do CVD CERT.PL o tym, że API w ZUS nadal jest dostępne i podatne (przekazane do CSIRT GOV).
2026-04-15 [T+79 dni] – Kontakt bezpośredni z COT ZUS i przekazanie zgłoszenia, że API nadal jest dostępne i podatne.
2026-04-17 [T+81 dni] – Dostęp do API zablokowany, podatność załatana.
Obsługa podatności – Ministerstwo Sprawiedliwości
2026-03-26 [T+59 dni] – Aktualizacja serwisu obsługującego podpisy kwalifikowane, nadal podatny w pełnym zakresie.
2026-04-15 [T+79 dni] – Kontakt bezpośredni z MS, przekazanie materiałów na temat podatności oraz kodu exploita na Usługę Centralnego Podpisu Elektronicznego Ministerstwa Sprawiedliwości (UCPE MS).
2026-04-24 [T+88 dni] – Załatanie podatności po pracach serwisowych.
Obsługa podatności – Urząd Dozoru Technicznego
2026-04-15 [T+79 dni] – Kontakt bezpośredni z UDT, przekazanie informacji o podatności.
2026-04-20 [T+84 dni] – Załatanie podatności.
Obsługa podatności – Centrum e-Zdrowia
2026-04-14 [T+78 dni] – Kontakt bezpośredni z CeZ, przekazanie materiałów na temat podatności oraz kodu exploita na system e-Gate Centrum e-Zdrowia.
2026-04-29 [T+93 dni] – Wprowadzenie przez CeZ mitygacji, która częściowo załatała podatność.
2026-05-20 [T+113 dni] – Naprawienie podatności po aktualizacji.
Patch do Szafir 2.0 Desktop
Na kilka dni przed publikacją tego artykułu Krajowa Izba Rozliczeniowa wydała aktualizację do oprogramowania Szafir 2.0 Desktop (build 705, data kompilacji 2026-05-18). Po tej poprawce dokumenty z “oszukanymi” certyfikatami nie przechodzą już poprawnie weryfikacji.
Analogiczna aktualizacja została również wprowadzona w bibliotekach Szafir SDK, których systemy administracji publicznej używają do weryfikacji podpisanych dokumentów. Nie jestem w stanie podać konkretnej daty, ale według moich szacunków taka łatka adresująca problem w sposób systemowy została wydana nie wcześniej niż 3 miesiące od mojego zgłoszenia.
CVE-2026-9058
Zespół CERT Polska uznał że konieczność osobnego sprawdzania statusu weryfikacji dokumentu oraz statusu certyfikatu (tj. czy został uznany za autentyczny) stanowi niepotrzebną złożoność mechanizmu zabezpieczeń.
W związku z tym opublikowano wpis o podatności: CVE-2026-9058: Improper Certificate Verification in Szafir SDK (wpis na stronie CERT Polska).
Podsumowanie
To już ostatni artykuł o moim badaniu w obszarze e-podpisów.
Na koniec warto wspomnieć, że opisałem jedynie techniki i pomysły, które okazały się w jakimkolwiek stopniu skuteczne przy atakowaniu rzeczywistych serwisów. Nie opisałem natomiast wielu ślepych uliczek, w które wpadłem podczas procesu odkrywania nowych technik ataku.
Cały projekt można bezpiecznie szacować na co najmniej 100-120 godzin roboczych. Zdecydowaną większość czasu poświęciłem na opracowanie części trzeciej, gdy już wiedziałem, że obrany kierunek jest perspektywiczny. Opisane badanie było finansowane z moich środków prywatnych i nie było realizowane pod niczyim kierunkiem ani zleceniem.
Mam nadzieję, że przekazana tu wiedza pomoże w opracowaniu lepszych procesów w organizacjach, a także lepszych technik testów penetracyjnych, niezwykle potrzebnych w tych skomplikowanych czasach.