6.3. Определение какие TCP или UDP порты открыты в системе

Описание.  Кандидат BSDA должен уметь использовать программы входящие в состав BSD, а так же сторонние программы, для определения того, какие порты в системе открыты, и какие порты видны через брандмауэр.

Практика. netstat(1), services(5), fstat(1); sockstat(1) и сторонное продукты nmap и lsof.

Комментарий

Как было показано выше (см. Раздел 6.1.2.3, «Работающие интернет сервисы и открытые сокеты»), команда netstat(1) пригодна для того, чтобы определить открытые tcp/udp соединения и их состояние. Другим средством для определения состояния файлов и сокетов являются команды fstat(1) и sockstat(1). Первая позволяет понять какие файловые дескрипторы какими пользователями открыты, вторая перечисляет открытые сокеты.

Жизнедеятельность всех программ выполняемых в пространстве пользователя может быть отслежена при помощи обращения к устройствам /dev/mem и /dev/kmem, предоставляющим информацию непосредственно из ядра системы. Файловой системы /proc в системах BSD нет. (Если она нужна для совместимости с какими-то программами, её можно специально смонтировать, при условии, что в ядре имеется поддержка PROCFS.) Программы fstat(1) и sockstat(1) берут информацию из упомянутых устройств.

В некоторых случаях названия протоколов употребляются символьные (вроде ssh, imap), в других случаях явно номера портов (22, 143). Соответствие символьных названий протоколов и их номеров указано в файле /etc/services.

6.3.1. fstat(1)

Команда fstat(1) выводит информацию обо всех открытых файловых дескрипторах. С её помощью можно получить информацию обо всех запущенных программах, так как каждая из них имеет по нескольку открытых файловых дескрипторов или сокетов, даже если в данный момент она не выполняет никакой работы. Пример, приведённый ниже, сильно урезан, так как всего в выводе команды fstat(1) было более семисот строк.

$ fstat > fstat-output
$ cat fstat-output
USER  CMD   PID FD MOUNT  INUM MODE   SZ|DV R/W
emin  fstat  84130 root /    2 drwxr-xr-x  512 r 1
emin  fstat  84130 wd /usr  5958657 drwxr-xr-x 2048 r
emin  fstat  84130 text /usr  447995 -r-xr-sr-x 14716 r
emin  fstat  84130 0 /dev   68 crw--w---- ttyp0 rw
emin  fstat  84130 1 /usr  5958902 -rw-r--r--  0 w
emin  fstat  84130 2 /dev   68 crw--w---- ttyp0 rw
emin  fstat  84130 3 /dev   20 crw-r-----  mem r
emin  fstat  84130 4 /dev   21 crw-r----- kmem r
emin  fstat  84130 5 /   8381 -rw-r--r-- 40960 r
......
root  getty  633 root /    2 drwxr-xr-x  512 r 2
root  getty  633 wd /    2 drwxr-xr-x  512 r
root  getty  633 text /usr  565434 -r-xr-xr-x 21016 r
root  getty  633 0 /dev   39 crw------- ttyv7 rw
root  getty  633 1 /dev   39 crw------- ttyv7 rw
root  getty  633 2 /dev   39 crw------- ttyv7 rw
......
root  getty  626 root /    2 drwxr-xr-x  512 r
root  getty  626 wd /    2 drwxr-xr-x  512 r
root  getty  626 text /usr  565434 -r-xr-xr-x 21016 r
root  getty  626 0 /dev   32 crw------- ttyv0 rw
root  getty  626 1 /dev   32 crw------- ttyv0 rw
root  getty  626 2 /dev   32 crw------- ttyv0 rw
......
root  devd   242 root /    2 drwxr-xr-x  512 r
root  devd   242 wd /    2 drwxr-xr-x  512 r
root  devd   242 text /   112 -r-xr-xr-x 281208 r
root  devd   242 0 /dev   8 crw-rw-rw- null rw
root  devd   242 1 /dev   8 crw-rw-rw- null rw
root  devd   242 2 /dev   8 crw-rw-rw- null rw
root  devd   242 3 /dev   5 crw------- devctl r
root  devd   242 4* local stream c1089000
root  adjkerntz 179 root /    2 drwxr-xr-x  512 r
root  adjkerntz 179 wd /    2 drwxr-xr-x  512 r
root  adjkerntz 179 text /   117 -r-xr-xr-x 6912 r
root  adjkerntz 179 0 -   -   bad -
root  adjkerntz 179 1 -   -   bad -
root  adjkerntz 179 2 -   -   bad -
root  init   1 root /    2 drwxr-xr-x  512 r 3
root  init   1 wd /    2 drwxr-xr-x  512 r
root  init   1 text /   47 -r-x------ 485892 r
  
