Сканирование портов средствами nmap с последующим добавлением результата в один csv файл

Рейтинг: 0Ответов: 2Опубликовано: 05.07.2023

Есть файл servers.csv с списком ip адресов, я запускаю команду:
nmap -sT -iL servers.csv >> $HOME/outputs.csv

И получаю вывод в таком формате:

Starting Nmap 7.92 ( https://nmap.org ) at 2023-07-05 09:07 MSK
Nmap scan report for 10.200.230.167
Host is up (0.0019s latency).
Not shown: 996 filtered tcp ports (no-response)
PORT     STATE SERVICE
53/tcp   open  domain
80/tcp   open  http
443/tcp  open  https
8443/tcp open  https-alt
Nmap done: 1 IP address (1 host up) scanned in 15.70 seconds

Моя цель — объединить эти два файла servers.csv и outputs.csv в первой строке ip адреса, а во второй порты, вот пример как я это вижу:

IP PORT
10.200.230.167 53,80,4438,443

Ответы

▲ 0Принят

По комментариям:
Что же, это замечательно, что мой вариант отработал, потому что у меня:

nmap localhost | grep 'report\|open' | sed 's#.*(\|/.*##g' | sed ':a;N;$!ba;s/\n/,/g'
Выдаёт:
,25,135,445,1001,24800

То есть ip адрес куда-то обрезается, с чем это связано, я так и не понял. Возможно как-то связано с тем, что я запускаю команды под WSL.
Но‥ раз у вас отработало, предлагаю и дальше работать с тем что получилось.

Решение

Изначально хотел использовать круглые скобки, по ним можно легко найти начало и конец ip адреса, но у вас выходит, что где-то скобки есть, где-то их нет. Абстрагируемся от скобок, изменим sed 's#.*(\|/.*##g' на s#.*for \|/.*##g, подойдёт для случаев, если у вас только ip адреса, а не хосты, и дополним постобработку так, чтобы было похоже на csv:

nmap -sT -iL servers.csv | grep 'report\|open' | sed 's#.*for \|/.*##g' | sed ':a;N;$!ba;s/\n/,/g' | sed 's/(\|)//g' | sed 's/\(\.[[:digit:]]*\),/\1\t/g' | sed 's/,\([[:digit:]]*\.\)/\n\1/g'
Вывод:
10.156.26.82    80
10.146.40.91    80,443
10.136.66.88    80,443

По пайпам пошагово:

  1. grep 'report\|open'
    Выбираем строки где содержится ip адрес и открытые порты

  2. sed 's#.*for \|/.*##g'
    Убираем весь текст до ip адреса и после портов (/ и далее)

  3. sed ':a;N;$!ba;s/\n/,/g'
    Убираем все переносы строки, при этом последний останется, не надо в конце делать && echo " ", что удобно, подробнее про решение здесь на английском SO, перевод объяснения оттуда:

    1. sed начинается с чтения первой строки, исключая новую строку, в пространство шаблона.
    2. Создаётся метка через :a.
    3. Добавляется новая строка и следующая строка в пространство шаблона через N.
    4. Если мы находимся перед последней строкой, делаем ветку на созданную метку $!ba ($! означает не делать этого на последней строке. Это необходимо для того, чтобы избежать повторного выполнения N, что приведет к завершению скрипта, если больше не будет ввода!).
    5. Наконец, подстановка заменяет каждую новую строку запятой на пространстве шаблона (которым является весь поток).
  4. sed 's/(\|)//g'
    Убираем ( и ), в принципе, этот шаг можно объединить с пунктом 2: sed 's#.*for \|/.*\|(\|)##g', но оставлю как есть для читаемости.

  5. Финальные штрихи, добавляем табуляцию и перенос где нужно:

    1. sed 's/\(\.[[:digit:]]*\),/\1\t/g'
      • Ищем точку (\.) после которой следуют числа и запятая, то есть конец ip адреса. Всё что до запятой берём в группу.
      • В замене используем группу \1, а запятую меняем на табуляцию \t
    2. sed 's/,\([[:digit:]]*\.\)/\n\1/g'
      • Здесь примерно то же самое, только запятую меняем перед ip адресом и не на табуляцию, а на перенос строки.

Получилась очень длинная команда, что не очень хорошо. Если есть более элегантное решение с awk, было бы приятно увидеть его в соседнем ответе.

▲ 1

Спациально для @DiMithras вариант с awk:

nmap 192.168.0.1 127.0.0.1 | awk -F'[/ ]' '
    BEGIN{printf("%-20s%-20s\n", "IP", "PORTS")}
    /Nmap/{ip=$6; sep=""}
    /open/{ips[ip]=sprintf("%s%s%s", ips[ip], sep, $1); sep=","}
    END{for(ip in ips)printf "%-20s%-20s\n", ip, ips[ip]}
'

Результат:

IP                  PORTS               
(127.0.0.1)         22,631,1234,8080    
(192.168.0.1)       22,1234,8080 

Моя версия nmap выводит ip в скобках оставил как есть для упрощения скрипта.