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
}

cgi

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 сортирует файлы в порядке их создания.

Щоб полегшити поділ на дві частини, помістимо імена у масив.