Довольно часто можно встретить такую реальную ситуацию, что понадобилась статистика по безопасности тачки. С подобным столкнулся и я, а если более конкретно, то меня интересовали открытые порты telnet'а (по стандарту это 23-й порт, кому нужны подробности - см. RFC 1060). Ну не станешь же ты, перец, использовать статистику из газет 🙂 - мало ли откуда её брали. Так что я перешёл к более решительным мерам.
Под рукой был сервер, который работал круглосуточно, с установленным и сконфигурированным Linux'ом. Конечно, можно было использовать готовые сканеры - благо в сети их навалом. Однако куда интереснее и полезнее с точки зрения приобретенных практических навыков написать хоть и простенький сканер, но все же самому.
Выбор языков программирования был вполне достаточный, но памятуя, что простота - залог успеха, я выбрал для написания связку Perl + Expect. Немного обосную для непосвященных: Perl - мощнейшая штука при обработке текста и при этом весьма удобная, а Expect - уже готовый продукт для автоматизации различных задач с заранее известными входными/выходными данными. Думаю, уже понятнее :).
Чтобы хоть немного разобраться с используемыми языками, сделаем следующее: для Expect существует такая утилитка, как autoexpect. С её помощью можно легко наваять простенький скрипт, на основе которого можно понять основные принципы, заложенные в Expect. Допустим, мы хотим добиться, чтобы скрипт "скачал" с ftp-сервера какой-нибудь файл. Для этого выполняем команду: autoexpect ftp ftp.our_ftp_server.com.
Далее мы просто конектимся к серверу, скачиваем нужный файл и закрываем сеанс. Надеюсь, работать с ftp-клиентом все уже умеют :). В результате получаем готовый скрипт, по умолчанию создаётся файл script.exp, но можно и сразу задавать имя файла - autoexpect -f my_script_name ftp ftp.our_ftp_server.com, который делает то, что мы до этого делали вручную. Разумеется, в autoexpect есть куча других полезных фишек, но их вы уже будете изучать самостоятельно. Для более подробной информации советую заглянуть в мануал - man autoexpect.
Изучение кода полученного скрипта в большей части случаев помогает довольно быстро научиться писать свои собственные скрипты. Если кто и после просмотра кода не понял, то догоняю, что этот интерпретатор просто "ожидает" заранее определенного действия или момента времени и выполняет заложенные в него задачи или не выполняет - в зависимости от способностей пишущёго ;)). Для тех, кто в танке, список простейших команд:
- set - установить значение переменной;
- expect - ждать заранее известное значение(вполне можно задать маской);
- send - отправить значение;
- spawn - породить процесс;
- close - завершить процесс;
- exit - завершить работу скрипта.
Итак, вооружившись моими советами и собственными мозгами можете попытаться написать свой собственный скрипт. Например, можно попробовать подсоединиться к своей собственной машине с помощью телнета и отправить сообщение самому себе, по мылу, разумеется :). Далеко не всякий сразу же бросится писать свой скрипт - на этот случай привожу уже готовый вариант написанного скрипта:
#!/bin/expect -- #пропишите правильный путь до expect'а set timeout 60 #устанавливаем период ожидания - в данном случае это можно и не использовать spawn telnet 127.0.0.1 #все логично - запустили telnet expect -re "ame" #ожидаем запроса на ввод логина { send -- "your_login r" #отвечаем на запрос(если он пришёл), "/r" - перевод строки. } expect -re "assw" #кто не понял: первая буква может быть и заглавной, смотрим лишь по окончанию. { send - "your_password r" #отправляем e-mail в качестве пароля } expect -re ">" #ожидаем приглашения на ввод команд send - " mail your_login r" send -- "This is just a test. r" send - ".r" expect -re ">" send -- "exitr" close #посылаем запрос на закрытие сеанса wait #ожидаем закрытия exit
Проверьте - все должно работать. Теперь слегка усложним скриптик и будем вести log-файл. Тут уж я помогать не буду - пишите сами ;). Для этого юзаем log_file - остальное смотрите в man pages :). После первых удачных опытов можно приступать собственно к написанию самого сканера. Тут возникают некоторые подводные камни: ну, в первую очередь стоит обратить внимание на команду spawn - порождение нового процесса. Что же тут может такого быть?
Оказывается, что иногда (в основном в циклах, рекурсивных структурах и т.д.) процесс просто остаётся и занимает tty - на первых порах я на этом здорово обжёгся. Чтобы этого избежать, во-первых, стоит запускать spawn с ключом -nottycopy, а, во-вторых, не стоит забывать делать wait и close - в циклах это одно из первых правил (в вышеприведенном примере эти команды вполне можно убрать - ничего страшного не случится). Кроме того, не стоит забывать про период ожидания ответа - timeout - подбирайте оптимальное значение в соответствии со своими линиями связи. Например, на плохих линиях я ставил до 3-х минут, хотя есть ли резон сканировать то, до чего практически нельзя добраться? В общем - думайте сами, решайте сами ;).
#!/bin/expect -- set dir "/my_dir" set timeout 60 set server "[lrange $argv 0 0]" set index1 "[lrange $argv 1 1]" match_max 5000000 set telnet_log $dir/telnet.$server for {set index2 "[lrange $argv 2 2]"} {$index2 < 255} {incr index2} { spawn -nottycopy telnet $server.$index1.$index2 log_file $telnet_log send_log " $server.$index1.$index2" log_file expect {
-ex ">" { log_file $telnet_log send_log " - free >r" log_file } -ex "#" { log_file $telnet_log send_log " - free #r" log_file } -ex "$" { log_file $telnet_log send_log " - free $r" log_file } -ex "%" { log_file $telnet_log send_log " - free %r" log_file } -re "ogin|ame" { log_file $telnet_log send_log " - authentification requiredr" log_file } -re "assw" { log_file $telnet_log send_log " - only password requiredr" log_file } -re "route" { log_file $telnet_log send_log " - no route to hostr" log_file } -re "timeout" { log_file $telnet_log send_log " - timeoutr" log_file } -re "timed out" { log_file $telnet_log send_log " - timeoutr" log_file } -re "refuse" { log_file $telnet_log send_log " - connection refusedr" log_file } -re "close" { log_file $telnet_log send_log " - client closed connectionr" log_file } -re "not avail" { log_file $telnet_log send_log " - not availabler" log_file } default { log_file $telnet_log send_log " - defaultr" log_file } } close wait } exit
Ну а теперь небольшие комментарии :). Как я и предупреждал, это лишь простейший вариант, хотя и вполне работоспособный. Он принимает в качестве параметров 3 аргумента: "A.B", "C" и "D"(без кавычек естественно), где A, B, C – числа из диапазона от 0 до 255, а D – от 0 до 254. Скрипт перебирает все варианты от A.B.C.D до A.B.C.255. Почему не все сразу? Дело в том, что на линии, где я сей скрипт использовал, иногда были обрывы связи, а то и самому скрипт приходилось останавливать.
Вывод - необходимо, чтобы скрипт при запуске сам продолжал сканирование с момента обрыва связи. Все это я реализовал еще в 2-х скриптах - вот тут-то и пригодился Perl. А чтобы это всё работало, само собой я использовал cron (кто не в курсе - см. man cron). Однако я думаю, что вам будет намного полезнее написать все скрипты самим ;). К тому же их можно реализовать и намного проще. И я ни в коем случае никого не призываю сканировать чужие сети с помощью данного скрипта - все может закончиться довольно плачевно :).