Z tego artykułu dowiesz się:
- Co to jest File Read (nieautoryzowany odczyt plików), dlaczego jest niebezpieczny i jakie podatności mogą być jego przyczyną
- Jak upewnić się, że mamy do czynienia z tą podatnością podczas testowania
- Jakie są strategie wykorzystania tej podatności i co można z niej osiągnąć?
- A także jakie są generyczne strategie zapobiegania / wykrywania jej w aplikacjach WWW.
1. FILE READ – czyli co konkretnie?
Skutkiem wielu podatności w aplikacjach webowych i podobnych technologiach jest tzw. file read, czyli odczyt plików. Oznacza to, że atakujący może poznać zawartość niektórych plików, które znajdują się na jakimś serwerze należącym do infrastruktury aplikacji. Z punktu widzenia metodologii, samo odczytywanie plików jest już tak właściwie jedną z technik wykorzystania podatności takich jak m.in.:
- SQL Injection
- SSRF
- XXE
- Path traversal
1.1 File read a LFI/RFI
Często podatność File Read może być efektem podatności LFI/RFI (Local/Remote File Inclusion). Czym one się różnią? Read oznacza, że możemy poznać zawartość jakiegoś pliku, wyświetlić lub wytransferować do siebie po prostu jego treść. To jest przypadek, który omawiamy.
Inclusion za to oznacza, że plik przed odczytem jest dodatkowo parsowany, to znaczy interpretowany przez jakiś silnik skryptowy / językowy. Inclusion może prowadzić bezpośrednio do wykonania kodu (przez parsowanie), a jednym z jego efektów ubocznych może być odczyt plików. Aktualnie „inclusion” spotyka się w aplikacjach webowych coraz rzadziej.
1.2 Wykorzystanie podatności – czyli co?
Zagrożenie płynące z podatności, która prowadzi do odczytu plików, może mieć różny poziom ryzyka. Wszystko tak naprawdę zależy od tego, co faktycznie wynika z tego, że możemy odczytać jakiś plik?
Aby podatność zakwalifikować jako stwarzającą zagrożenie, musimy umieć pokazać, co faktycznie złego może się stać zgodnie z zasadą „PoC || GTFO” 🙂
Proof of Concept zawierający odczyt pliku /etc/passwd może wygląda imponująco, ale co tak naprawdę można zrobić z tą podatnością i jak bardzo można ją wyeskalować, żeby pokazać, że atakujący może zagrozić któremuś ze składników bezpieczeństwa – Confidentiality (poufność), Availability (dostępność) lub Integrity (integralność)? Czy jako atakujący, możemy odczytać coś, co faktycznie spowoduje, że poznamy jakieś poufne dane? Czy możemy uzyskać dostęp do czegoś, co pomoże nam przejąć większą niż wynika z naszych uprawnień, kontrolę nad aplikacją? O możliwych scenariuszach i o tym, jakich plików szukać aby zmaksymalizować impact, piszemy poniżej.
2. Potwierdzamy, że podatność istnieje – TIPS & TRICKS
Jeżeli jakaś podatność, lub funkcjonalność aplikacji oferuje nam możliwość odczytu plików na systemie, zazwyczaj dowiemy się o tym w dwojaki sposób:
- Uda nam się odczytać „na ślepo” plik, który na 100% istnieje na systemie, np. /etc/passwd
- Uda nam się odczytać jakiś plik z webroota, np. plik .js, którego względną lokalizację znamy, tak jak poniżej:
Często jednak odpowiedzi aplikacji mogą być mylące, a niektóre pliki mogą się „nie wczytywać”, co opiszemy już za chwilę. Jak jeszcze można spróbować potwierdzić istnienie tej podatności?
- Spróbować wywołać Stack Trace / inny komunikat błędu. Dzięki temu możemy poznać bezwzględną ścieżkę, która istnieje na serwerze i spróbować odwołać do istniejących w niej plików, dzięki czemu wiemy, że odwołujemy się do czegoś istniejącego
- Spróbować odczytać ścieżkę „.” lub „/” – jednym słowem, odczytać katalog a nie plik. W niektórych przypadkach może się zdarzyć, że aplikacja wyświetli pełen directory listing, co dodatkowo ułatwi nam zadanie – poprzez wyświetlenie konkretnych plików
- Spróbować odczytać inne pliki na systemie niż /etc/passwd – zwłaszcza krótsze i nie zawierające znaków specjalnych, np. na Linuxie mogą to być /etc/hostname lub /etc/issue,
- na Windowsie jest dużo większy problem z „plikami-pewniakami”, za to jeżeli kontrolujemy pełną ścieżkę, to możemy odwołać się do ścieżki UNC (jeśli nie znasz tej techniki – czytaj dalej!)
2.1 Zmyłki ze strony aplikacji
To, że jakiegoś pliku nie da się odczytać, nie znaczy że on nie istnieje. Może być to także spowodowane tym, że
- Plik jest za duży, lub zawiera znaki, których z jakiegoś powodu nasza aplikacja „nie trawi”. Najczęściej taka sytuacja może wystąpić w przypadku plików binarnych i w sytuacji odczytu plików przez podatność XXE
- Aplikacja nie ma dostępu do pliku, który chcemy odczytać. Dobrze skonfigurowane aplikacje nie są dopuszczone do odczytu plików na filesystemie, więc realnie odczyt plików może nastąpić tylko z webroota. Oczywiście to nadal pozostawia atakującemu czy też testującemu szerokie pole manewru – w dalszej części rozpatrzymy także taki wariant.
- Jeżeli chcemy odczytać plik z innego katalogu korzystając z techniki takiej jak Path Traversal, to możemy skorzystać z różnego rodzaju enkodowania – czasami ../../ nie zadziała lub będzie filtrowane przez aplikację, ale np. \\..\\..\\.. już będzie w porządku. Wiele przykładów tego typu kodowania znajdziemy na TEJ wordliście, którą możemy oczywiście zmodyfikować na potrzeby indywidualnych testów.
3. Dobieramy się do „mięsa”
Możemy odczytywać pliki na serwerze i chcemy pokazać, jak groźna jest to podatność, lub nawet chcemy pozyskać dodatkowe informacje i przejąć kontrolę nad serwerem. Co robimy? Jakie pliki odczytujemy?
3.1 Linux
- /etc/issue – ten plik też zawsze istnieje, jest raczej krótki, ale zawiera bardzo istotną informację przydatną dalej – dokładną wersję systemu operacyjnego. Dzięki temu możemy odwzorować środowisko „u siebie” a następnie dowiedzieć się, jakie pliki na pewno istnieją na tej samej wersji systemu.
- /etc/shadow – optymistyczny wariant, zawiera hashe haseł i odczytanie go oznacza, że aplikacja działa z uprawnieniami roota i możemy odczytać co tylko chcemy.
- /proc/self/environ, /proc/[cyfra]/environ – ścieżka /proc/[id]/environ zawiera zmienne środowiskowe procesu o id równemu [id], self oznacza bieżący proces aplikacji. Może się okazać, że w zmiennych środowiskowych do wystartowania aplikacji lub innego procesu przekazano jakieś wrażliwe dane.
- ~/.bash_history, ~/.zsh_history, /root/.bash_history, itp. – pliki historii powłoki to niezwykle interesujące miejsca, z których możemy dowiedzieć się np. jakie operacje były wykonywane przez administratora, jakie inne usługi były ostatnio uruchamiane, a czasami możemy też spotkać jakieś poświadczenia wpisywane bezpośrednio do linii poleceń. Zazwyczaj mamy dostęp tylko do „swojej” historii, ale zawsze można probówać też czy przypadkiem nie mamy dostępu do folderów innych użytkowników. Tego, jacy użytkownicy są na systemie, dowiemy się np. z /etc/passwd lub directory listingu folderu /home.
- ~/.aws/credentials – plik zawierający poświadczenia do chmury AWS. Podobne lokalizacje to np. ~/.config/gcloud/access_tokens i ~/.config/gcloud/credentials. W przypadku klastra kubernetesa możemy szukać lokalizacji np. /var/run/secrets/kubernetes.io/serviceaccount/token – te i podobne pliki mogą dostarczyć nam dostępu do większej infrastruktury, w ramach której działa aplikacja.
- ~/.ssh/id_rsa – klucz prywatny ssh może przydać się do połączenia z innymi hostami lub hostem docelowym, jeśli są do tego skonfigurowane – często ślady połączenia znajdziemy w historii powłoki (np. ssh -i [zdobyty_klucz])
- /dev/null – może skończyć się DoS’em – warto wiedzieć, nie warto testować 🙂
Ostatnia deska ratunku
A co, jeżeli wiemy, że podatność prowadząca do czytania plików istnieje, ale jest ograniczenie np. dotyczące rozszerzenia? Co, jeżeli możemy przeczytać tylko pliki z jakimś rozszerzeniem, o określonej długości – jak znaleźć potencjalnych kandydatów? Możemy podobny system zbudować u siebie np. w formie maszyny wirtualnej, a następnie skorzystać z polecenia find:
Uruchamiając polecenie find na swoim systemie o zbliżonej lub tej samej wersji co docelowy, możemy znaleźć wiele plików, które są kandydatami na odczyt. Korzystamy z polecenia:
find / -type f -size -510c -name „*.gif” 2>/dev/null
Gdzie poszczególne argumenty oznaczają:
/ – przeszukuj cały bieżący system plików
-type f – znajdź tylko pliki
-size -510c znajdź pliki do 510 znaków długości
-name „*.gif” odpowiadające schematowi nazw [cokolwiek].gif
2>/dev/null – wszystkie błędy przekieruj do /dev/null (nie wyświetlaj błedów w output’cie)
3.2 Windows
Sytuacja na systemie windows nieco bardziej się komplikuje, ponieważ Windows jest dużo uboższy w „ciekawe pliki” względem Linuxa. Aby potwierdzić istnienie podatności, możemy spróbować odczytać np.:
- C:\windows\win.ini – bezwartościowy dla atakującego plik potiwerdzający istnienie podatności
- C:\boot.ini – jak wyżej
Jeżeli na Windowsie działają jakieś usługi, to dopiero one mogą posiadać „ciekawe” pliki konfiguracyjne. O usługach piszemy dalej w generycznych strategiach, a przykładem może być np. plik z poświadczeniami AWS, który na Windowsie zazwyczaj znajduje się w katalogu
- C:\Users\ USERNAME \.aws\credentials
Za to jeżeli kontrolujemy pełną ścieżkę odczytu a nie tylko „traversal”, możemy wywołać połączenie do zewnętrznego serwera i dowiedzieć się, z jakiego użytkownika korzysta aplikacja oraz być może nawet złamać jego hasło.
Windows specjalnie traktuje tzw. ścieżki UNC. Są to ścieżki do zasobów sieciowych. Jeżeli mamy możliwość podania pełnej ścieżki, to podajemy do odczytu:
- \\naszIP\nieistniejącyzasob
lub
- \\\\naszIP\\nieistniejącyzasob jeśli aplikacja „escapuje” backslashe.
Skłaniając aplikację działającą na windowsie do połączenia się w tej sposób do naszego serwera może nam się udać wykraść hash NetNTLMv2 – czyli skrót hasła użytkownika w kontekście którego działa aplikacja. Aby „odebrać” połączenie możemy użyć np. metasploita – modułu auxiliary/server/capture/smb
Więcej o łamaniu i wykorzystaniu hashów typu NetNTLMv2 przeczytasz TUTAJ (eng).
3.3 TIPS & TRICKS – pozostałe techniki
Zależnie od technologii i tego, jakie pliki znajdziemy na serwerze, ciekawe informacje, które mogą pomóc nam dostać się do paneli administracyjnych, baz danych itp. mogą znajdować się plikach.
- Dobrym sposobem jest przeprowadzenie skanu np. nmapem. Dzięki temu dowiemy się, jakie jeszcze usługi znajdją się na serwerze. Wiele z nich posiada domyślne pliki konfiguracyjne, pozwalające nam np. na odczytanie stamtąd hasła, a dalej zalogowanie się. Przykładem takiego pliku może być np. jmx.properties w aplikacjach javowych lub tomcat-users.xml zawierający hasła użytkowników tomcata.
- Warto obserwować wszelkie inferfejsy administracyjne, które pojawiają się w aplikacji webowej – zwłaszcza panele logowania do CMS, Middleware itd.
- Dotyczy to także poszukiwania „ciekawych” plików na Windowsie – tam w głównej mierze to, co uda się zdobyć zależy od usług, do których uda się uzyskać dostęp.
A co ciekawego może być w samym webroocie czyli katalogu aplikacji webowej? Zależnie od technologii warto skupić się na poniższych tropach:
- PHP
- Pliki konfiguracyjne i includowane przez inne pliki. Warto przejrzeć, czy jakieś pliki są „requirowane” lub „includowane” a dalej podążać śladem kolejnych załączeń. Finalnie możemy dotrzeć do jakiegoś pliku konfiguracyjnego obsługującego np. połączenia z bazą danych.
- Jeśli możemy listować katalogi, to możemy spróbować znaleźć katalog z sesjami na Apache – a następnie zamontować nazwę pliku jako swój cookie PHPSESSID i być może przejąć cudze sesje
- Plik .htpasswd mogący zawierać poświadczenia, które możemy próbować złamać.
- .NET
- web.config – na starszych wersjach .NET plik web.config zawiera tzw. Machine Key służący do szyfrowania parametrów ViewState. Jego odczyt może pomóc nam przeprowadzić atak deserializacji na aplikację. Więcej o tej technice przeczytasz TUTAJ.
- nowy .NET Core nie korzysta już z tego mechanizmu
- pliki .ASP i podobne analogicznie do .PHP mogą zawierać wrażliwe dane
- Java
- pliki .JSP analogicznie do .ASP i .PHP – szukamy wrażliwych danych i poświadczeń
- /WEB-INF/web.xml – szukamy ukrytych endpointów
- sciąganie i dekompilacja bibliotek .jar w celu znalezienia sekretów w kodzie lub poznania logiki aplikacji
- wiele java’owych mieddleware jak Tomcat, Weblogic przechowuje poświadczenia w plikach konfiguracyjnych – warto ich poszukać
web.xml może zawierać ścieżki do ukrytych endpointów (servletów) aplikacji w elementach servlet-mapping
- Inne środowiska i sposoby
- W Node.js lub ruby możemy poszukać plikow .env, /config
- Jedną z ciekawych metod może być uruchomienie narzędzia do Content Discovery (Dirb, FFuf, GoBuster, Burp Intruder itp. ) bezpośrednio na podatności. Możemy użyć wtedy wordlisty specyficznej dla danej technologii.
- Jeżeli aplikacja na to pozwala (nie przytłoczy jej liczba zapytań) to tego typu technika może pozwolić na znalezienie wielu plików
4. Podsumowanie
Jest wiele podatności, które mogą skutkować odczytem plików. Tak naprawdę od konfiguracji aplikacji zależy czy atakującemu lub pentesterowi uda się eskalować tą podatność dalej. A co administrator może zrobić, aby bronić się, lub wykryć, że ktoś próbuje / wykorzystuje taką podatność?
- Aplikacja powinna działać w kontekście osobnego użytkownika który posiada odpowiednie restrykcje, np. nie ma dostępu z zewnątrz po SSH, i nie może logować się samym kluczem, a także ma bardzo ograniczone możliwości dostępu do systemu plików,
- Zaleca się skorzystanie z zasady najmniejszych wymaganych przywilejów (Principle of least privilege) – czyli użytkownik powinien nie móc nic, poza wymaganymi do działania aplikacji operacjami, np. odczytem i zapisem tylko do odpowiednich lokalizacji w webroocie,
- Odczyt plików często może być pochodną innych podatności takiej jak np. XXE, jest więc skutkiem a nie przyczyną. Mimo to warto monitorować wszelkie próby odwołania do zasobów, zawierające specyficzne sekwencje znaków ../ ..\ itp. Tego typu żadania, zwłaszcza w dużej ilości prawie na pewno znaczą o tym, że ktoś próbuje dostać się w głąb naszej aplikacji,
- Chronimy inne usługi poprzez stosowanie firewalla i blokujemy interfejsy administracyjne przed dostępem z zewnątrz – wtedy nie dojdzie do sytuacji, gdzie ktoś odczyta np. plik konfiguracyjny do jakiejś usługi a następnie „wejdzie” nam do środka przez np. Tomcat’a.
PS. Jeśli chcesz mieć pod ręką więcej wiedzy z zakresu cyberbezpieczeństwa, to koniecznie obserwuj nas na Linkedin, Facebooku lub Youtube, aby nie przeoczyć żadnego artykułu!