Daty plików kłamią. Jak ustalić, kiedy naprawdę ktoś wszedł

W piątek wieczorem otworzyłem katalog jednego z klientów i zobaczyłem plik PHP z datą modyfikacji sprzed roku. Wyglądał jak stały element strony, coś, co siedzi tam od zawsze. Tyle że tego pliku na pewno nie było tam tydzień wcześniej, bo tydzień wcześniej ten sam katalog przeglądałem osobiście i pamiętam listę do trzeciego rzędu. Coś się tu nie zgadzało, i nie chodziło o mnie.

To jest cała pułapka forensyki w małej firmie po incydencie. Kiedy dzwoni klient z pytaniem „od kiedy to jest u nas?", pierwsza rzecz, po którą sięga większość ludzi, to data modyfikacji pliku. I to jest dokładnie ta odpowiedź, której udziela włamywacz - bo datę pliku umie zmienić dosłownie każdy, a robi to jednym poleceniem. Prowadzę agencję AI i automatyzacji dla MŚP - wszystko, czego uczę klientów, najpierw testuję na sobie i na własnej infrastrukturze. Ostatnie tygodnie na naszych serwerach były w tym sensie kosztowną nauką. Opowiem, jak od tego czasu ustalam, kiedy naprawdę ktoś wszedł do systemu, i dlaczego prawdziwa chronologia prawie nigdy nie mieszka w datach plików.

Spis treści

Dlaczego data pliku jest najsłabszym dowodem?

Kiedy jesteś nietechniczny, data pliku brzmi jak coś twardego. Jest zapisana obok nazwy, widać ją w każdym menedżerze plików, w Windowsie, na hostingu, w FTP. Wygląda dokładnie jak metryczka - kiedy powstała, kiedy była zmieniona. Ludzki mózg traktuje ją jak stempel z urzędu.

W rzeczywistości data pliku jest atrybutem, który system operacyjny grzecznie pokazuje temu, kto zapyta - i grzecznie zapisuje to, co każe mu wpisać właściciel. „Właścicielem" na przejętym serwerze bywa niestety ten, kto się właśnie włamał. Wystarczy jedno polecenie z linii komend, `touch -t 202508081200 plik.php`, i plik dostaje datę 8 sierpnia 2025, dwunasta w południe. Bez śladu. Bez pytania. Niezależnie od tego, kiedy naprawdę powstał.

Dlatego, kiedy ktoś dzwoni z pytaniem „od kiedy to u nas jest?", ja już nie patrzę na datę pliku. Patrzę na coś innego, i za chwilę to opiszę. Ale najpierw jedno wyznanie - też się na tym przejechałem. Kilka lat temu na innej infrastrukturze przyjąłem, że skoro plik ma datę sprzed dwóch lat, to musi być z tego czasu, i tłumaczyłem klientowi, że to „stara sprawa". Nie była. Była z zeszłego tygodnia. Nauczyłem się tego twardo i chętnie oszczędzę ci tego samego kosztu.

Cztery znaczniki czasu, a tylko jeden mówi to, o czym myślisz

W systemie Linux każdy plik ma cztery czasy, nie jeden. Warto o nich wiedzieć, bo różnią się tym, kto i jak łatwo je zmienia. Wytłumaczę to jak analogię, bo bez analogii to techniczna zupa.

Wyobraź sobie zeszyt szkolny. Data modyfikacji (mtime) to data ostatniego wpisu do zeszytu - kiedy dopisałeś ostatnie zdanie. Data dostępu (atime) to kiedy ktoś zeszyt otworzył, choćby na sekundę, choćby na chwilę spojrzał. Data zmiany (ctime) to kiedy zmieniła się okładka albo naklejka na spisie treści - metadane wokół zeszytu, nie sama treść. I wreszcie data urodzenia (birthtime, w niektórych systemach dostępna, w niektórych nie) - kiedy zeszyt kupiłeś, moment jego powstania.

Kluczowa różnica dla forensyki: mtime i atime da się dowolnie ustawić z poziomu użytkownika, ctime jest twardsza - żeby ją cofnąć, potrzeba już zabawy z jądrem systemu albo pełnych praw roota z modyfikacją stanu dysku, a birthtime na wielu popularnych systemach plików w ogóle nie da się przestawić standardowymi narzędziami. Włamywacz wchodzący przez lukę w aplikacji ma zazwyczaj tyle praw, ile ma aplikacja - i one wystarczają na `touch`, ale nie zawsze wystarczają na resztę. Dlatego, jeśli już patrzę na czasy pliku, patrzę na cały komplet, a nie na to, co pokazuje FTP.

