1

I would like to find the list of IP address with all open ports using awk command.

My nmap output file is:

# Nmap 7.01 scan initiated Sat Mar 18 06:27:08 2017 as: nmap -oG output.txt -T4 -f -iL iplist.txt
Host: 10.0.0.99 ()    Status: Up
Host: 10.0.0.99 ()    Ports: 135/open/tcp//msrpc///, 139/open/tcp//netbios-ssn///, 445/open/tcp//microsoft-ds///, 514/filtered/tcp//shell///, 554/open/tcp//rtsp///, 10243/open/tcp//unknown///    

Host: 192.168.0.1 ()    Status: Up
Host: 192.168.0.1 ()    Ports: 135/open/tcp//msrpc///, 139/open/tcp//netbios-ssn///, 445/open/tcp//microsoft-ds///, 10243/open/tcp//unknown/// 

Host: 192.168.0.101 ()    Status: Up
Host: 192.168.0.101 ()    Status: Up
# Nmap done at Sat Mar 18 06:29:02 2017 -- 3 IP addresses (3 hosts up) scanned in 113.19 seconds

Expected Output :

10.0.0.99   135
10.0.0.99   139
10.0.0.99   445
10.0.0.99   514
10.0.0.99   554
10.0.0.99   10243
192.168.0.1 135
192.168.0.1 139
192.168.0.1 445 
192.168.0.1 10243

output saved in another file (e.g. parse.txt)

  • doesn't nmap have a variety of output-formatting options already? – Jeff Schaller Mar 18 '17 at 14:41
2

You asked for awk, but this is simple to do with Perl, since it makes it easy to iterate over multiple matches of a regex.

$ perl -ne '$host = $1 if /^Host: (\S+)/; while(m,(\d+)/open,g) 
    { print "$host\t$1\n" } ' < nmap.out 
10.0.0.99       135
10.0.0.99       139
...


perl -ne                       # Run the code for each input line
'$host = $1 if /^Host: (\S+)/  # If the line starts with "Host: ", 
                               # save the next non-whitespace string
while( m,(\d+)/open,g ) {      # Go through all matches of `<digits>/open`
                               # on the line, catching the digits
    print "$host\t$1\n"        # Print the saved hostname and the matched digits.
}'                              
2

An awk approach:

$ awk -F'[/ ]' '{h=$2; for(i=1;i<=NF;i++){if($i=="open"){print h,$(i-1)}}}' file 
10.0.0.99 135
10.0.0.99 139
10.0.0.99 445
10.0.0.99 554
10.0.0.99 10243
192.168.0.1 135
192.168.0.1 139
192.168.0.1 445
192.168.0.1 10243

The -F[/ ] sets the input field separator to / or a space. This means that the host IP will be $2 and both the port number and the word "open" will be on their own fields. We can therefore iterate over all fields of a line (for(i=1;i<=NF;i++){}) and, if the current field is open, print the hostname and the previous field (the port): if($i=="open"){print h,$(i-1)}.

0

I don't think I was able to put across my viewpoint clearly here. What I want to emphasize is that instead of having an explicit loop, we can piggyback on the regex m// to do the looping for us, below is the restructured regex for greater clarity:

perl -lne '
    /^
        Host: \s+ (\S+) \s+         # stuff $1 with hostname
        .+?:                        # seek the nearest colon :
        (?:                         # setup a loop
           \s+(\d+)\/open\/\S+      # this pattern should repeat
           (?{ print "$1\t$2" })    # print hostname+digits foreach turn
        )* # (?{ CODE }) does a code execution during regex search
    /x;
' yourfile

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.