Dzisiaj na jednym forum zobaczyłem pytanie, czy można jakoś tak zrobić, by port wydawał się zamknięty dla kogoś, kto skanuje sieć, ale by "normalny" klient mógł się połączyć. Odpowiedź w zasadzie powinna brzmieć NIE (no, w każdym razie dla NORMALNEGO klienta), ale można próbować pewnej sztuczki...
Trochę schowane porty
Na początek mały przykład. To jest wynik tcpdump przy skanowaniu nmap portów od 1 do 10.
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes 22:28:46.964121 arp who-has 192.168.1.240 (ff:ff:ff:ff:ff:ff) tell 192.168.1.254 22:28:46.974117 IP 192.168.1.254.44523 > 192.168.1.240.5: tcp 0 22:28:46.975984 IP 192.168.1.254.44523 > 192.168.1.240.10: tcp 0 22:28:46.977954 IP 192.168.1.254.44523 > 192.168.1.240.8: tcp 0 22:28:46.979555 IP 192.168.1.254.44523 > 192.168.1.240.6: tcp 0 22:28:46.981465 IP 192.168.1.254.44523 > 192.168.1.240.4: tcp 0 22:28:46.983230 IP 192.168.1.254.44523 > 192.168.1.240.2: tcp 0 22:28:46.985160 IP 192.168.1.254.44523 > 192.168.1.240.3: tcp 0 22:28:46.986640 IP 192.168.1.254.44523 > 192.168.1.240.9: tcp 0 22:28:46.988536 IP 192.168.1.254.44523 > 192.168.1.240.1: tcp 0 22:28:46.990222 IP 192.168.1.254.44523 > 192.168.1.240.7: tcp 0 22:28:48.076636 IP 192.168.1.254.44524 > 192.168.1.240.7: tcp 0 22:28:48.076871 IP 192.168.1.254.44524 > 192.168.1.240.1: tcp 0 22:28:48.077077 IP 192.168.1.254.44524 > 192.168.1.240.9: tcp 0 22:28:48.077284 IP 192.168.1.254.44524 > 192.168.1.240.3: tcp 0 22:28:48.077522 IP 192.168.1.254.44524 > 192.168.1.240.2: tcp 0 22:28:48.077729 IP 192.168.1.254.44524 > 192.168.1.240.4: tcp 0 22:28:48.077939 IP 192.168.1.254.44524 > 192.168.1.240.6: tcp 0 22:28:48.078147 IP 192.168.1.254.44524 > 192.168.1.240.8: tcp 0 22:28:48.078383 IP 192.168.1.254.44524 > 192.168.1.240.10: tcp 0 22:28:48.078605 IP 192.168.1.254.44524 > 192.168.1.240.5: tcp 0
Jak widać każdy z portów sprawdzany jest DWA razy. I tutaj pojawia się miejsce dla małej sztuczki. TCP/IP projektowane było do pracy w warunkach, kiedy zagubienie pakietu w transmisji nie jest niczym szczególnym. Z tego też powodu w procesie nawiązywania połączenia wysłany pakiet SYN jest powtarzany kilkukrotnie. Ilość powtórzeń jest wartością parametryzowalną, ale zwykle wynosi od 2 do 4, co powoduje, że próba połączenia z poziomu NORMALNEGO klienta generuje od 3 do 5 pakietów SYN, a więc przynajmniej o jeden więcej niż nmap przy standardowym skanowaniu portów w trybie synscan. Oczywiście, między kolejnymi powtórzeniami pakietu SYN mija coraz więcej czasu. Wygląda to mniej więcej tak (mój Linuks ma ustawioną wartość powtórzeń na 3):
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes 22:35:40.338483 IP 192.168.1.254.1426 > 192.168.1.240.222: tcp 0 22:35:43.339291 IP 192.168.1.254.1426 > 192.168.1.240.222: tcp 0 22:35:49.338378 IP 192.168.1.254.1426 > 192.168.1.240.222: tcp 0 22:36:01.336559 IP 192.168.1.254.1426 > 192.168.1.240.222: tcp 0
Na czym więc powinna polegać ta sztuczka? Wystarczy, gdy port będzie odpowiadał dopiero na 3 próbę połączenia. Oczywiście, połączenie będzie strasznie wolno nawiązywane, ale czy ja mówiłem, że rozwiązanie będzie praktyczne? Jak można zrealizować coś takiego? Iptables i moduł recent. Konkretnego przykładu reguł w tej chwili nie podam, bo mi się nie chce ich testować. Czy można to wykorzystać praktycznie? Można, oczywiście jest to rozwiązanie sztukowane sznurkami i wcale doskonale nie działa, poza tym z uwagi na możliwości modułu recent wcale nie jest powiedziane, że ktoś skanując kilka razy serwer w końcu tego portu nie wykryje, ale tak jak już pisałem, to jest pewien eksperyment myślowy, ot taka sztuka dla sztuki. Ogólnie koncepcja nie jest nowa, istnieje takie pojęcie jak port-knocking. Chodzi o to, że pewne porty stają się dostępne dopiero po wysłaniu pewnej sekwencji pakietów. Powiedzmy, że serwer SSH staje się dostępny dopiero po tym, jak ktoś spróbuje połączyć się na porty 21, 29 i 333 w ciągu 3 sekund. Istnieje również stosowny demon. Sam stosuję podobne rozwiązanie do ukrycia serwera SSH na swoim serwerku. Denerwowały mnie wpisy w logach o próbach odgadnięcia hasła, a ponieważ bywam czasami w różnych miejscach, nie mogłem wpisać w regułach konkretnych adresów, z których będę się łączył. Nie chciałem jednak stosować skomplikowanej sekwencji "pukania", w związku z czym poszedłem na łatwiznę. Stworzyłem regułkę właśnie z wykorzystaniem modułu recent, która otwiera port 22 na jedną próbę połączenia z danego adresu IP, pod warunkiem, że wcześniej z tego adresu mój serwer został cztery razy spingowany. Port jest otwierany na 30 sekund. Stosowne reguły wyglądają mniej więcej tak:
-N IN_KNOCK_SSH -A IN_KNOCK_SSH -m recent --remove --name IN_KNOCK_SSH -j ACCEPT -A INPUT -i ppp0 -m recent --set --name IN_KNOCK_SSH -m icmp -p icmp --icmp-type echo-request -j DROP -A INPUT -i ppp0 -m recent --rcheck --hitcount 4 --seconds 30 --name IN_KNOCK_SSH -m state --state NEW -p tcp -m tcp --dport 22 -j IN_KNOCK_SSH
Choć rozwiązanie jest prymitywne, to spełnia swoje zadanie. Normalnie port 22 nie jest otwarty. Wpisy o zgadywaniu hasła przestały drażnić moje oczy...