Основы работы с Linux. / Сканируем порты на хостах. / Сканируем порты на хостах.
Cканируем порты.
Просканировать удаленный хост можно такой командой
nmap -P0 192.168.9.240
Посмотреть кто слушает определенный порт.
netstat -anp | grep LISTEN | grep 620
Задача провести сканирование открытых портов на хостах, взятых из списка в файле hostlist.
Далее провести анализ и сравнить с предыдущим сканированием, выяснить изменения и составить отчет.
Разобьем задачу на 3 файла.
scan.sh - будет сканировать порты (от 1 до 1024), принимая на stdin имя хоста;
compare.sh - будет сравнивать результаты с предыдущим сканированием;
report.sh - будет запускать 1 и 2 и формировать файл отчета.
Создание цикла по входящим хостам.
Файл scan.sh
while read HOSTNAME
do
echo $HOSTNAME
done
read - читает строку из stdin.
Передаем данные из файла hostlist
Файл report.sh
./scan.sh < hostlist
Формируем название файла с результатами сканирования.
scan.sh
printf -v TODAY 'scan_%(%F)T' -1
echo $TODAY
%()T - спецификатор функции printf обозначающий дату-время.
%F - спецификатор системной фукнции strftime описывающий формат “год-месяц-день”
-1 - означает “сейчас”
Изменим scan.sh добавим функцию и ее вызовем.
printf -v TODAY 'scan_%(%F)T' -1
function scan() {
host=$1
printf '%s' "$host"
}
while read HOSTNAME
do
scan $HOSTNAME
done
В bash явно не указываются параметры функции, они забираются через $1,2 и т.д.
printf - не выводит символ переноса строки.
Делаем цикл по портам.
function scan() {
host=$1
printf '%s' "$host"
for ((port=1;port<1024;port++)){
echo $port
}
}
Или так
for ((port=1;port<1024;port++)) do echo $port done
Доработаем функцию попыткой создать подключение на сокет
function scan() {
host=$1
printf '%s' "$host"
for ((port=1;port<1024;port++))
do
echo >/dev/null 2>&1 < /dev/tcp/${host}/${port}
if (($? == 0)) ; then printf ' %d' "${port}" ; fi
done
echo
}
echo без аргументов выведет stdout символ переноса строки и мы его игнорируем переводя в /dev/null.
Однако перенаправление из stdin будет выполнятся bash, несмотря на то, что это не используется коммандой echo.
2>&1 < /dev/tcp/${host}/${port} - тут мы переправляем содержимое и ошибки из предполагаемого файлового дескриптора сокет-соединения, причем данные даже не будут считаны т.к. команде echo это не нужно. Просто проверит есть ли файл (дескриптор соединения).
Переправление результатов сканирования в файл.
while read HOSTNAME
do
scan $HOSTNAME
done > $TODAY
Можно было бы переправить внутри цикла, но так удобней т.к. нам не нужно заботиться об обрезании файла в цикле перенаправлением >. Даже если бы мы использовали >> в цикле, то нужно было бы перед ним обрезать файл.
Сравнение с предыдущим сканированием.
Использование
./compare.sh <file1> <file2>
Определяем функцию поиска порта
function NotInList ()
{
for port in "$@"
do
if [[ $port == $LOOKFOR ]]
then
return 1
fi
done
return 0
}
$@ - разворачивает параметры функции по одному отдельными значениями
Напоминаем что ненулевое значение считается ложным.
Читаем содержимое 2 файлов по файловым дескрипторам.
while true
do
read aline <&4 || break # EOF
read bline <&5 || break # EOF, для симметрии
echo $aline
echo $bline
done 4< $1 5< $2
4< $1 5< $2 - тут мы создаем файловые дескрипторы, переправляя в них содержимое файлов первого и второго параметра
<&4 - & показывает что мы используем дескриптор файла а не файл с именем 4
|| - логическое OR оно выполнить breack только в случае если read вернет ошибку, что происходит на второй итерации цикла, когда достигается конец файла.
Проверим равенство двух строк, и если они равны переходим на новую итерацию цикла.
[[ $aline == $bline ]] && continue;
Извлечение хоста и портов.
HOSTA=${aline%% *}
PORTSA=( ${aline#* } )
HOSTB=${bline%% *}
PORTSB=( ${bline#* } )
${string#substring} - удаляет самое короткое совпадение подстроки от начала.
${string##substring} - удаляет самое длинное совпадение подстроки от начала.
% - работает с конца строки
HOSTA=${aline%% *} - удалит все символы с конца строки до первого пробела
PORTSA=( ${aline#* } ) - создаст массив портов удаляя все символы от начала до первого пробела
Далее мы делаем две проверки на порты, которые были открыты и закрыты.
for porta in ${PORTSA[@]}
do
LOOKFOR=$porta
NotInList ${PORTSB[@]} && echo " closed: $porta"
done
for portb in ${PORTSB[@]}
do
LOOKFOR=$portb
NotInList ${PORTSA[@]} && echo " new: $portb"
done
NotInList ${PORTSB[@]} && echo ” closed: $porta” - выводит только в случае успешного выполнения выражения слева от &&
Формирование отчета
Файл report.sh
./scan.sh < hostlist
FILELIST=$(ls scan_* | tail -2)
FILES=( $FILELIST )
./compare.sh ${FILES[0]} ${FILES[1]}
$(ls scan_* | tail -2) - выбираем два последних отчета, ls сортирует файлы в порядке их создания.
Чтобы облегчить разделение на две части, поместим имена в массив.