NFsec Logo

Port knocking za pomocą iptables

17/07/2009 w Bezpieczeństwo 1 komentarz.  (artykuł nr 99, ilość słów: 1453)

T

echnika port knockingu polega na umożliwieniu za pomocą wysłania odpowiedniej sekwencji pakietów (zwanych puknięciami) do hosta, oraz zdalne wykonanie wcześniej zdefiniowanego polecenia na maszynie, do której pukamy. Głównie technika ta ma zastosowanie w uzyskaniu dostępu do przejścia w firewallu za pomocą specjalnej sekwencji pakietów wysłanych na różne porty. Może być używana do otwierania tymczasowych dziur w restrykcyjnym firewallu dla dostępu na przykład do popularnie udostępnianej usługi SSH. Posiadamy możliwość zdefiniowania różnych sekwencji puknięć czy kolejności portów, na które przychodzi puknięcie. Istnieje kilka implementacji tej techniki, za pomocą różnych dedykowanych programów np. knockd czy bardziej bezpieczny cyptknock. Poniżej zostanie przedstawiona technika autoryzacji, na komputerze tylko i wyłącznie za pomocą iptables i jego odpowiednim rozszerzeniom używanym do budowy reguł zapory sieciowej w Linuksie. W swojej prostocie jest idealnym rozwiązaniem w wielu sytuacjach nie wymagających zbyt wielu wyrafinowanych technik autoryzacyjnych, a także może być idealną wyjściową podstawą do bardziej zaawansowanych rozwiązań w różnych środowiskach sieciowych.

“System operacyjny” zapory sieciowej Linuksa – iptables posiada w swojej budowie kilka wbudowanych modułów znacznie rozszerzających jego możliwości zarządzania przebiegiem pakietów przez tworzone reguły. Dla naszego przykładu wykorzystamy moduł “recent” w wielu przypadkach wykorzystywany do blokowania adresów z wieloma połączeniami w określonym czasie, czy tworzenia tymczasowych czarnych list, a także detekcji dziwnych prób niedozwolonej autoryzacji. Poniżej przedstawiony jest listing reguł iptables uaktywniających technikę port knocking dla usługi SSH nasłuchującej na porcie 22. By otworzyć połączenie SSH, niezbędne będzie puknięcie do portu 666, a po zakończeniu połączenia, zamknięcie go puknięciem na port 999. Reguły te mogą zostać dodane do już istniejącej zapory, w której usługa SSH jest już filtrowana (znak “\” jest przejściem do następnego wiersza tylko w zapisie – wszystkie reguły powinny być wpisane w jednym ciągu bez tego znaku):

iptables -N PUKPUK
iptables -A INPUT -j PUKPUK
iptables -A FORWARD -j PUKPUK
iptables -A PUKPUK -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A PUKPUK -m state --state NEW -m tcp -p tcp --dport 22 -m recent --rcheck --name SSH -j ACCEPT
iptables -A PUKPUK -m state --state NEW -m tcp -p tcp --dport 22 -j DROP
iptables -A PUKPUK -m state --state NEW -m tcp -p tcp --dport 666 -m recent --name SSH --set -j DROP
iptables -A PUKPUK -m state --state NEW -m tcp -p tcp --dport 999 -m recent --name SSH --remove -j DROP