Praktycznie sprawdzenie zajmuje sekundę - w konsoli polecenie `stat plik.php` wypluwa wszystkie cztery czasy naraz. Kiedy widzę, że mtime jest z zeszłego roku, a ctime z ostatniego tygodnia, wiem, że ktoś kombinował, i wiem mniej więcej kiedy. Bo ctime pokazuje, kiedy realnie ktoś ten plik dotknął, choćby po to, żeby jego mtime cofnąć.

Jak cofa się datę? Prościej, niż myślisz

Nie chcę nikogo uczyć włamywania się, więc powiem tylko tyle, ile trzeba, żeby zrozumieć, po czym rozpoznaję sfałszowaną chronologię.

Standardowa procedura po przejęciu strony wygląda tak: sprawca wgrywa swój plik z kodem, który daje mu dostęp - tak zwany webshell. Zaraz potem ustawia jego datę modyfikacji na dawną, najlepiej na czas, w którym coś legalnie się w tym katalogu zmieniało - datę aktualizacji WordPressa, datę wgrania motywu, cokolwiek, co uwiarygadnia obecność pliku. Sprytniejsi kopiują nazwy pasujące do środowiska, na przykład `wp-cache.php` obok prawdziwego `wp-load.php`, żeby na liście plików nic nie krzyczało. To sekundy roboty.

Widziałem to na naszych serwerach w konkretnej postaci. Backdoory z metryczką z sierpnia poprzedniego roku, siedzące w katalogach obok normalnych plików WordPressa. Ktoś, kto szybko rzuci okiem, przewinie listę bez zatrzymania - „aha, stare pliki". Dopiero kiedy weźmiesz każdy z osobna i zapytasz system o pełen komplet czasów, widać, że mtime jest wykreowany. Ctime tego samego pliku bywa świeży, sprzed kilku dni. To jest dokładnie ten moment, w którym data pliku przestaje być dowodem i staje się przynętą.

Uczciwie: sprawcy z wyższej półki potrafią więcej. Potrafią wgrać plik z zachowaniem czasów z otoczenia i tak zsynchronizować metadane, że nawet ctime wygląda spójnie. Ale to już rzadkość, i to nie w atakach masowych - w cichych, celowanych, kosztownych operacjach. Przeciętne włamanie na sklep czy stronę WordPress to nie jest ta liga. Tam wystarczy przyjrzeć się plikom uważnie, i widać, gdzie kończy się prawda, a zaczyna kosmetyka.

Prawdziwa chronologia mieszka w kopiach zapasowych

Kiedy nie ufam datom plików, gdzie szukam prawdy? W drugim egzemplarzu tego samego systemu z innego momentu w czasie. Czyli w kopii zapasowej.

Kopia zapasowa jest jak zdjęcie. Sfotografowałeś katalog dwudziestego pierwszego lipca, siódmego sierpnia i dziesiątego sierpnia. Jeśli plik `wp-cache.php` nie ma go na zdjęciu z dwudziestego pierwszego, jest na zdjęciu z siódmego, a jego rzekome mtime pokazuje sierpień poprzedniego roku - to nie mtime kłamie tylko trochę. Kłamie fundamentalnie. Prawdziwy moment pojawienia się tego pliku mieści się między dwoma zdjęciami, i wtedy dokładnie wiadomo, na jakim odcinku czasu ktoś się dostał.

To brzmi prosto, ale ma jeden warunek, na którym potyka się większość małych firm - kopie zapasowe muszą naprawdę istnieć i muszą być z różnych momentów. Nie jedna kopia sprzed miesiąca. Nie kopia, której nikt nigdy nie próbował odczytać. Kopia zapasowa, której nikt nie otworzył, nie jest kopią zapasową - to zdanie powtarzam klientom przy każdej ofercie na opiekę nad stroną. Kopia jest tyle warta, ile weryfikacja odczytem.

