SQL Server jest oprogramowaniem, które pod kątem bezpieczeństwa posiada całą gamę opcji. Każda z tych opcji może być ustawiona w lepszy lub gorszy sposób w zależności od wiedzy i doświadczenia osoby ustawiającej. Niektóre “luki” w złej konfiguracji serwera są mniej niebezpieczne – inne z kolei bardziej. Są jednak pewne granice, których przekraczać nie wolno! Przykładów jest wiele np. nie można ustawiać trywialnego hasła do konta sa, nie wolno dawać uprawnień sysadmin na prawo i lewo – nie wolno nie robić kopii zapasowych baz produkcyjnych. Każde z tych uchybień może mieć katastrofalne skutki jak np. utrata danych w bazie danych. Jest jednak pewne ustawienie, którego udostępnienie użytkownikom może nie tylko powodować szkody w naszej bazie danych, ale również w innych miejscach jak system operacyjny hosta lub w skrajnych przypadkach nawet całej sieci! Chodzi mianowicie o procedurę xp_cmdshell o której traktuje niniejszy wpis.
xp_cmdshell jest rozszerzoną procedurą przechowywaną w bazie systemowej master, która umożliwia uruchomienie komendy linii poleceń Windows z poziomu SQL Server. Domyślnie jest ona wyłączona lecz możemy ją włączyć używając sp_configure lub mechanizmu polityk. Włączmy tą funkcjonalność używając pierwszej opcji:
sp_configure 'xp_cmdshell', 1 RECONFIGURE
Użycie samej procedury jest bardzo proste – wystarczy podać jeden tekstowy argument, który ma być skryptem/poleceniem linii komend. Dla przykładu poniższe wywołanie procedury wylistuje wszystkie elementy znajdujące się na dysku C:\ hosta, który jest moim dyskiem systemowym:
xp_cmdshell 'dir C:\'
Jak można zauważyć powyżej – każda linia zwrócona do konsoli w naszym przypadku jest reprezentowana jako osobny wiersz. Na dysku C:\ mam w tym wypadku sześć folderów i jeden plik tekstowy, który tam umieściłem na potrzeby artykułu. Spróbujmy teraz usunąć ten plik:
xp_cmdshell 'del C:\New?Text?Document.txt'
Powyższa komenda jest o tyle interesująca gdyż pokazuje, że w ścieżce do pliku/folderu nie może występować spacja, którą musimy po prostu zastąpić znakiem zapytania. W rezultacie otrzymaliśmy jeden wiersz z wartością null:
Jednakże listując ponownie dysk C:\ zobaczymy, że żądanego pliku już tam nie ma – sama operacja usunięcia nie zwróciła nam nic co jest w tym przypadku normalne i jest jawnym wskazaniem, że plik został usunięty:
Jak to w ogóle możliwe, że z poziomu SQL Server mogę usuwać pliki w systemie plików?! A co jeśli to byłby jakiś istotny plik systemu operacyjnego? Albo plik innej aplikacji/bazy danych, kopii zapasowych itp? No właśnie! Samo usunięcie było możliwe gdyż domyślnie wykonując tą procedurę do kooperacji z systemem plików wykorzystywane jest konto usługi SQL Server, które w tym wypadku było ustawione na mojego użytkownika Administrator, który jak sama nazwa wskazuje jest administratorem całego systemu:
Najgorsza z możliwych sytuacji ponieważ w parę minut mogę nieodwracalnie uszkodzić całą maszynę wirtualną na której działam! To jest jeden z kolejnych powodów dla których powinniśmy dla każdej usługi mieć dedykowane konto z jak najmniejszymi uprawnieniami.
Mając dostęp do linii komend możemy wykonywać ogromną liczbę operacji – m.in możemy wywołać komendę/skrypt powershell np. poniższe zapytanie zwróci listę aktywnych procesów w formacie tabeli na komputerze hosta:
xp_cmdshell 'powershell.exe "Get-Process|Format-Table"'
Całkiem fajne prawda? Jakby się uprzeć można zarządzać całą infrastrukturą IT z poziomu SSMS:) Poza tym nic nie stoi na przeszkodzie aby napisać skrypt, który zaśmieci dysk systemowy czy inną ważną lokalizację co może być dosyć dużym kłopotem i spowodować ogromne szkody. Wykorzystajmy powershell’a żeby znaleźć proces naszej instancji SQL Server:
xp_cmdshell 'powershell.exe "Get-Process sqlserv*"'
W łatwy sposób dostaliśmy identyfikator naszego procesu – to teraz go zabijmy:
xp_cmdshell 'powershell.exe "Stop-Process 1284"'
SQL Server oczywiście nie zdążył nawet zwrócić nam rezultatu gdyż komenda od razu go wyłączyła:
Myślę, że nawet najbardziej szczegółowa polityka bezpieczeństwa umożliwiająca osiągnięcie czegoś takiego jest absolutnie niedopuszczalna.
Domyślnie używać tej procedury mogą członkowie wbudowanej grupy sysadmin – jednakże jest możliwość wywołania jej przez innych użytkowników. Wtedy też do wywołania komendy jest używane konto przechowywane jako credential – aby stworzyć takiego credentiala należy użyć wbudowanej procedury sp_xp_cmdshell_proxy_account:
sp_xp_cmdshell_proxy_account 'win-n5eepku6qvv\Administrator' ,'123456'
Jako pierwszy parametr podajemy konto Windows, które będzie używane jako proxy, a jako następny hasło do tego konta. Po wykonaniu tej komendy możemy zobaczyć, że stworzony został obiekt proxy o nazwie ##xp_cmdshell_proxy_account## i to właśnie konto będzie używane przez użytkowników mających możliwość wywołania procedury xp_cmdshell nie będących sysadminami:
Po stworzeniu proxy należy przypisać możliwość wykonania naszej procedury do konkretnego użytkownika (w tym przypadku będzie to użytkownik o nazwie Test, który należy jedynie do grupy public w ramach bazy master):
GRANT exec ON xp_cmdshell TO [Test]
Następnie przełączmy się na tego użytkownika i spróbujemy wykonać jakieś polecenie linii komend z wykorzystaniem xp_cmdshell:
EXECUTE AS USER='Test' SELECT SUSER_NAME() AS CurrentUser GO xp_cmdshell 'dir C:\'
Jak widać wszystko działa bez żadnego problemu – dlatego też kolejny raz warto wspomnieć o tym, że dostęp do tej procedury powinien być bardzo mocno przemyślany. Jeśli chcemy usunąć proxy z naszego systemu również możemy użyć procedury sp_xp_cmdshell_proxy_account tym razem podając jeden parametr o wartości null:
sp_xp_cmdshell_proxy_account null
Jeśli już chcemy użyć tego mechanizmu i stworzyć proxy – to konto przypisane do tego proxy powinno mieć prawa jedynie do tej operacji, którą chcemy wykonać. Im więcej praw tym większe szkody procedura może wyrządzić. Wspomniałem, że nie warto tego używać prawda? Wspomnę raz jeszcze – nie warto! Czasem spotyka się pewne procedury wykorzystujące ten mechanizm do kopiowania plików kopii zapasowych z jednego miejsca do drugiego – ale dlaczego nie wykorzystać do tego Agenta lub chociażby Integration Services? Nad tymi mechanizmami mamy nieco większą kontrolę i dużo łatwiej monitorować takie rozwiązania. Oczywiście zazwyczaj jestem zdania, iż lepiej że coś w oprogramowaniu istnieje niż jakby miało tego nie być – w tym przypadku też tak jest – warto mieć świadomość możliwości komendy, ale również świadomość zagrożeń.
- Avoiding Issues: Monitoring Query Pushdowns in Databricks Federated Queries - October 27, 2024
- Microsoft Fabric: Using Workspace Identity for Authentication - September 25, 2024
- Executing SQL queries from Azure DevOps using Service Connection credentials - August 28, 2024
Stąd też xp_cmdshell jest domyślanie wyłączony 😉
Natomiast do galerii rzeczy których nie wolno, można dołączyć też nadawanie roli db_owner:
https://blog.netspi.com/hacking-sql-server-stored-procedures-part-1-untrustworthy-databases
Również ciekawym (i potencjalnie odłożonym w czasie) typem ataku wykorzystujący przynależność do db_owner jest możliwość utworzenia DDL Triggera:
https://docs.microsoft.com/en-us/sql/relational-databases/triggers/manage-trigger-security
@Norbert – jak najbardziej! Według mnie triggery inne niż do celów audytu też powinny być domyślne wyłączone 😛 Ale jak najbardziej trafne spostrzeżenia – sam często spotykam się z sytuacją, że w przypadku braku dostępu do danych przy bardziej zaawansowanym gąszczu ról – użytkownicy mają przywilej dbo bo przecież tak łatwiej i wszystko działa:)
tu dość mocny babol
bo “execute as login” nie ma wplywu na xp_cmdshell usera, to co najwyzej zmienia uzytkownika polaczenia do bazy danych
Cześć, dzięki za komentarz! W którym miejscu jest babol?
Przykład z przełączeniem użytkownika EXECUTE AS USER pokazuje tylko, co się dzieje gdy użytkownik nie będący sysadminem wywołuje xp_cmdshell. Oczywiście użytkownik ten nie jest używany do wykonywania poleceń cmdshell – szczególnie że jest to zwykły user SQL Server. Mam nadzieję, że teraz jest to jasne.
Pozdrawiam