1

Прежде всего, конечно, отметилась сама программа fstat(1). Видно, что от имени пользователя emin выполняется команда fstat с PID 84130. В поле FD перечислены файловые дескрипторы этой команды. Обратите внимание: дескрипторы 0, 1, и 2 отвечают за STDIN, STDOUT и STDERR. Поскольку в нашем случае вывод был направлен в файл, перый дескриптор в поле MOUNT указывает на /usr (на испытуемой машине там находятся пользовательские каталоги). Если бы мы направили вывод на консоль, то в этом месте был бы /dev:

$ fstat
USER  CMD   PID FD MOUNT  INUM MODE   SZ|DV R/W
emin  fstat  748 root /    2 drwxr-xr-x  512 r
emin  fstat  748 wd /usr  75413572 drwxr-xr-x 1536 r
emin  fstat  748 text /usr  34739458 -r-xr-sr-x 14716 r
emin  fstat  748 0 /dev  114 crw--w---- ttyp0 rw
emin  fstat  748 1 /dev  114 crw--w---- ttyp0 rw
emin  fstat  748 2 /dev  114 crw--w---- ttyp0 rw
emin  fstat  748 3 /dev   10 crw-r-----  mem r
emin  fstat  748 4 /dev   11 crw-r----- kmem r
emin  fstat  748 5 /   25101 -rw-r--r-- 73728 r
......
   

а если в pipe, то вывод будет выглядеть так:

$ fstat | less
USER  CMD   PID FD MOUNT  INUM MODE   SZ|DV R/W
emin  less   731 root /    2 drwxr-xr-x  512 r
emin  less   731 wd /usr  75413572 drwxr-xr-x 1536 r
emin  less   731 text /usr  34739548 -r-xr-xr-x 97736 r
emin  less   731 0* pipe c1ca4780 <-> c1ca482c  0 rw
emin  less   731 1 /dev  114 crw--w---- ttyp0 rw
emin  less   731 2 /dev  114 crw--w---- ttyp0 rw
emin  fstat  730 root /    2 drwxr-xr-x  512 r
emin  fstat  730 wd /usr  75413572 drwxr-xr-x 1536 r
emin  fstat  730 text /usr  34739458 -r-xr-sr-x 14716 r
emin  fstat  730 0 /dev  114 crw--w---- ttyp0 rw
emin  fstat  730 1* pipe c1ca482c <-> c1ca4780  0 rw
emin  fstat  730 2 /dev  114 crw--w---- ttyp0 rw
emin  fstat  730 3 /dev   10 crw-r-----  mem r
emin  fstat  730 4 /dev   11 crw-r----- kmem r
emin  fstat  730 5 /   25101 -rw-r--r-- 73728 r
......
   

Видно, что fstat(1) открыл pipe с номерами сокетов c1ca4780 и c1ca482c файловом дескрипторе 1 (STDOUT), а less сделал то же, но в обратном порядке и в файловом дескрипторе 0 (STDIN), через них идёт обмен данными.

Вернёмся к примеру. Второй файловый дескриптор направлен, как видно, на консоль ttyp0 (Это поток STDERR).

