Описание. Кандидат BSDA должен уметь определить доступна ли удалённая система через TCP/IP и, если да, уметь при помощи telnet(1) убедиться отвечает ли сервис на клиентские запросы.
Практика. ping(8), traceroute(8), telnet(1), nc(1) на FreeBSD и OpenBSD
Утилита ping предназначена для того, чтобы при помощи отправки
ICMP пакетов убедиться в работоспособности хоста. Утилита
настолько широкоизвестна, что наверное нет необходимости
подробно
о ней говорить. Заметим только, то, о чём многие порой забывают:
у утилиты ping(8) есть масса разных
аргументов, и её можно использовать для разнообразнейшей
диагностики. В скриптах полезно бывает применять опцию
-c
при помощи которой можно сказать после
какого числа посланных и принятых (или не принятых) ICMP пакетов
работа программы остановится (по умолчанию, она работает
бесконечно, пока пользователь не нажмёт сочетание клавиш
Ctrl+C). Опция -i
позволяет задать интервал
времени между пакетами (по умолчанию 1 секунда). Опция
-I
позволяет задать конкретный интерфейс, с
которого будет отправлен пакет (если вопреки таблице
маршрутизации его надо послать куда-то в другое место).
-S
позволяет задать некоторый конкретный IP
адрес источника пинга. Очень разрушительная опция, но весьма
полезная в диагностике состояния сети —
-f
позволяет совместно с опцией
-c
отправить одновременно множество ICMP пакетов
(устроить так называемый флуд (flood — наводнение,
поток, жарг. болтовня)). В случае наличия помех в сети часть
пакетов будет потеряна, при нормальном пинге пакеты скорее всего
пройдут полностью. Ниже дан пример такой «атаки»
совершённой в сети с некачественным оборудованием (не
качественным на физическом уровне).
$
ping -f -c 10000 192.168.0.12
Password:
PING 192.168.0.12 (192.168.0.12): 56 data bytes
...............................................
--- 192.168.0.12 ping statistics ---
10000 packets transmitted, 6301 packets received, 36% packet loss
round-trip min/avg/max/stddev = 0.159/0.173/0.594/0.017 ms
Как видим, 36% пакетов было потеряно. В нормальной локальной сети, с хорошими проводами и не перенапряжёнными коммутаторами, такая «атака» не должна приводить к значительным потерям пакетов.
Ещё один «необычный» способ использования программы ping(8) может состоять в том, чтобы пинговать широковещательный адрес сети. В этом случае на пинги могут начать отвечать разные машины в сети (а могут и не отвечать, это зависит от их настроек). Таким образом можно попытаться получить информацию о том, какие машины в локальной сети в настоящий момент включены.
Не все хосты обязаны отвечать на запросы программы ping(8). Обычно программа ping(8) посылает пакеты, которые называются ECHO_REQUEST и ожидает получить пакет ECHO_RESPONSE. Однако варианты ответов могут быть разными. Ниже показан вариант с ответом «Destination Port Unreachable». Такой ответ, однако, означает, что на той стороне есть «живая» машина.
$
ping -c1 192.168.25.24
PING cube.mccme.ru (192.168.25.24): 56 data bytes
92 bytes from cube.mccme.ru (192.168.25.24): Destination Port Unreachable
Vr HL TOS Len ID Flg off TTL Pro cks Src Dst
4 5 00 5400 f8d6 0 0000 40 01 fcfe 192.168.25.158 192.168.25.24
--- 192.168.25.24 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
Немного напоминает знаменитый диалог Винни-Пуха и Пятачка:
Тут он наклонился, сунул голову в нору и крикнул:
— Эй! Кто-нибудь дома?
Вместо ответа послышалась какая-то возня, а потом снова стало тихо.
— Я спросил: «Эй! Кто-нибудь дома?» — повторил Пух громко-громко.
— Нет! — ответил чей-то голос. — И незачем так орать, — прибавил он, — я и в первый раз прекрасно тебя понял.
— Простите! — сказал Винни-Пух. — А что, совсем-совсем никого нет дома?
— Совсем-совсем никого! — отвечал голос. Тут Винни-Пух вытащил голову из норы и задумался.
Он подумал так: «Не может быть, чтобы там совсем-совсем никого не было! Кто-то там всё-таки есть — ведь кто-нибудь должен же был сказать: «Совсем-совсем никого!»»
Не путайте ответы «Destination Port Unreachable» и «Destination Host Unreachable». Последний генерируется маршрутизатором (или, в частном случае вашей собственной машиной), если он не знает куда послать пакет:
$
ping -c1 192.168.25.1
PING 192.168.25.1 (192.168.25.1): 56 data bytes
36 bytes from gateway (172.16.0.1): Destination Host Unreachable
Vr HL TOS Len ID Flg off TTL Pro cks Src Dst
4 5 00 5400 9514 0 0000 40 01 5fd9 172.16.0.2 192.168.25.1
--- 192.168.25.1 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
В первом («Винни-Пуховском») примере мы получили ответ от машины, которую мы и пинговали, следовательно она всё-таки включена. А во втором примере, пакет не был доставлен машине, так как она не была найдена (в приведённом примере не была найдена не только машина, но даже сеть, в которой она должна находиться).
Команда traceroute(1) в некоторых случаях позволяет выяснить маршрут от одного компьютера до другого. Для этого она посылает пакеты на целевую машину последовательно увеличивая параметр TTL (time to live). В норме TTL должен уменьшаться на единицу на каждом маршрутизаторе, пока не станет равным нулю. Если он обнулится, пакет будет отброшен, а отославшей его стороне вернётся пакет ICMP TIME_EXCEEDED. В этом пакете будет присутствовать IP маршрутизатора, который его послал. По этой информации traceroute(1) сможет перечислить машины, через которые идут пакеты до целевой машины.
Замечание | |
---|---|
Имейте ввиду: не все маршрутизаторы уменьшают TTL. Некоторые из них могут оказаться прозрачными для traceroute(1). |
Для примера, попробуем выяснить маршрут к несуществующей сети:
$
traceroute -n 10.0.0.1
traceroute to 10.0.0.1 (10.0.0.1), 64 hops max, 40 byte packets
1 172.16.0.1 0.418 ms 0.781 ms 0.228 ms
2 172.16.0.1 1.703 ms !H 0.585 ms !H 0.491 ms !H
Здесь мы выполняли команду traceroute(1) на машине 172.16.0.2. Она не знает маршрута к хосту 10.0.0.1 и пересылает пакет на машину 172.16.0.1 — свой маршрутизатор по умолчанию, а тот вернул ответ «Destination Host Unreachable», о чём свидетельствует флаг !H. Эта строка появилась потому, что брандмауэр на маршрутизаторе 172.16.0.1 не выпускает пакеты предназначенные для приватных сетей на свой дефолтный маршрутизатор возвращая ICMP пакет с сообщением об ошибке. Строка с номером 1 это результат работы первого пакета ICMP, в котором TTL был выставлен в 1. Этот пакет достиг машины 172.16.0.1, но дальнейшей маршрутизации не претерпел, так как у него истёк срок жизни, поэтому сообщение об ошибке сгенерировано не было. И только следующий пакет ICMP с TTL=2, породил сообщение об ошибке.
Далее идут несколько умозрительные примеры, почёрпнутые из справки по команде traceroute(1).
$
traceroute -n 192.168.2.1
traceroute to 192.168.5.1 (192.168.5.1), 64 hops max, 40 byte packet
1 172.16.0.1 0.418 ms 0.781 ms 0.228 ms
2 192.168.0.1 39 ms 39 ms 19 ms
3 192.168.0.1 39 ms 39 ms 19 ms
4 192.168.1.1 39 ms 40 ms 39 ms
5 192.168.2.1 39 ms 39 ms 39 ms
Заметьте, что строки 2 и 3 совпадают — это происходит потому, что на втором маршрутизаторе имеются ошибки в ядре — система 4.3BSD маршрутизирует пакет с нулевым TTL.
$
traceroute -n 192.168.9.1
traceroute to 192.168.9.1 (192.168.9.1), 64 hops max
1 172.16.0.1 0.418 ms 0.781 ms 0.228 ms
2 192.168.0.1 39 ms 39 ms 19 ms
3 192.168.0.1 39 ms 39 ms 19 ms
4 192.168.1.1 39 ms 40 ms 39 ms
5 192.168.2.1 39 ms 39 ms 39 ms
6 * * *
7 192.168.4.1 259 ms 499 ms 279 ms
8 * * *
9 * * *
10 * * *
11 * * *
12 192.168.9.1 339 ms 279 ms 279 ms
Шлюзы 6, 8, 9, 10 и 11 либо не высылают нам ICMP с сообщением «time exceeded», либо у их сообщений слишком маленький TTL и оно нас не достигает. В точности нельзя сказать, что происходит на маршрутизаторе 12. Например, это может быть следствием ошибок в ядре 4.[23]BSD: BSD 4.x (x меньше либо равен 3) высылали сообщение об ошибке используя TTL оригинального пакета. Таким образом, ICMP «time exceeded» принципиально не мог до нас добраться.
У программы traceroute(1) есть ещё один
полезный аргумент: -P [TCP|UDP|ICMP|...]
, с
помощью которого можно задать используемый протокол. По
умолчанию в системах BSD (и в
Linux тоже) traceroute(1)
высылает пакеты UDP направленные на абстрактный верхний порт.
Такие пакеты могут резать брандмауэры, поэтому и предусмотрена
возможность выбора протокола. Опция -I
включает
протокол ICMP. С её помощью traceroute(1)
работает так же, как утилита tracert в
Windows.
Ещё большую функциональность предлагает команда hping(8), описанная ниже.
Данная программа не входит в курс BSDA, тем не менее я очень советую с ней поупражняться. Как и программа nmap(1), hping(8) мощное средство тестирования брандмауэров, с её помощью можно генерировать разнообразный «неправильный» трафик.
Важно | |
---|---|
При помощи команды hping(8) можно сделать так называемую «back door» — чёрный ход. Команда hping(8) может запускаться только с правами суперпользователя. При этом, у неё есть режим в котором она ищет в приходящих пакетах некоторую метку и выполняет команды, которые встретятся после метки. Таким образом, если злоумышленник взломает вашу машину и сможет собрать и запустить hping(8) с нужными аргументами, то потом он сможет выполнять на атакованной системе произвольный код с полномочиями суперпользователя не осуществляя никаких операций связанных с аутентификацией. |
Часто машина не отвечает на пакеты ECHO_REQUEST, но при этом на ней успешно работает web-сервер и она отвечает на попытку установить соединение с 80-м портом. Программа hping(8) может «пинговать» хосты не только при помощи ICMP пакетов, которые часто отвергаются брандмауэрами, но так же и при помощи TCP и UDP пакетов.
Так же программа hping(8) позволяет при помощи TCP пакетов с плавно изменяющимся TTL выяснить маршрут к хосту, даже если это не смогла сделать программа traceroute(1). Принцип работы тот, же, но TCP пакеты, мягко говоря, реже уничтожаются брандмауэрами.
Проделаный ниже эксперимент проводился с хорошо известным в нашей стране интернет-магазином, который работает круглосуточно, но на пинги не откликается. Все имена в распечатке заменены, так как мне неизвестно из каких соображений администрация магазина выбрала такую политику безопасности.
$
ping -c1 example.org PING example.org (192.0.34.166): 56 data bytes --- example.org ping statistics --- 1 packets transmitted, 0 packets received, 100% packet loss#
hping -p 80 -S -c 1 example.org HPING example.org (rl0 192.0.34.166) S set, 40 headers + 0 data bytes len=46 ip=192.0.34.166 ttl=121 id=47606 sport=80 flags=SA seq=0 win=16384 rtt=18.6 ms --- example.org hping statistic --- 1 packets tramitted, 1 packets received, 0% packet loss round-trip min/avg/max = 18.6/18.6/18.6 ms$
traceroute example.org traceroute to example.org (192.0.34.166), 64 hops max, 40 byte packets 1 ****** (***.***.***.***) 0.432 ms 0.244 ms 4.614 ms 2 ****** (***.***.***.***) 0.751 ms 10.655 ms 1.809 ms 3 ****** (***.***.***.***) 0.859 ms 5.817 ms 10.049 ms 4 ****** (***.***.***.***) 13.455 ms 13.785 ms 17.565 ms 5 ****** (***.***.***.***) 18.931 ms 19.907 ms 18.807 ms 6 ****** (***.***.***.***) 25.173 ms 20.845 ms 20.466 ms 7 * * * 8 * * * ^C#
hping -p 80 -S -c 8 --traceroute example.org HPING example.org (rl0 192.0.34.166): S set, 40 headers + 0 data bytes hop=1 TTL 0 during transit from ip=***.***.***.*** name=****** hop=1 hoprtt=0.4 ms hop=2 TTL 0 during transit from ip=***.***.***.*** name=****** hop=2 hoprtt=0.7 ms hop=3 TTL 0 during transit from ip=***.***.***.*** name=****** hop=3 hoprtt=1.4 ms hop=4 TTL 0 during transit from ip=***.***.***.*** name=****** hop=4 hoprtt=11.9 ms hop=5 TTL 0 during transit from ip=***.***.***.*** name=****** hop=5 hoprtt=28.3 ms hop=6 TTL 0 during transit from ip=***.***.***.*** name=****** hop=6 hoprtt=40.0 ms len=46 ip=192.0.34.166 ttl=121 id=48766 sport=80 flags=SA seq=6 win=16384 rtt=20.3 ms len=46 ip=192.0.34.166 ttl=121 id=8660 sport=80 flags=SA seq=7 win=16384 rtt=32.5 ms --- example.org hping statistic --- 8 packets tramitted, 8 packets received, 0% packet loss round-trip min/avg/max = 0.4/16.9/40.0 ms
Как видим, если послать «пинг» при помощи пакета TCP на порт 80 с выставленным флагом SYN, на него приходит ответ. (Иначе не мог бы функционировать магазин). Таким же образом, мы можем выяснить и маршрут до него. При этом ни команда ping(8), ни traceroute(8) с задачей не справляются.
Подробное описание программы hping имеется в работе Николая Малых [url://Malyh-hping2-2005].
Основное назначение программы telnet(1) заключается в том, что она предоставляет удалённый терминал, позволяя управлять машиной на расстоянии. К сожалению, программа telnet(1) не шифрует трафик и даже пароли передаёт в открытом виде. Мы покажем чем это опасно, когда будем обсуждать программу tcpdump(1).
Важно | |
---|---|
Использовать программу telnet(1) по назначению — дурной тон. Это крайне небезопасно! Вместо неё следует использовать программу ssh(1). |
Однако программу telnet(1) можно использовать для того, чтобы провести диалог с удалённым сервером, проверить работу сервиса прослушивающего соответствующий порт. Например, при передаче почты, устанавливается связь с 25-м портом почтового сервера и далее идут команды SMTP протокола. Ниже приведён короткий пример такого диалога. Здесь символ | означает, что на этой строке приведено служебное сообщение программы telnet(1), > — строка которую мы вводим с клавиатуры, т.е. её клиент отсылает серверу, < — ответ сервера.
$
telnet mxs.mail.ru 25
| Trying 194.67.23.20...
| Connected to mxs.mail.ru.
| Escape character is '^]'.
< 220 Mail.Ru ESMTP
> HELO somewhere.org
< 250 mx8.mail.ru ready to serve
> QUIT
< 221 mx8.mail.ru closing connection
| Connection closed by foreign host.
Обратите внимание на фразу Escape character is '^]'. Если по каким-то причинам вы не можете дождаться ответа сервера и вам надо разорвать соединение, нажмите клавиши <Ctrl>+] и перед вами появится командная строка telnet(1). В ней вы можете набрать команду quit и завершить сеанс:
$
telnet mxs.mail.ru 25 Trying 194.67.23.20... Connected to mxs.mail.ru. Escape character is '^]'. 220 Mail.Ru ESMTP ^]telnet>
quit Connection closed.
Работа с telnet(1) может быть неудобна тем, что вы не всегда можете отредактировать свой текст в случае ошибки, к тому же некоторые сервисы, расчитанные на работу с роботом могут завершить соединение по таймауту, если вы набираете команды недостаточно быстро. Наконец, telnet(1) невозможно использовать в скриптах.
Чтобы преодолеть эти недостатки, вместо
telnet(1) можно использовать программу
nc(1) (netcat). В следующем примере, мы
делаем тоже, что и в предыдущем, но две команды SMTP (HELO example.org
и QUIT
) мы посылаем на стандартный ввод
команде nc(1), а на STDOUT
мы получаем ответы почтового сервера (вместо команды
echo, использована команда
printf(1), для того, чтобы напечатать
команды HELO и QUIT на двух разных строках):
$
printf 'HELO example.org\nQUIT' | nc mxs.mail.ru 25
220 Mail.Ru ESMTP
250 mx19.mail.ru ready to serve
Конечно программой nc(1) можно пользоваться и
интерактивно, если не назначить ей STDIN
,
она, как и положено любой UNIX программе, будет брать
STDIN
с клавиатуры:
$
nc msx.mail.ru 25
< 220 Mail.Ru ESMTP
> HELO somehere.ru
< 250 mx18.mail.ru ready to serve
> QUIT
< 221 mx18.mail.ru closing connection
В качестве примера скрипта использующего программу nc(1) приведём программу, которая пытается определить правильность некоторого адреса email. Приведённая программа первым делом разбирает адрес email, находя в нём часть отвечающую за имя сервера. Затем делает запрос к серверу DNS при помощи программы dig(1) определяя имя почтового сервера. И, наконец, при помощи nc(1) проводит диалог с этим сервером пытаясь при помощи команды SMTP RCPT определить существование почтового адреса на данной машине. Почтовый сервер на эту команду должен дать положительный ответ (код больше двухсот, но меньше трёхсот) и надпись <Recipient ok>. Но может и не дать никакого ответа по соображениям безопасности (смотря как настроен почтовый сервер). Тонкости работы команды dig(1) рассмотрены в Раздел 6.5, «Запрос к серверу DNS». Программирование в Bourne shell в Раздел 7.7, «Написание несложных Bourne-скриптов».
#!/bin/sh if [ $# -lt 1 ] then echo "Usage mailtest.sh user@server" exit 1 fi # Вычленяем имя сервера из почтового адреса server=`echo $1 | sed s/.*@//` # Определяем почтовый сервер ответственный за передачу писем на # сервер $server mxserver=$( dig $server MX |\ egrep 'MX[[:space:]]+[0-9]+[[:space:]]+[^[:space:]]+\.' |\ sort -n -k5,5 |\ head -1 | awk '{print $6}' | sed 's/\.$//' ) echo Investigate: $1 echo Server: $server echo MX server: $mxserver # Пытаемся провести SMTP диалог с почтовым сервером nc $mxserver 25 << EOF HELO example.org MAIL From: postmaster@example.org RCPT To: $1 QUIT EOF
Программа nc(1) штатно присутствует в FreeBSD и OpenBSD, а в DragonFly BSD и NetBSD её надо ставить отдельно, как сторонний продукт.
В Раздел C.2.1.6.4.3, «TCP proxy» можно найти пример в котором nc(1) используется в качестве прокси-сервера.