Krótko: Aby znaleźć nieznane klucze SSH, trzeba sprawdzić wszystkie pliki authorized_keys na serwerze, a nie tylko plik roota. Poleceniem sshd -T proszę ustalić, jakie pliki czyta Państwa usługa SSH, wypisać odciski palca każdego klucza i porównać je ze znanymi kluczami. Obce wpisy najpierw zabezpieczyć, potem usunąć, a następnie utwardzić dostęp.
Klucz, który przetrwa każdą zmianę hasła
Po incydencie wielu operatorów jako pierwsze zmienia wszystkie hasła. Przeciw jednej z najmniej rzucających się w oczy tylnych furtek to nie pomaga: dodatkowemu kluczowi publicznemu w pliku authorized_keys. Kto posiada pasujący klucz prywatny, loguje się bez hasła, tak długo, aż ktoś znajdzie ten wiersz.
Nieznane klucze SSH należą więc do oznak, które po wykonaniu kodu zawsze należy sprawdzić. Dla PHP2Shell nie ma opublikowanych, potwierdzonych wskaźników kompromitacji (Indicators of Compromise); wykrywanie opiera się na analizie zachowania. Obcy klucz nie jest swoistym dowodem PHP2Shell, ale jasnym znakiem, że ktoś chciał mieć trwały dostęp. Dalsze oznaki opisuje Czy już zostałem skompromitowany?.
Gdzie na serwerze leżą klucze SSH
OpenSSH czyta według dokumentacji domyślnie dwa pliki w katalogu domowym każdego użytkownika: .ssh/authorized_keys i .ssh/authorized_keys2. Drugi bywa zapominany. To ustawienie można zmienić, a przez AuthorizedKeysCommand serwer może nawet pobierać klucze z zewnętrznego programu. Dlatego proszę najpierw sprawdzić, co faktycznie obowiązuje na Państwa serwerze:
# Wyświetlić efektywną konfigurację SSH (tylko odczyt)
sudo sshd -T | grep -iE '^(authorizedkeysfile|authorizedkeyscommand|permitrootlogin|passwordauthentication|pubkeyauthentication)'
# Dodatkowe pliki konfiguracyjne i bloki Match
ls -la /etc/ssh/sshd_config.d/ 2>/dev/null
sudo grep -rn -iE '^\s*(Match|AuthorizedKeys)' /etc/ssh/
Szczególnym miejscem jest katalog domowy użytkownika serwera WWW. W Debianie i Ubuntu jest to dla www-data często /var/www. Jeśli ten katalog jest zapisywalny dla PHP, wprowadzony kod może tam założyć .ssh/authorized_keys. To, czy logowanie się wtedy uda, zależy od powłoki logowania i ustawień SSH, ale sam plik jest już sygnałem alarmowym.
Nie każdy nieznany wpis jest złośliwy. Narzędzia wdrożeniowe, usługi kopii zapasowych lub dawni usługodawcy zapisują własne klucze. Takie wpisy powinny jednak mieć znanego właściciela i cel.
Sprawdzanie authorized_keys krok po kroku
Poniższe polecenia niczego nie zmieniają. Wypisują wszystkie pliki kluczy użytkowników i pokazują odcisk palca każdego klucza:
# Wszystkie pliki authorized_keys w katalogach domowych wszystkich użytkowników
getent passwd | cut -d: -f1,6 | while IFS=: read -r user home; do
for f in "$home/.ssh/authorized_keys" "$home/.ssh/authorized_keys2"; do
[ -f "$f" ] && echo "== $user: $f" && sudo ls -la --time-style=full-iso "$f"
done
done
# Wyświetlić odciski palca i komentarze pliku
sudo ssh-keygen -lf /root/.ssh/authorized_keys
# Dalsze pliki kluczy poza zwykłymi miejscami
sudo find / -xdev -name 'authorized_keys*' 2>/dev/null
Wynik proszę przejść według tej listy kontrolnej:
- Czy każdy klucz należy do znanej osoby lub znanej usługi? Proszę porównać odciski palca, a nie tylko komentarz.
- Czy użytkownik ma klucze, których w ogóle nie powinien mieć? Na przykład użytkownik serwera WWW, bazy danych lub konta usługowe.
- Czy przed kluczem stoją opcje? Wpisy z
command=wykonują przy każdym logowaniu stałe polecenie. Jest to uzasadnione dla kopii zapasowych, ale także kryjówka. - Kiedy plik został ostatnio zmieniony? Data zmiany po 17.10.2026, czyli pierwszej, według doniesień mediów, zaobserwowanej próbie ataku PHP2Shell, zasługuje na szczególną uwagę. Znaczniki czasu można jednak sfałszować.
- Czy właściciel i uprawnienia się zgadzają? Plik, który należy do innego użytkownika niż katalog domowy, rzuca się w oczy.
Bezpieczne usuwanie obcych kluczy
Zanim usuną Państwo wiersz, proszę zabezpieczyć plik ze znacznikiem czasu i sumą kontrolną poza serwerem. Proszę zanotować odcisk palca obcego klucza. Pomoże później w pytaniu, czy ten sam klucz pojawia się także na innych serwerach, na przykład u agencji z wieloma systemami klientów.
Proszę też sprawdzić protokoły logowania. W /var/log/auth.log (Debian, Ubuntu) lub /var/log/secure (rodzina RHEL) usługa SSH protokołuje udane logowania z odciskiem palca użytego klucza. Dzięki temu widać, czy obcy klucz był już używany i z jakiego adresu.
Proszę pomyśleć także o drugim kierunku. Jeśli na serwerze leżą klucze prywatne, na przykład pod ~/.ssh/id_* dla wdrożeń lub kopii zapasowych, atakujący mógł je skopiować. Za ich pomocą może dotrzeć do innych serwerów, repozytoriów Git lub celów kopii zapasowych. Takie klucze uchodzą po włamaniu za spalone: wygenerować nowe klucze i usunąć stare na wszystkich systemach docelowych.
Obcy klucz rzadko jest jedynym śladem. Kto go zapisał, miał uprawnienia zapisu i być może urządził dalsze drogi, na przykład zadania cron lub dodatkowe konta. Proszę więc sprawdzić także ukryte zadania cron i obce konta administratora. Jeśli atakujący miał uprawnienia roota, budowa serwera od nowa jest często bardziej wiarygodna niż oczyszczanie.
Obce klucze proszę usuwać dopiero wtedy, gdy PHP jest w poprawionym stanie (8.5.12, 8.4.27, 8.3.36, 8.2.35 lub potwierdzony backport), a wszystkie workery zostały zrestartowane. W przeciwnym razie atakujący może przez tę samą lukę od razu wpisać klucz ponownie.
Utwardzenie dostępu SSH po fakcie
Po oczyszczeniu opłaca się krótkie utwardzenie. Większość punktów wprowadza się szybko, ale zawsze należy je testować z drugą, otwartą sesją, aby się nie zablokować.
| Środek | Ustawienie lub postępowanie |
|---|---|
| Logowanie tylko kluczem | PasswordAuthentication no |
| Nie pozwalać na bezpośrednie logowanie roota | PermitRootLogin no, praca przez konta osobiste z sudo |
| Wyłączyć drugi plik kluczy | AuthorizedKeysFile .ssh/authorized_keys |
| Ograniczyć klucz do źródła | opcja from= przed kluczem, np. dla serwera kopii zapasowych |
| Ograniczyć klucze usługowe | opcja restrict i stałe command= dla automatyzacji |
| Użytkownik serwera WWW bez logowania | powłoka logowania nologin, brak .ssh w katalogu domowym |
Proszę ponadto prowadzić listę wszystkich uprawnionych kluczy z właścicielem i celem. Jeśli pracownik lub usługodawca odchodzi, jego klucz jest usuwany na wszystkich serwerach. Bez tej listy następna kontrola znów jest pracą detektywistyczną. Uznane wytyczne utwardzania SSH zawierają także CIS Benchmarks.
Jak konkretnie pomaga PHP SOS?
- Ustalamy, jakie pliki kluczy faktycznie czyta Państwa usługa SSH, i sprawdzamy każdy wpis względem znanych właścicieli.
- Zabezpieczamy znaleziska i analizujemy protokoły logowania, aby wyjaśnić, czy obcy klucz był używany.
- Oczyszczamy klucze, konta i zadania cron we właściwej kolejności i wcześniej zamykamy lukę.
- W ramach utwardzania serwerów wprowadzamy ustawienia SSH i konfigurujemy zarządzanie kluczami z listą właścicieli.
Częste pytania
Po czym poznać, że klucz SSH jest obcy?
Każdy klucz ma odcisk palca i zwykle komentarz na końcu wiersza. Odciski proszę porównać z kluczami pracowników i usługodawców. Sam komentarz niczego nie dowodzi, ponieważ można go dowolnie wybrać i sfałszować.
Czy zmiana hasła roota pomaga?
Nie przeciw wprowadzonemu kluczowi. Logowanie kluczem nie potrzebuje hasła. Dlatego same pliki authorized_keys muszą zostać sprawdzone i oczyszczone.
Dlaczego użytkownik serwera WWW może mieć klucz SSH?
Użytkownik serwera WWW ma własny katalog domowy, w Debianie i Ubuntu często /var/www. Jeśli leży tam .ssh/authorized_keys, a konto ma powłokę logowania, możliwe jest logowanie jako ten użytkownik. Przy zapisywalnym katalogu WWW wprowadzony kod PHP może taki plik założyć.
Czym jest authorized_keys2?
Starszy drugi plik, który OpenSSH według ustawień domyślnych również czyta. Wiele instrukcji kontroli go pomija. Poleceniem sshd -T proszę sprawdzić, które pliki Państwa serwer faktycznie analizuje.