Дескрипторы 3 и 4 общаются с устройствами /dev/mem и /dev/kmem именно из них fstat(1) и берёт всю представленную здесь информацию. В операционных системах BSD нет виртуальной файловой системы /proc. Вся информация предостваляется ядром через упомянутые устройства. Хотя впринципе, если это надо для совместимости с какими-то программами, можно эмулировать наличие файловой системы /proc.

Перед перечнем файловых дескрипторов с номерами, мы видим три строки (самые первые) с отметками в поле файлового дескриптора root, wd, text. Их значение:

root
корневой inod
wd
рабочий каталог (current working directory)
text
текст исполнимого файла (собственно код)
tr
kernel trace file
mmap
memory-mapped file

Зная точку монтирования и номер inod можно найти к чему относятся приведённые значения wd, text и др.:

$ fstat
USER  CMD   PID FD MOUNT  INUM MODE   SZ|DV R/W
......
emin  fstat  748 wd /usr  75413572 drwxr-xr-x 1536 r
emin  fstat  748 text /usr  34739458 -r-xr-sr-x 14716 r
......
$ find -x /usr ( -inum 75413572 -o -inum 34739458 ) -ls 2>/dev/null
34739458  32 -r-xr-sr-x 1 root kmem 14716 30 авг 2005 /usr/bin/fstat
75413572  4 drwxr-xr-x 21 emin emin 1536 20 мар 21:13 /usr/home/emin
   

Синтаксис программы find(1) обсуждается в Раздел 7.6, «Поиск файла по заданным атрибутам». Команда lsof(1), обсуждающаяся ниже, печатает в выводе не только номера inod, но и имена файлов, которые она берёт из кеша ядра.

2 Здесь, для примера, приведены записи о 8-ми экземплярах команды getty(8), которые открыты на терминалах ttyv0ttyv7 и ждут логина пользователя. (Мы прошли на машину по ssh(1), поэтому ни один из терминалов неиспользован).
3 В конце приведены сведения о процессе init(8) — родительском процессе для всех прочих процессов.

6.3.2. sockstat(1)

Программа sockstat(1) предоставляет информацию о сокетах, как сетевых, так и сокетах доступных в виде файлов.

