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

[+]6.3.1. fstat(1)
[+]6.3.2. sockstat(1)
[+]6.3.3. lsof(1)
[*]6.3.4. nmap(1)

Описание.  Кандидат 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.