W naszym przypadku łapaliśmy się na tym, że mieliśmy kopie, ale w oknie od drugiego do dziesiątego sierpnia była luka, której nie zauważyliśmy przez ładny kawałek czasu. To znaczyło, że dla części plików nie potrafiliśmy powiedzieć, czy wpadły trzeciego, piątego czy siódmego - a to różnica w interpretacji ataku. Bardzo szybko zamknęliśmy to inaczej: krótszy interwał między kopiami, przechowywanie w drugim miejscu i cotygodniowe otwarcie losowej kopii i sprawdzenie, że pliki się w niej otwierają. Trzy proste zasady, ale każda z nich powstała jako lekcja po własnym błędzie.

Dokładnie tę samą lekcję opisałem osobno, jeśli interesuje cię strona kopii, a nie sama forensyka - napisałem tam, jak weryfikuję punkty przywracania odczytem, a nie tylko istnieniem pliku. Reguła jest ta sama: „istnieje" i „działa" to dwie różne rzeczy.

Baza danych pamięta lepiej niż pliki

Systemy typu WordPress mają jedną cechę, która niesamowicie pomaga w forensyce, jeśli się o niej wie: prawie wszystko dzieje się w bazie danych, a nie w plikach. I baza danych ma pamięć bardzo trudną do sfałszowania w sposób niezauważalny.

Weź konto administratora. W tabeli `wp_users` każde konto ma numer, i numer ten rośnie liniowo - konto z numerem 42 powstało po koncie 41, a przed 43. Nawet jeśli sprawca podmieni datę rejestracji (`user_registered`), numer w bazie zdradzi kolejność. Kiedy widzę fałszywe konto administratora z numerem sto dwa, a normalne konta użytkowników kończą się na dziewięćdziesiątym trzecim - wiem, że doszło do niego po dziewięćdziesiątym trzecim. I mogę spojrzeć w kopię zapasową bazy z zeszłego tygodnia, żeby zobaczyć, czy tego numeru wtedy jeszcze nie było.