$ sockstat
USER  COMMAND PID FD PROTO LOCAL ADDRESS   FOREIGN ADDRESS
emin  sshd  84087 3 stream -> ??
emin  sshd  84087 4 tcp4 xxx.yyy.zzz.180:22 xyz.yzx.zxy.yxz:56325
root  sshd  84084 4 tcp4 xxx.yyy.zzz.180:22 xyz.yzx.zxy.yxz:56325
root  sshd  84084 5 stream -> ??
dovecot imap-login 72803 0 tcp4 *:143     *:*
dovecot imap-login 72803 3 stream -> ??
dovecot imap-login 72803 8 stream -> /var/run/dovecot/login/default
dovecot imap-login 63557 0 tcp4 *:143     *:*
dovecot imap-login 63557 3 stream -> ??
dovecot imap-login 63557 8 stream -> /var/run/dovecot/login/default
dovecot imap-login 59983 0 tcp4 *:143     *:*
dovecot imap-login 59983 3 stream -> ??
dovecot imap-login 59983 8 stream -> /var/run/dovecot/login/default
www  httpd  58349 3 tcp4 *:80     *:*
www  httpd  58348 3 tcp4 *:80     *:*
www  httpd  58347 3 tcp4 *:80     *:*
www  httpd  46549 3 tcp4 *:80     *:*
www  httpd  11184 3 tcp4 *:80     *:*
www  httpd  81458 3 tcp4 *:80     *:*
www  httpd  32934 3 tcp4 *:80     *:*
root  dovecot-au 53422 0 stream -> ??
root  dovecot-au 53422 3 stream /var/run/dovecot/login/default
root  dovecot-au 53422 7 stream /var/run/dovecot/login/default
root  dovecot-au 53422 8 stream /var/run/dovecot/login/default
root  dovecot-au 53422 9 stream /var/run/dovecot/login/default
root  dovecot 53421 5 tcp4 *:143     *:*
root  dovecot 53421 6 dgram -> /var/run/logpriv
root  dovecot 53421 9 stream /var/run/dovecot/login/default
root  dovecot 53421 10 stream -> ??
root  dovecot 53421 11 stream -> ??
root  dovecot 53421 13 stream -> ??
root  dovecot 53421 14 stream /var/run/dovecot/auth-worker.53422
root  dovecot 53421 15 stream -> ??
www  httpd  39377 3 tcp4 *:80     *:*
mysql mysqld  636 3 tcp4 *:3306    *:*
mysql mysqld  636 4 stream /tmp/mysql.sock
www  httpd  612 3 tcp4 *:80     *:*
www  httpd  609 3 tcp4 *:80     *:*
www  httpd  606 3 stream /var/run/cgisock.536
root  httpd  536 3 tcp4 *:80     *:*
root  cron  499 6 dgram -> /var/run/logpriv
smmsp sendmail 484 3 dgram -> /var/run/log
root  sendmail 480 3 dgram -> /var/run/logpriv
root  sendmail 480 4 tcp4 *:25     *:*
root  sendmail 480 5 tcp4 *:587     *:*
root  sshd  475 3 tcp4 *:22     *:*
root  ntpd  454 3 dgram -> /var/run/logpriv
root  ntpd  454 4 udp4 *:123     *:*
root  ntpd  454 5 udp4 xxx.yyy.zzz.180:123 *:*
root  ntpd  454 6 udp4 172.16.0.1:123  *:*
root  ntpd  454 7 udp4 127.0.0.1:123   *:*
daemon rpc.lockd 409 3 udp4 *:901     *:*
daemon rpc.lockd 409 4 tcp4 *:803     *:*
daemon rpc.lockd 409 5 dgram -> /var/run/logpriv
daemon rpc.lockd 409 7 udp4 *:797     *:*
_pflogd pflogd  402 5 stream -> ??
root  pflogd  400 4 stream -> ??
root  pflogd  400 5 dgram -> /var/run/logpriv
root  rpc.lockd 390 3 udp4 *:901     *:*
root  rpc.lockd 390 4 tcp4 *:803     *:*
root  rpc.lockd 390 5 dgram -> /var/run/logpriv
root  rpc.lockd 390 6 udp4 *:720     *:*
root  rpc.lockd 390 7 udp4 *:797     *:*
root  rpc.statd 385 4 udp4 *:743     *:*
root  rpc.statd 385 5 tcp4 *:966     *:*
root  rpc.statd 385 6 dgram -> /var/run/logpriv
root  nfsd  375 3 tcp4 *:2049    *:*
root  mountd  373 4 udp4 *:891     *:*
root  mountd  373 5 tcp4 *:925     *:*
root  rpcbind 351 5 stream /var/run/rpcbind.sock
root  rpcbind 351 6 dgram -> /var/run/logpriv
root  rpcbind 351 7 udp4 *:111     *:*
root  rpcbind 351 8 udp4 *:796     *:*
root  rpcbind 351 9 tcp4 *:111     *:*
bind  named  279 3 dgram -> /var/run/logpriv
bind  named  279 20 udp4 xxx.yyy.zzz.180:53 *:*
bind  named  279 21 tcp4 xxx.yyy.zzz.180:53 *:*
bind  named  279 22 udp4 172.16.0.1:53   *:*
bind  named  279 23 tcp4 172.16.0.1:53   *:*
bind  named  279 24 udp4 127.0.0.1:53   *:*
bind  named  279 25 tcp4 127.0.0.1:53   *:*
bind  named  279 26 udp4 *:59517    *:*
bind  named  279 27 tcp4 127.0.0.1:953   *:*
root  syslogd 264 3 dgram /var/run/log
root  syslogd 264 4 dgram /var/run/logpriv
root  syslogd 264 5 dgram /var/run/log
root  syslogd 264 6 dgram /var/named/var/run/log
root  syslogd 264 7 udp4 *:514     *:*
root  devd  242 4 stream /var/run/devd.pipe
  

