Malware w bazie danych — czego skaner plików nigdy nie znajdzie

Scenariusz, który wraca regularnie: ktoś wyczyścił pliki, porównał rdzeń z wydaniem, usunął web‑shelle, a strona dalej wyświetla obce reklamy albo przekierowuje z telefonu. Pliki są czyste, bo infekcja nie jest w plikach. Jest w bazie danych, a większość skanerów bazy w ogóle nie czyta.
Dlaczego atakujący wybierają bazę
Trzy powody. Po pierwsze, skanery plików (także te hostingowe) tego nie widzą, więc infekcja żyje dłużej. Po drugie, treść z bazy jest wyświetlana na każdej podstronie bez potrzeby modyfikowania kodu. Po trzecie, nawet pełna reinstalacja CMS‑a z czystych plików nie pomaga, bo baza zostaje ta sama.
Gdzie szukać w WordPressie
wp_options
To najważniejsze miejsce. Sprawdź:
siteurlihome— zmienione na obcą domenę oznaczają przekierowanie całej strony;active_plugins— wtyczki, których nie ma w panelu, a są aktywne;- opcje widżetów (
widget_text,widget_custom_html) — doklejone<script>lub<iframe>; - opcje motywu i wtyczek typu „wstaw kod w nagłówku” — tam atakujący wkleja skrypt, bo wtyczka legalnie go wyświetli na każdej stronie;
- dowolne opcje o losowych nazwach z długą wartością zaczynającą się od
a:lubeval.
SELECT option_name, LEFT(option_value, 120)
FROM wp_options
WHERE option_value LIKE '%<script%'
OR option_value LIKE '%eval(%'
OR option_value LIKE '%base64_%'
OR option_value LIKE '%fromCharCode%';
wp_posts
Skrypt doklejony do treści każdego wpisu, strony albo produktu:
SELECT ID, post_title, post_type
FROM wp_posts
WHERE post_content LIKE '%<script%'
OR post_content LIKE '%<iframe%'
OR post_content LIKE '%display:none%';
Część trafień będzie uzasadniona (osadzone wideo, własne skrypty). Zwracaj uwagę na te same fragmenty powtórzone w setkach wpisów i na domeny, których nie znasz.
wp_users i wp_usermeta
SELECT ID, user_login, user_email, user_registered FROM wp_users ORDER BY user_registered DESC LIMIT 20;
Konta administratorów założone w nocy, z adresami na losowych domenach. Sprawdź też wp_usermeta dla klucza wp_capabilities — użytkownik może mieć login „subscriber”, a uprawnienia administratora.
Zaplanowane zadania
W wp_options klucz cron. Zadania o nieznanych nazwach, uruchamiane co godzinę, to częsty sposób na ponowne pobieranie infekcji po jej usunięciu.
Gdzie szukać w Joomli
Prefiks tabel jest losowy (np. abc12_), znajdziesz go w configuration.php jako $dbprefix.
_modules— moduł typumod_custom(Custom HTML) z<script>w kolumniecontent, przypisany do wszystkich stron, często w pozycji szablonu, która nie jest widoczna;_extensions— kolumnaparamsicustom_dataz zaciemnionym kodem; rozszerzenia włączone, których nie instalowałeś;_template_styles— parametry szablonu, w tym pola na własny kod;_content— kolumnaintrotextifulltextz doklejonym skryptem;_usersi_user_usergroup_map— obce konta w grupie Super Users (group_id = 8w domyślnej instalacji);_session— aktywne sesje obcych użytkowników.
SELECT id, title, position, LEFT(content, 120)
FROM abc12_modules
WHERE content LIKE '%<script%' OR content LIKE '%<iframe%';
Jak usunąć bez kasowania treści
Nie kasuj rekordów. Doklejony skrypt jest fragmentem wartości, a reszta wartości to Twoja treść. Zrób kopię bazy, a potem:
- Dla pojedynczych rekordów (opcje, moduły) popraw wartość ręcznie w phpMyAdmin albo w panelu CMS‑a.
- Dla setek wpisów z identycznym fragmentem użyj
REPLACE, ale najpierw sprawdź dokładną treść fragmentu, bo spacja różnicy sprawi, że część zostanie:
UPDATE wp_posts
SET post_content = REPLACE(post_content, '<script src="https://obca-domena/x.js"></script>', '')
WHERE post_content LIKE '%obca-domena/x.js%';
- Uwaga na wartości serializowane (
a:3:{s:5:"title";...}) wwp_optionsi_extensions.params. Zawierają długości łańcuchów. Jeśli usuniesz fragment ręcznie, długość przestanie się zgadzać i CMS odrzuci całą wartość. Takie rekordy poprawiaj z poziomu panelu CMS‑a (który zapisze je poprawnie) albo narzędziem, które rozumie serializację. - Usuń obce konta i zmień hasła pozostałych administratorów.
- Wyczyść cache CMS‑a i wtyczek, bo stara treść ze skryptem może być zapisana w cache.
Jak sprawdzić, czy to koniec
Pobierz stronę jako telefon z Google i jako Googlebot (komendy w naszych wpisach o przekierowaniach i spamie SEO). Jeśli odpowiedź jest czysta, powtórz zapytania SQL po tygodniu. Jeśli fragment wrócił, w plikach zostało coś, co go dopisuje, zwykle zaplanowane zadanie albo wtyczka w mu-plugins.
Skan CleanOps obejmuje pliki, nie bazę. Bazę sprawdzisz po naprawie z asystentem AI w panelu: ma to samo połączenie z serwerem co skan, dane do bazy odczyta z pliku konfiguracyjnego, a każde zapytanie, które coś zmienia, wykona dopiero po Twoim zatwierdzeniu. Zapytania z tego wpisu możesz mu wkleić wprost.