Pierwsze trzy reguły odnoszą się do stworzenia nowego łańcucha o nazwie PUKPUK (standardowo iptables posiada już trzy zdefiniowane łańcuchy 1. INPUT – połączenia przychodzące 2. OUTPUT – połączenia wychodzące 3. FORWARD – połączenia przekazywane) oraz przekazania wszystkich połączeń przychodzących jak i przekazywanych przez host także do łańcucha PUKPUK. Czyli wszyscy użytkownicy próbujący połączyć się z hostem lub poprzez host (np. będący routerem przekazującym połączenia do innej maszyny) będą musieli zapukać na port 666, aby zostać wpuszczonym. Czwarta reguła pozwala wszystkim istniejącym połączeniom oraz wszystkim nowym połączeniom wynikającym z istniejących połączeń na swobodny przepływ przez zaporę ogniową. Bez tej reguły nasze połączenie przez SSH by zostało zakończenie wraz z zamknięciem jego portu, a tak – jeśli na porcie SSH zostanie zablokowane tworzenie nowych połączeń – nasze, już istniejące połączenie pozostanie w niezmienionym stanie – mówiąc prościej: jest to zabezpieczenie w przypadku, gdy byśmy wykonali zapukanie zamykające port SSH. Piąta reguła jest najważniejszą. Jej działanie polega na sprawdzeniu czy nowe połączenie (nie już istniejące jak w przypadku czwartego wpisu), którego portem docelowym jest port TCP 22 (czyli SSH), jest uwzględnione w liście autoryzowanych numerów IP. Do tej listy źródłowe numery IP są dodawane za pomocą siódmej reguły, a usuwane ósmą. Szósta reguła blokuje cały ruch do portu 22, póki nowy adres IP nie zostanie dodany za pomocą siódmej reguły i zweryfikowany przez piątą regułę. Jak to działa w praktyce?

      Na hoście, który ma posiadać mechanizm pukania do usługi SSH dodajemy powyższe wpisy iptables. Z innej maszyny, próbujemy połączyć się przez SSH. Oczywiście połączenie zostanie odrzucone, ponieważ blokuje je szósta reguła. W takim przypadku należy wykonać siódmą regułę, która jest aktywowana połączeniem TCP (rodzaj protokołu jaki wybraliśmy posiada także kluczową rolę) na port 666. Możemy tego dokonać w prosty sposób poprzez próbę połączenia poprzez telnet IP_hosta_z_ssh 666 (lub inny program umożliwiający wysłanie odpowiedniej sekwencji pakietów SYN / ACK dla nowego połączenia). Oczywiście otrzymamy komunikat o przekroczeniu czasu połączenia oraz braku możliwości nawiązania łączności, ale w tym samym czasie zostanie na maszynie docelowej zostanie utworzony wpis autoryzacyjny wraz z źródłowym adresem IP próbującym się połączyć, który zostanie zapisany do /proc/net/ipt_recent/SSH – naszej listy autoryzowanych hostów, które mogą nawiązywać nowe połączenie z portem SSH. Gdy wyświetlimy zawartość tego pliku przed puknięciem na port 666 będzie on pusty, po puknięciu powinniśmy posiadać wpis podobny do tego:

# cat /proc/net/ipt_recent/SSH

src=83.16.206.59 ttl: 34 last_seen: 281889049 
oldest_pkt: 2 last_pkts: 24679369, 2868309649

W ten sposób na naszej liście hostów autoryzowanych do nowego połączenia SSH pojawił się pierwszy adres IP. Teraz z tego samego adresu IP możemy bez problemu połączyć się z demonem SSH na porcie 22, w dodatku port ten, jest tylko i wyłącznie otwarty dla naszego IP – połączenia innych hostów (o innym adresie IP) będą odrzucane dopóki nie zapukają na port 666. Po zakończonej sesji SSH należy zapukać na port 999, w celu zamknięcia możliwości nowych połączeń z naszego IP (możemy zrobić to nawet podczas trwania sesji, ponieważ nasze połączenie i tak nie zostanie zamknięte! – co umożliwia nam wcześniej opisana czwarta reguła). Po zapukaniu na port 999 – z listy autoryzacyjnej /proc/net/ipt_recent/SSH zostanie usunięty dany adres IP, tym samym straci możliwość nawiązywania nowych połączeń z hostem na porcie SSH.