Здесь запись типа xxx.yyy.zzz.180:53 означает, что система слушает интерфейс с адресом IP xxx.yyy.zzz.180, порт 53 (из файла /etc/services узнаём, что это сервер DNS). Запись типа *:22 означает, что на 22-м порту запущен демон sshd.

Вот некоторые полезные опции данной команды:

-l
Список портов открытых на прослушивание
-c
Список установленных соединений
-4, -6
Только протокол IPv4 или IPv6
-n
Не производить reverse-DNS запросы (опция присутсвует не во всех реализациях)
-u
Перечислит открытые локальные UNIX-сокеты.
-p 21-23,25,80,110
Фильтр по номерам портов. В данном случае позвляет вывести информацию только по портам 21, 22, 23, 25, 80 и 110.

Аналогичную информацию можно получить при анализе вывода программы netstat(1). См. Раздел 6.1.2, «netstat(1)» и Раздел 6.1.2.3, «Работающие интернет сервисы и открытые сокеты».

6.3.3. lsof(1)

Это тоже очень полезная программа, которая умеет рассказывать об открытых сокетах, сетевых соединениях и открытых файлах. Данная программа не входит в состав BSD, а доступна исключительно ввиде стороннего продукта (порта или пакета).

С опцией -i она может рассказать об открытых интернет-соединениях:

$ lsof -i -n | head
COMMAND PID USER FD TYPE  DEVICE SIZE/OFF NODE NAME
syslogd 306 root 5u IPv4 0xc18cc708  0t0 UDP *:syslog
ntpd  439 root 4u IPv4 0xc18cc654  0t0 UDP *:ntp
ntpd  439 root 5u IPv4 0xc18cc5a0  0t0 UDP 192.168.0.4:ntp
ntpd  439 root 6u IPv4 0xc18cc4ec  0t0 UDP 192.168.0.6:ntp
ntpd  439 root 7u IPv4 0xc18cc438  0t0 UDP 127.0.0.1:ntp
sshd  470 root 3u IPv4 0xc18fade0  0t0 TCP *:ssh (LISTEN)
sendmail 476 root 4u IPv4 0xc18fac24  0t0 TCP 127.0.0.1:smtp (LISTEN)
mysqld  570 mysql 3u IPv4 0xc18fa8ac  0t0 TCP *:3306 (LISTEN)
mysqld  570 mysql 3u IPv4 0xc18fa8ac  0t0 TCP *:3306 (LISTEN)
  

Здесь в скобках показано состояние соединения TCP (расшифровку см. выше). Сделав поиск по слову ESTABLISHED мы можем узнать соединения, по которым в данный момент могут передаваться данные:

$ lsof -i -n | grep ESTABLISHED | head
sshd  53540 root 4u IPv4 0xc28ae1bc  0t0 TCP xxx.yyy.zzz.xyz:ssh->xxx.yyy.zzz.zyx:52954 (ESTABLISHED)
sshd  53542 emin 4u IPv4 0xc28ae1bc  0t0 TCP xxx.yyy.zzz.xyz:ssh->xxx.yyy.zzz.zyx:52954 (ESTABLISHED)
httpd 47448 www 66u IPv4 0xc1f296f0  0t0 TCP xxx.yyy.zzz.xyz:http->**************:31825 (ESTABLISHED)
httpd 51286 www 66u IPv4 0xc2407378  0t0 TCP xxx.yyy.zzz.xyz:http->***********:2381 (ESTABLISHED)
httpd 51314 www 66u IPv4 0xc21c3534  0t0 TCP xxx.yyy.zzz.xyz:http->*************:11009 (ESTABLISHED)
httpd 51316 www 66u IPv4 0xc1b6dde0  0t0 TCP xxx.yyy.zzz.xyz:http->************:3664 (ESTABLISHED)
httpd 52426 www 66u IPv4 0xc2407a68  0t0 TCP xxx.yyy.zzz.xyz:http->************:10555 (ESTABLISHED)
httpd 53595 www 66u IPv4 0xc208ec24  0t0 TCP xxx.yyy.zzz.xyz:http->*************:55420 (ESTABLISHED)
httpd 53666 www 66u IPv4 0xc43adde0  0t0 TCP xxx.yyy.zzz.xyz:http->**************:3339 (ESTABLISHED)
httpd 53778 www 66u IPv4 0xc1fa8534  0t0 TCP xxx.yyy.zzz.xyz:http->*************:33735 (ESTABLISHED)
  