Podobnie z zawartością tabeli `wp_options` - tam czasem siedzą wpisy podszywające się pod systemowe, o nazwach udających funkcje aktualizacji („site_transient_health_" i losowe znaki). Tam też jest autoinkrementalny identyfikator, który zdradza kolejność. Analogicznie w tabeli wpisów, komentarzy, wszystkiego, co ma numerację - kolejność jest twarda i włamywacz musiałby ingerować w sekwencje bazy, żeby ją zmienić, a to zostawia trwały ślad w innym miejscu.

Baza jest też dużo mniejsza niż pliki i łatwiejsza do porównania z kopią. Trzymanie codziennych kopii bazy danych to znikomy koszt, a różnica między kopią z wczoraj a stanem dzisiaj potrafi w minutę pokazać, kto wszedł i kiedy. Ilekroć doradzam małej firmie, od czego zacząć zabezpieczenia, po prostym haśle i dwuskładniku pierwsza rzecz to codzienny zrzut bazy i tygodniowa próba jego przywrócenia w środowisku testowym. Nie potrzebujesz do tego korporacyjnego systemu backupu. Potrzebujesz dyscypliny na jedno polecenie dziennie.

Logi serwera masz tylko przez chwilę

Logi serwera www to jak nagrania z monitoringu w sklepie. Wszystko, co się stało, jest tam zapisane - kto skąd wszedł, co pobrał, o której godzinie, z jakiej przeglądarki. Włącznie z żądaniem wgrania backdoora. Tyle że logi mają tę wredną właściwość, że domyślnie kasują się po pewnym czasie. Standard w wielu konfiguracjach to dziesięć dni. Miesiąc bywa rzadko. Rok - praktycznie nigdy, chyba że ktoś specjalnie to skonfigurował.

Kiedy klient dzwoni „coś dziwnego się dzieje", a ostatnie backupy pokazują, że atak zaczął się trzy tygodnie temu, logów już nie ma. Widzisz tylko ostatnie dziesięć dni, w których atakujący był już w środku i zachowywał się normalnie. Wektor wejścia - czyli od czego się to zaczęło - został skasowany razem z resztą starych logów. To boli, bo bez wektora wejścia nie wiesz, przez którą lukę wszedł, a bez tej wiedzy trudno załatać dziurę na dobre.

Wnioski są proste, ale nikt ich nie wyciąga, dopóki nie stanie się coś przykrego. Wydłuż retencję logów, zwłaszcza dostępowych do panelu administracyjnego i wszelkich uploadów. Kopiuj logi do drugiego miejsca, bo kompromitacja serwera oznacza kompromitację logów - włamywacz nie zostawia śladów, jeśli może je skasować. I przyjmij, że logi z tego samego serwera, na który się włamano, mają wartość ograniczoną - trzeba mieć drugą kopię gdzieś, gdzie sprawca nie sięga.

Robimy to teraz z automatu: logi z każdego serwera wędrują do osobnej maszyny w innej sieci, z retencją miesięcy, nie dni. Koszt symboliczny, wartość - w momencie, kiedy potrzeba - trudna do przecenienia. Klienci tego nie widzą, dopóki nie zdarzy się incydent. Wtedy nagle rozumieją, dlaczego pobieramy za to grosze miesięcznie.

Kroki, które robię, gdy trafiam na taki plik

Jeśli miałbym ułożyć to w kolejność, wygląda ona u mnie tak, gdy podejrzewam włamanie:

1. Nie ufaj temu, co widzisz w FTP. Data pokazana w kliencie FTP to wyłącznie mtime, a wiesz już, że mtime kłamie. Zaloguj się przez SSH i uruchom `stat` na podejrzanym pliku, żeby zobaczyć wszystkie czasy naraz. Jeśli ctime jest świeży, a mtime stary - masz podejrzanego. 2. Sięgnij po ostatnie kopie zapasowe. Sprawdź, w której kopii plik pierwszy raz się pojawił. To wąski przedział czasu, w którym atak się wydarzył, i to jest dokładnie to, czego szukasz. 3. Wejdź do bazy danych i szukaj identyfikatorów. Nowe konta administratora z wysokim numerem, wpisy w tabeli opcji podszywające się pod systemowe, komentarze bez treści z linkami. Baza jest odporniejsza na fałszowanie niż pliki. 4. Zejdź do logów i szukaj w oknie, które wskazała ci baza. Jeśli baza mówi, że nowe konto pojawiło się między pierwszym a trzecim sierpnia, otwórz logi z tego okresu i szukaj podejrzanego adresu IP, dziwnych żądań POST, wgrywania plików. Wektor wejścia zwykle stoi tam, gdzie ktoś klepał to samo miejsce parę razy z rzędu. 5. Sprawdź, co jeszcze pojawiło się w tym samym oknie. Włamywacze rzadko zostawiają tylko jeden plik. Robią sieć - jeden do dostępu, drugi jako zapasowy, trzeci jako pułapka na tego, kto sprząta. Jeśli znalazłeś jeden podejrzany plik, są szanse, że są jeszcze cztery, i wszystkie mają podobny cień w metadanych.

Ten schemat da się przeprowadzić w pół godziny na serwerze, do którego masz dostęp. Nie potrzebujesz specjalistycznego oprogramowania. Potrzebujesz kopii zapasowych z różnych momentów, dostępu do bazy i wglądu w logi. Każda z tych trzech rzeczy jest tania. Każda z nich uratuje ci firmę w momencie, w którym będziesz jej potrzebował, a nikt nigdy nie żałuje, że je ma - żałuje się dopiero, kiedy ich zabraknie.

Czego nauczyły mnie te dwa tygodnie

Ostatnie tygodnie na naszej infrastrukturze były kosztowne, ale przynajmniej pouczające. Nie chcę udawać, że wyszedłem z tego bez zadrapań, bo wyszedłem z paroma i było widać, że mieliśmy szczęście, a nie wyłącznie procedury.

Pierwsza lekcja - stron bez opieki się nie da bronić. Konta, które od dawna nie miały aktualizacji, luk nie łata się na żądanie po fakcie. To trzeba robić regularnie, w tle, i to musi mieć swoje miejsce w budżecie klienta. Mieliśmy w portfelu serwisy z aktywnymi umowami na opiekę i takie bez. Wszystkie te bez padły. Wszystkie te z opieką - nie padły. To nie zbieg okoliczności.

Druga lekcja - backupy trzeba weryfikować odczytem, nie samym istnieniem pliku. Sprawdziliśmy, gdzie mamy luki w kopiach, i w kilku miejscach znaleźliśmy przerwy, o których nie mieliśmy pojęcia. Teraz raz w tygodniu automat losuje kopię i próbuje ją otworzyć - jeśli się nie da, dostajemy alert. To rzecz, o której lubię pisać, bo zajmuje pół dnia na wdrożenie i chroni firmę przed sytuacją, w której wychodzi, że mamy „backup", ale nikt tego nigdy nie sprawdził. Więcej o samej zasadzie: jak zabezpieczam infrastrukturę klientów w małej firmie - tam pisałem o awarii, ale wnioski o kopiach są identyczne.

Trzecia lekcja, i chyba najważniejsza - daty plików to nie dowód, tylko pomocnicza wskazówka. W momencie, kiedy komuś zaufałem, że to co widzi w FTP, to prawda, pomyliłem się. Prawda mieszka w porównaniu kilku źródeł - kopii, bazy, logów - i dopiero z ich zbieżności wychodzi rzeczywisty obraz. Jeden dowód to nie dowód. Trzy niezależne, które się zgadzają - to dopiero coś, na czym można oprzeć rozmowę z klientem.

I jeszcze coś, co nie pasuje do żadnej z tych trzech lekcji, ale wisi nade wszystkim - spokój ma cenę. Ktoś, kto włamał się na stronę, chce, żebyś działał pod presją, w panice, kasował dowody własnymi rękami, żeby „przywrócić działanie jak najszybciej". Jeśli klient ma stronę, na której coś się dzieje, pierwszym odruchem prawie zawsze jest „usuń wszystko, wgraj świeże", i to jest odruch, który likwiduje ślady. Zanim usuniesz cokolwiek, zrób obraz stanu, choćby zwykłe skopiowanie całości do innego katalogu z inną nazwą. Ten obraz to twoja jedyna szansa na odpowiedź „od kiedy?" i „jak?". Bez niego zostaje tylko wróżenie.

Słownik pojęć

  • mtime (data modyfikacji) - kiedy ostatnio zmieniła się zawartość pliku. Dowolnie ustawialna z poziomu użytkownika jednym poleceniem, dlatego w forensyce ma niską wartość dowodową.
  • ctime (data zmiany) - kiedy ostatnio zmieniły się metadane pliku, w tym jego mtime. Twardsza od mtime, bo do jej podrobienia trzeba zabawy z jądrem systemu.
  • atime (data dostępu) - kiedy plik był ostatni raz otwarty do odczytu. Rzadko przydatne w praktyce, bo na wielu serwerach jest wyłączone dla wydajności.
  • birthtime (data urodzenia) - kiedy plik powstał. Nie na każdym systemie plików dostępna; tam, gdzie jest, bywa najtrudniejsza do zmiany standardowymi narzędziami.
  • webshell - plik wgrany na serwer, który daje włamywaczowi zdalny dostęp do wykonywania poleceń. W klasycznym scenariuszu wgląda tak samo jak inne pliki aplikacji, a jego rozpoznanie to główna praca po incydencie.
  • wektor wejścia - konkretna luka albo droga, którą sprawca dostał się do systemu. Bez znajomości wektora wejścia zatkanie dziury jest zgadywaniem, dlatego tak ważne są długie logi z dostępów.
  • retencja logów - jak długo serwer trzyma stare wpisy w logach, zanim je skasuje. Domyślnie kilka dni; realistycznie dla śledzenia incydentów potrzeba miesięcy, i to najlepiej na drugiej maszynie.

Jeśli w twojej firmie coś ci nie pasuje

Jeśli ten tekst czytasz dlatego, że masz w firmie sytuację, w której coś ci nie pasuje - dziwny plik, komentarz nie wiadomo skąd, konto, którego nie zakładałeś, poczta wysyłająca coś, czego nie napisałeś - napisz do mnie na [email protected]. Nie sprzedaję audytu z półki. Zaczynam od pytania, co konkretnie widzisz i od kiedy, i to pytanie zwykle wystarcza, żeby rozstrzygnąć, czy potrzebujesz pomocy, czy twój niepokój ma inne źródło. Bez faktury na start.

Jeśli natomiast czytasz to profilaktycznie i chcesz mieć pewność, że gdy coś się stanie, będziesz miał z czego odbudować chronologię - to jest właśnie rozmowa, którą warto odbyć wcześniej, nie pod presją. Jak wygląda współpraca ze mną, opisałem osobno.

✓ Sprawdzone na
własnej firmie
cybulski.ai JC
You've successfully subscribed to cybulski.ai
Great! Next, complete checkout for full access to cybulski.ai
Welcome back! You've successfully signed in.
Unable to sign you in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.