Luką w tego rodzaju autoryzacji jest to, że port 666 będzie widoczny przy skanowaniu hosta skanerem (np. nmap). W dodatku jeśli skaner przejdzie przez port 666, otworzy sobie sam drogę do SSH. Dlatego port otwierający musi być zawsze większy niż port usługi, na której nasłuchuje maskowana przez nas usługa, a port zamykający musi być większy od portu otwierającego. W ten sposób skaner idąc od najniższych wartości, w górę przejdzie przez port 22 (który będzie zamknięty), następnie port 666, otwierając dopiero port 22 (oczywiście by musiał ponownie wykonać skanowanie by dowiedzieć się, że jest został on otwarty), ale przechodząc przez port 999, znowu zamknie dostęp do portu 22. Sposobem tym, przy ponownym skanowaniu będzie widział tylko otwarty port 666 (tak jak na początku). Nasuwa się pytanie: w jaki sposób można ulepszyć tą technikę? Oczywiście poprzez spowodowanie wydłużenia sekwencji pukania, czyli puknięcie na odpowiedni port, powinno otwierać kolejny port, a dopiero puknięcie do niego – dostęp do usługi, a wszystko to możemy ograniczyć jeszcze czasowo:

iptables -N PUK1
iptables -N PUK2
iptables -N PUK3
iptables -A INPUT -p tcp --dport 4321 -j PUK1
iptables -A INPUT -p tcp --dport 1234 -j PUK2
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --seconds 10 --rcheck --name TOJA -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j DROP
iptables -A PUK1 -m recent --set --name KTOTAM
iptables -A PUK1 -m recent --remove --name TOJA
iptables -A PUK1 -j REJECT
iptables -A PUK2 -m recent --rcheck --name KTOTAM --seconds 5 -j PUK3
iptables -A PUK2 -m recent --remove --name KTOTAM
iptables -A PUK3 -m recent --set --name TOJA
iptables -A PUK3 -j REJECT

Metoda, ta wprowadza nam trochę innowacji w porównaniu do poprzedniej. Po pierwsze występuje tu już zależność pomiędzy dwoma łańcuchami PUK1 oraz PUK2. Bez pierwszego puknięcia nie ma mowy o możliwości wykonania drugiego puknięcia – połączenie zostanie automatycznie odrzucone. Tak, więc aby nasz adres został uwzględniony musimy wykonać dokładną sekwencję połączeń na port 4321, następnie 1234. W dodatku połączenia pomiędzy tymi portami muszą zostać wykonane w przeciągu pięciu sekund!, inaczej cała sekwencja będzie musiała zostać powtórzona. To samo tyczy się dalszego połączenia na port 22 – czyli razem na cały portknocking mamy 10 sekund (2 x 5 pomiędzy poszczególnymi portami). Warto zwrócić uwagę, na to, że nawet po udanym połączeniu na port 22 oraz jego zakończeniu mimo wpisów z naszym adresem źródłowym w /proc/net/ipt_recent/KTOTAM i /proc/net/ipt_recent/TOJA, nie będziemy mogli ponownie połączyć się z portem 22 póty do póki nie wykonamy kolejnego zapukania zaczynając od portu 4321. Dlatego wpis zamykający port… :

iptables -A INPUT -p tcp --dport 3214 -m state --state NEW -m recent --name TOJA --remove -j DROP

… nie będzie nam potrzebny. W dodatku dzięki drugim wpisom dla łańcuchów PUK1 i PUK2 tablice TOJA i KTOTAM będą czyszczone przy kolejnych połączeniach. Łańcuch PUK3 jest pośrednikiem, w ostatecznym etapie udostępnienia portu 22, dla wybranego adresu IP. Należy mieć na uwadze, że zaprezentowane tutaj rozwiązania wymagają dodatkowych rozwiązań by w pełni stały się możliwe do zastosowania w bezpiecznej autoryzacji do serwera. Zagłębiając się w tematykę modułu recent można znacznie rozbudować możliwości portknockingu za pomocą iptables. By uniknąć nadmiernego zamieszania z otwieraniem i zamykaniem portów podczas skanowania z Internetu możemy z powodzeniem wprowadzić blokowanie za pomocą Portsentry, które możemy ustawić na detekcję przed portami usług oraz portów przeznaczonych do portknockingu.

Więcej informacji: Netfilter HowTo, ipt_recent, portsentry

Kategorie K a t e g o r i e : Bezpieczeństwo

Tagi T a g i : , , ,

1 komentarz.

  1. Od wersji jądra 2.6.16 ścieżka: /proc/net/ipt_recent/ posiada postać: /proc/net/xt_recent/.