Следующий скрипт отслеживает количество соединений с web-сервером apache, и печатает статистику, по которой можно установить с какого IP пришло слишком много запросов.

#!/bin/sh
while :
do
 echo "========== `/bin/date` =========="
 /usr/local/bin/lsof -i -n |
  /usr/bin/awk '/^httpd.*ESTABLISHED/{print $9}' |
  /usr/bin/sed 's/.*->([0-9.]*):.*/1/' |
  /usr/bin/sort | /usr/bin/uniq -c | /usr/bin/sort -n -k1,1
 /bin/sleep 5
done
  

Ту же задачу, впрочем, можно решить и при помощи команды netstat(1):

#!/bin/sh
while :
do
 echo "========== `/bin/date` =========="
 /usr/bin/netstat -n | /usr/bin/awk '/62.117.108.4.80 .*ESTABLISHED/{print $5}' |
  /usr/bin/sed 's/.[0-9]*$//' |
  /usr/bin/sort | /usr/bin/uniq -c | /usr/bin/sort -n -k1,1
 /bin/sleep 5
done
  

Без опции -i программа lsof(1) выводит информацию об открытых файлах. Вывод её несколько удобнее, чем у программы fstat(1), так как включает в себя не только номера inod'ов, но и имена файлов, которые она берёт из кеша ядра.

6.3.4. nmap(1)

Программа nmap(1) сканирует порты, доступные на системе, иногда позволяет по fingerprint'у определить тип операционной системы на изучаемой системе:

$ nmap scanme.nmap.org

Starting Nmap 4.00 ( http://www.insecure.org/nmap/ ) at 2006-03-24 18:56 MSK
Interesting ports on scanme.nmap.org.48.153.217.205.in-addr.arpa (205.217.153.62):
(The 1660 ports scanned but not shown below are in state: filtered)
PORT STATE SERVICE
22/tcp open ssh
25/tcp closed smtp
53/tcp open domain
70/tcp closed gopher
80/tcp open http
113/tcp closed auth
135/tcp open msrpc
136/tcp open profile
137/tcp open netbios-ns
138/tcp open netbios-dgm
139/tcp open netbios-ssn
445/tcp open microsoft-ds

Nmap finished: 1 IP address (1 host up) scanned in 258.100 seconds
  

Программа nmap(1) имеет различные опции указывающие каким образом она должна смотреть открыт ли порт. Разумеется программа эта может быть использована как во благо (тестирование своего собственного брандмауэра), так и во вред. Тем более администратор должен знать о её возможностях.

По умолчанию программа занимается тем, что по очереди перебирает порты и посылает по ним SYN пакеты, а в ответ на SYN/ACK пакет высылается пакет RST (см. Раздел B.1.4.3, «TCP»). Возможны и другие способы сканирования, путём отсылки ACK пакетов, UDP пакетов и др. Всё это подробным образом освещяется в справочной странице по nmap(1). По необъяснимой для меня причине столь разрушительная программа в портах FreeBSD устанавливается так, что запустить её может кто угодно. На мой взгляд, первое, что должен выполнить администратор после установки такой программы, это команду: chmod 500 /usr/local/bin/nmap. Я конечно понимаю, что пользователь всё равно может собрать её локально, но зачем же его к этому подталкивать? Это я понять немогу.

В следующем разделе я расскажу о неменее разрушительной программе hping(8), которая, почему-то не входит в курс BSDA.


Обновлено: 12.03.2015