A simple port scanner script

So I was thinking about what I could try next in my quest to learn bash and came across a web post on how to scan ‘localhost’ for open ports. It was actually just the command, so I embellished it a bit to include options, printing, etc. Then I happened upon ‘netcat’ and thought “could this be better?” Turns out it is, if only for the fact it provides more information and runs a lot faster and uses way less CPU resources. I’ve left the web solution included in the script should someone want to compare the two, but in my final script, it will be gone. Though the script works, I am absolutely certain there are mistakes or better ways of doing what I did. Also, if anyone has a way to improve it that would be great. For instance, it would be nice to know what is using those open ports. :expressionless: So here is the script:

#!/bin/bash
#
# bash ver 4.2-68.1.5
# openSUSE 13.1
# A simple port scanner performed on 'localhost'
# Note: This script has to be run with root permissions to get full benefit
#
# Like to start with a clean slate.
clear
# Set some variables
myDate=$(date +"%A %B %d %Y %T %Z %n")
myFile="/root/open_ports.txt"
declare -i myRange1
declare -i myRange2
declare -i myScanned
#
echo -e "==============================
"
if  "$(whoami)" != 'root' ];
    then
        echo "I'm sorry "$USER", you must be root to run this script."
        exit 1;
fi
echo -e "Hello, "$USER". this script will scan for open ports.
"
echo -e "==============================
"
#
# ++++++++++ CREATE FILE IF NECESSARY ++++++++++
# Make sure they only enter y or n
#
while true
do
    read -s -n1 -p "Do you want to save data to a file [y/n]: " myAns
    echo $myAns
#
    case $myAns in
        y|Y)  # see if file /root/port_scanner.txt exists, If no, then create, if yes, move on
            touch $myFile
            echo "========== Report Generated "$myDate >> $myFile
            echo -e "++++++ File /root/open_ports.txt ready.
"
            break
            ;;
        n|N)
            echo -e "++++++ No file will be created or modified
"
            break
            ;;
        *)  # Presses something other than 'y' or 'n'
            echo "Smartass, just press y or n"
            continue
            ;;
    esac
done
#
# ++++++++++ GET PORT OR PORT RANGE ++++++++++
#
echo -e "==============================
"
echo "Ports are generally divided into three groups:"
echo "**** Well Known Ports: 0-1023" # I am omitting '0' from this range and making 1 the minimum port
echo "**** Registered Ports: 1024-49151"
echo -e "**** Dynamic/Private Ports: 49152-65535
"
echo -e "==============================
"
#
while true
do
    read -s -n1 -p "Moving on, do you want to check a (s)ingle port or a (r)ange of ports [s/r]: " myOpt
    echo $myOpt
    case $myOpt in
        s|S)
            read -p "Enter the port you want to check [1-65535]: " myRange1
            if  "$myRange1" -lt "1" ]] ||  "$myRange1" -gt "65535" ]]
                then
                echo "Must be in range of 1-65535"
                continue
            fi
            myRange2=$myRange1
            break
            ;;
        r|R)
            read -p "Enter the first port you want to check ]: " myRange1
            if  "$myRange1" -lt "1" ]] ||  "$myRange1" -gt "65535" ]]
                then
                echo "Must be in range of 1-65535"
                continue
            fi
            read -p "Enter the ending port you want to check ]: " myRange2
            if  "$myRange2" -lt "$myRange1"+1 ]] ||  "$myRange2" -gt "65535" ]]
                then
                echo "Must be greater than Range 1 and less than or equal to 65535"
                continue
            fi
            break
            ;;
        *)
            echo "Really? Out of range"
            continue
            ;;
    esac
done
#
# ++++++++++ TIME TO SCAN PORTS ++++++++++
#
echo
echo -e "==============================
"
echo -e "Scanning for open ports using web solution
"
#
# Print results to file- Yes
#
if  "$myAns" == "y" ]
    then
    for (( p=$myRange1; p<=$myRange2; p++ )) do
    (echo >/dev/tcp/localhost/$p) >/dev/null 2>&1 && echo "$p open" | tee >> $myFile;
    done
fi
#
# Or No
#
for (( p=$myRange1; p<=$myRange2; p++ )) do
    (echo >/dev/tcp/localhost/$p) >/dev/null 2>&1 && echo "$p open" ;
done
#
echo
echo -e "==============================
"
echo -e "Scanning for open ports using netcat (nc)
"
#
# Lets try it with netcat
# Note: on my default system I have the 'netcat-openbsd' package. Not sure if this will
# + work with netcat-traditional.
#
if  "$myAns" == "y" ]
    then
    nc -vz localhost $myRange1-$myRange2 2>&1 | grep succeeded | tee >> $myFile;
fi
#
nc -vz localhost $myRange1-$myRange2 2>&1 |grep succeeded;
#
echo
echo -e "==============================
"
echo "Total number of ports scanned"
#
if  "$myRange2" = "$myRange1" ]]
  then
  myScanned=1
  else
  let "myScanned = $myRange2 - $myRange1"
fi
echo "Ports Scanned = " $myScanned
#
# Lets us know script is done by sending us a beep.
#
echo -e "\a" > /dev/console;
echo -e "==============================
"
echo "tata"

and here is the output file:

========== Report Generated Sunday June 08 2014 12:41:42 EDT
* With web solution
25 open *(mail)
7634 open *(hddtemp)
47452 open *(unk)
* With netcat
Connection to localhost 25 port [tcp/smtp] succeeded!
Connection to localhost 7634 port [tcp/*] succeeded!
Connection to localhost 55453 port [tcp/*] succeeded!
Connection to localhost 60924 port [tcp/*] succeeded!

Note: entrys with a '*' I added, not the program

As always, comments, suggestions, corrections and such are appreciated.

Sorry, I did not take the time to read through al of it. but I saw your question about “wich program is listening to that port” (if I interprete correctly). Of course that can not be found out by a portscanner (working from “outside”), but when you want to know which ports are open for listening and which prcosesses do the listenening, then look into

netstat -tulp

You need to be root to get the full advantage of the -p option.

Just gave it a try and it certainly gives a lot more information, i.e. I now know that some of my seemingly random ports (those above 30000) belong to the ‘avahi-daemon’ (now I have to go look up what that is :). I also may have to consider netstat vs netcat. Time to get another cup of coffee.

On 06/08/2014 11:36 AM, sparkz alot wrote:
>
> So I was thinking about what I could try next in my quest to learn bash
> and came across a web post on how to scan ‘localhost’ for open ports. It
> was actually just the command, so I embellished it a bit to include
> options, printing, etc. Then I happened upon ‘netcat’ and thought “could
> this be better?” Turns out it is, if only for the fact it provides more
> information and runs a lot faster and uses way less CPU resources. I’ve
> left the web solution included in the script should someone want to
> compare the two, but in my final script, it will be gone. Though the
> script works, I am absolutely certain there are mistakes or better ways
> of doing what I did. Also, if anyone has a way to improve it that would
> be great. For instance, it would be nice to know what is using those
> open ports. :expressionless: So here is the script:

Yet, netcat is great. Note that on some distributions the command ‘nc’ is
used instead of netcat. I think openSUSE actually lets you use either
depending on which netcat package you’ve installed.

> Code:
> --------------------
> #!/bin/bash
> #
> # bash ver 4.2-68.1.5
> # openSUSE 13.1
> # A simple port scanner performed on ‘localhost’
> # Note: This script has to be run with root permissions to get full benefit
> #
> # Like to start with a clean slate.
> clear
> # Set some variables
> myDate=$(date +"%A %B %d %Y %T %Z %n")
> myFile="/root/open_ports.txt"

Why hard-coded to /root instead of elsewhere? Also, losing old data when
running a second time might be annoying. Check out the mktemp command to
easily find/use temporary files for output. You may also consider writing
permanent output files with timestamps in their name so that multiple
attempts do not overwrite old data unnecessarily. It’s not like you’re
dealing with GiB of data or anything.

> declare -i myRange1
> declare -i myRange2
> declare -i myScanned
> #
> echo -e "==============================
"
> if “$(whoami)” != ‘root’ ];
> then
> echo “I’m sorry “$USER”, you must be root to run this script.”
> exit 1;

I see no reason for this, other than your web solution is trying to write
to places it has no business writing and you have hard-coded paths to
/root. Unless you really, really need ‘root’, don’t require it. Port
scanning seldom requires root, and you can do 99.9% of what you want and
perhaps insert a note somewhere in the output stating, “If you want the
process tied to all listening ports on the local system, then run as ‘root’”.

> fi
> echo -e "Hello, “$USER”. this script will scan for open ports.
"
> echo -e "==============================
"
> #
> # ++++++++++ CREATE FILE IF NECESSARY ++++++++++
> # Make sure they only enter y or n
> #
> while true
> do
> read -s -n1 -p "Do you want to save data to a file [y/n]: " myAns
> echo $myAns
> #
> case $myAns in
> y|Y) # see if file /root/port_scanner.txt exists, If no, then create, if yes, move on
> touch $myFile
> echo "========== Report Generated "$myDate >> $myFile
> echo -e "++++++ File /root/open_ports.txt ready.
"
> break
> ;;
> n|N)
> echo -e “++++++ No file will be created or modified
"
> break
> ;;
> ) # Presses something other than ‘y’ or ‘n’
> echo “Smartass, just press y or n”
> continue
> ;;
> esac
> done
> #
> # ++++++++++ GET PORT OR PORT RANGE ++++++++++
> #
> echo -e "==============================
"
> echo “Ports are generally divided into three groups:”
> echo "
*** Well Known Ports: 0-1023” # I am omitting ‘0’ from this range and making 1 the minimum port
> echo “**** Registered Ports: 1024-49151”
> echo -e "**** Dynamic/Private Ports: 49152-65535
"
> echo -e "==============================
"
> #
> while true
> do
> read -s -n1 -p "Moving on, do you want to check a (s)ingle port or a (r)ange of ports [s/r]: " myOpt
> echo $myOpt
> case $myOpt in
> s|S)
> read -p "Enter the port you want to check [1-65535]: " myRange1
> if “$myRange1” -lt “1” ]] || “$myRange1” -gt “65535” ]]
> then
> echo “Must be in range of 1-65535”
> continue
> fi
> myRange2=$myRange1
> break
> ;;
> r|R)
> read -p "Enter the first port you want to check ]: " myRange1
> if “$myRange1” -lt “1” ]] || “$myRange1” -gt “65535” ]]
> then
> echo “Must be in range of 1-65535”
> continue
> fi
> read -p "Enter the ending port you want to check ]: " myRange2
> if “$myRange2” -lt “$myRange1”+1 ]] || “$myRange2” -gt “65535” ]]
> then
> echo “Must be greater than Range 1 and less than or equal to 65535”
> continue
> fi
> break
> ;;
> *)
> echo “Really? Out of range”
> continue
> ;;
> esac
> done
> #
> # ++++++++++ TIME TO SCAN PORTS ++++++++++
> #
> echo
> echo -e "==============================
"
> echo -e "Scanning for open ports using web solution
"
> #
> # Print results to file- Yes
> #
> if “$myAns” == “y” ]

Consolidate this code to a single branch earlier when the user chose to
use a file or not. At that point, if they choose to NOT write to a file,
set myFile to /dev/null and then you can eliminate the duplicate code
below here. Having the exact same code twice should be avoided whenever
possible since it makes maintenance a pain. Another option: Make this a
function that can be called and then call with a real file or /dev/null so
that reviewing your code and understanding its flow is easier. A function
named “scanPorts” that accepted the range of ports (including a single
port if only scanning a single port) and a target file (regular or
/dev/null) would simplify a lot of this.

> then
> for (( p=$myRange1; p<=$myRange2; p++ )) do
> (echo >/dev/tcp/localhost/$p) >/dev/null 2>&1 && echo “$p open” | tee >> $myFile;
> done
> fi
> #
> # Or No
> #
> for (( p=$myRange1; p<=$myRange2; p++ )) do
> (echo >/dev/tcp/localhost/$p) >/dev/null 2>&1 && echo “$p open” ;
> done

I have never tried looking for local ports this way. The much-faster way
is just to use netstat or ss output; as long as you’re on the local box
you can get all kinds of useful information. For example:

Code:

/usr/sbin/ss -utnlp #UDP, TCP, numeric, listening, show process

This output shows more than what the user may have explicitly requested,
but since it comes back instantaneously with just one command you could
then use grep to look for one or multiple ports:

Code:

/usr/sbin/ss -utnlp | grep -e ‘:389’ -e ‘:390’ -e ‘:391’ -e ‘:392’

Or if it’s not fun creating the list of grep -e options from your range,
then perhaps create one with ORing involved:

Code:

/usr/sbin/ss -utnlp | grep -e ‘:389\b|:390\b|:391\b|:392\b’

The only part of the ‘ss’ command that requires ‘root’, afaik, is the
resolving the socket back to a process, so it would be at this point that
you do something like, "If you want to see the process listening, use ‘root’.

> #
> echo
> echo -e "==============================
"
> echo -e "Scanning for open ports using netcat (nc)
"
> #
> # Lets try it with netcat
> # Note: on my default system I have the ‘netcat-openbsd’ package. Not sure if this will
> # + work with netcat-traditional.
> #
> if “$myAns” == “y” ]
> then
> nc -vz localhost $myRange1-$myRange2 2>&1 | grep succeeded | tee >> $myFile;
> fi
> #
> nc -vz localhost $myRange1-$myRange2 2>&1 |grep succeeded;

Scanning locally is kind of wasted since other commands list things so
quickly/easily. As a result I’d probably add a prompt before the port
range to see if you want to scan locally or some other address(es).

> #
> echo
> echo -e "==============================
"
> echo “Total number of ports scanned”
> #
> if “$myRange2” = “$myRange1” ]]
> then
> myScanned=1
> else
> let “myScanned = $myRange2 - $myRange1”
> fi
> echo "Ports Scanned = " $myScanned
> #
> # Lets us know script is done by sending us a beep.
> #
> echo -e “\a” > /dev/console;
> echo -e "==============================
"
> echo “tata”
> --------------------

If you’re interested in port scanning and other things in general, check
out nmap.


Good luck.

If you find this post helpful and are logged into the web interface,
show your appreciation and click on the star below…

Thanks for the response ab.

Yet, netcat is great. Note that on some distributions the command ‘nc’ is
used instead of netcat. I think openSUSE actually lets you use either
depending on which netcat package you’ve installed.

You are correct, I tried both and they both work. Being the lazy person I am, I chose to use ‘nc’ since it involved less typing.

Also, losing old data when
running a second time might be annoying. Check out the mktemp command to
easily find/use temporary files for output. You may also consider writing
permanent output files with timestamps in their name so that multiple
attempts do not overwrite old data unnecessarily. It’s not like you’re
dealing with GiB of data or anything.

True, which is why I chose to use “touch”. If the file doesn’t exist, touch creates it, if it does exist, touch appends to it.

I see no reason for this, other than your web solution is trying to write
to places it has no business writing and you have hard-coded paths to
/root. Unless you really, really need ‘root’, don’t require it. Port
scanning seldom requires root, and you can do 99.9% of what you want and
perhaps insert a note somewhere in the output stating, “If you want the
process tied to all listening ports on the local system, then run as ‘root’”.

Well, basically three reasons I have it run as root. First, being new to Linux and scripting, I wanted to see if I ‘could’ make it a requirement. Second, being nosey, I want to be able to see that .1%. Finally, I wanted my console to ‘beep’ lol!

Consolidate this code to a single branch earlier when the user chose to
use a file or not. At that point, if they choose to NOT write to a file,
set myFile to /dev/null and then you can eliminate the duplicate code
below here. Having the exact same code twice should be avoided whenever
possible since it makes maintenance a pain. Another option: Make this a
function that can be called and then call with a real file or /dev/null so
that reviewing your code and understanding its flow is easier. A function
named “scanPorts” that accepted the range of ports (including a single
port if only scanning a single port) and a target file (regular or
/dev/null) would simplify a lot of this.

I figured I was a mite slap-happy with the ‘ifs-thens’ and ‘cases’ so I will work on consolidating the code. As far as making a ‘function’, I’ve not gotten that far in my learning, but will read up on it and see what sort of monster I can create with it. :slight_smile:

I have never tried looking for local ports this way. The much-faster way
is just to use netstat or ss output; as long as you’re on the local box
you can get all kinds of useful information. For example:

Code:

/usr/sbin/ss -utnlp #UDP, TCP, numeric, listening, show process

This output shows more than what the user may have explicitly requested,
but since it comes back instantaneously with just one command you could
then use grep to look for one or multiple ports:

Code:

/usr/sbin/ss -utnlp | grep -e ‘:389’ -e ‘:390’ -e ‘:391’ -e ‘:392’

Or if it’s not fun creating the list of grep -e options from your range,
then perhaps create one with ORing involved:

Code:

/usr/sbin/ss -utnlp | grep -e ‘:389\b|:390\b|:391\b|:392\b’

The only part of the ‘ss’ command that requires ‘root’, afaik, is the
resolving the socket back to a process, so it would be at this point that
you do something like, "If you want to see the process listening, use ‘root’.

Excellent information I will try out. Actually, I started with this code that I got off the internet:

for (( p=$myRange1; p<=$myRange2; p++ )) do
    (echo >/dev/tcp/localhost/$p) >/dev/null 2>&1 && echo "$p open" ;
done

Then I just sort of built everything around it. To be honest, I don’t know if it’s good or bad code. I do know that while writing and testing the script I was dissatisfied with the run time and cpu usage and figured there had to be something better, which is how I stumbled upon netcat. So, yes, that code will be gone from the script.

Scanning locally is kind of wasted since other commands list things so
quickly/easily. As a result I’d probably add a prompt before the port
range to see if you want to scan locally or some other address(es).

You are absolutely correct. My whole goal, though, was to deal strictly with ‘localhost’ and honestly, when I first tried netstat and tcpdump I found myself just staring at the screen and drooling from information overload :O.

If you’re interested in port scanning and other things in general, check
out nmap.

Another one I was not aware of. I shall add that to my list of commands to check out. Great information, ab, appreciate you’re taking the time.

True, which is why I chose to use “touch”. If the file doesn’t exist, touch creates it, if it does exist, touch appends to it.

Nope, touch does not append anything. It touches the file, which in fact means

Update the access and modification times of each FILE to the current time.

And the creation of a non existing file is a by-product:

A FILE argument that does not exist is created empty, unless -c or -h is supplied.

Well for the details you can read the man page.

Maybe you are confusing this with output redirection from the shell where there is a difference between

>

and

>>

And yes, shell programming is for a large percentage knowledge of the existence of tools and how to knit them together with pipes, etc.

You’re right. I should read my own script comments :shame:. It’s the

 $[myVariable] >> $myFile

that does the appending. I agree with the “knowledge” part and for me, it’s the knitting them together and syntax that I am finding most difficult.

Well, I’ve made some changes. But have run into a problem which I hope you folks can help me with.
First off, here’s the revised code:

#!/bin/bash
#
# bash ver 4.2-68.1.5
# openSUSE 13.1
# A simple port scanner performed on 'localhost'
# Note: This script has to be run with root permissions to get full benefit
#
# Switch screens to start with a clean slate.
/usr/bin/clear
#
# Set some variables
myDate=$(date +"%A %B %d %Y %T %Z %n")
myFile="/root/open_ports.txt"
declare -i myRange1
declare -i myRange2
declare -i myScanned
#
# Colors. Not all colors are used. I just included them should you want to change.
#
ESC_SEQ="\x1b"
COL_RESET=$ESC_SEQ"39;49;00m"
COL_GREEN=$ESC_SEQ"32;01m"
COL_BLUE=$ESC_SEQ"34;01m"
COL_RED=$ESC_SEQ"31;01m"
COL_YELLOW=$ESC_SEQ"33;01m"
COL_MAGENTA=$ESC_SEQ"35;01m"
COL_CYAN=$ESC_SEQ"36;01m"
#
echo -e "==============================
"
if  "$(whoami)" != 'root' ];
    then
        echo -e "$COL_RED I'm sorry "$USER", you must be root to run this script.$COL_RESET"
        exit 1;
fi
echo -e "$COL_GREEN Hello, "$USER". this script will scan for open ports.$COL_RESET
"
echo -e "==============================
"
#
# ++++++++++ CREATE FILE IF NECESSARY ++++++++++
# Make sure they only enter y or n
#
while true
do
    echo -e "$COL_YELLOW"
    read -s -n1 -p " Do you want to save data to a file [y/n]: " myAns
    echo $myAns
    echo -e "$COL_RESET"
    case $myAns in
        y|Y)  # see if file /root/port_scanner.txt exists, If no, then create, if yes, move on
            touch $myFile;
            echo -e "========== Report Generated "$myDate >> $myFile
            echo -e "$COL_CYAN ++++++ File /root/open_ports.txt ready $COL_RESET 
"
            break
            ;;
        n|N)
            echo -e "$COL_CYAN ++++++ No file will be created or modified $COL_RESET 
"
            break
            ;;
        *)  # Presses something other than 'y' or 'n'
            echo -e "$COL_RED Smartass, just press y or n $COL_RESET"
            ;;
    esac
done
#
# ++++++++++ GET PORT OR PORT RANGE ++++++++++
#
echo -e "==============================
"
echo -e "$COL_CYAN"
echo " Ports are generally divided into three groups:"
echo "  **** Well Known Ports: 0-1023" # I am omitting '0' from this range and making 1 the minimum port
echo "  **** Registered Ports: 1024-49151"
echo -e "  **** Dynamic/Private Ports: 49152-65535 
"
echo -e "$COL_RESET"
echo -e "==============================
"
#
while true
do
    echo -e "$COL_YELLOW"
    read -s -n1 -p " Do you want to check a (s)ingle port or a (r)ange of ports [s/r]: " myOpt
    echo $myOpt
    echo -e "$COL_RESET"
    case $myOpt in
        s|S)
        echo -e "$COL_YELLOW"
            read -p " Enter the port you want to check [1-65535]: " myRange1
            echo -e "$COL_RESET"
            if  "$myRange1" -lt "1" ]] ||  "$myRange1" -gt "65535" ]]
                then
                echo -e "$COL_CYAN Must be in range of 1-65535 $COL_RESET"
                continue
            fi
            myRange2=$myRange1
            break
            ;;
        r|R)
            echo -e "$COL_YELLOW"
            read -p " Enter the first port you want to check : " myRange1
            echo -e "$COL_RESET"
            if  "$myRange1" -lt "1" ]] ||  "$myRange1" -gt "65535" ]]
                then
                echo -e "$COL_CYAN Must be in range of 1-65535 $COL_RESET"
                continue
            fi
            echo -e "$COL_YELLOW"
            read -p " Enter the ending port you want to check : " myRange2
            echo -e "$COL_RESET"
            if  "$myRange2" -lt "$myRange1"+1 ]] ||  "$myRange2" -gt "65535" ]]
                then
                echo -e "$COL_CYAN Must be greater than Range 1 and less than or equal to 65535 $COL_RESET"
                continue
            fi
            break
            ;;
        *)
            echo -e "$COL_RED Really? Out of range, try again Einstein $COL_RESET"
            continue
            ;;
    esac
done
#
# ++++++++++ TIME TO SCAN PORTS ++++++++++
#
# Print results to terminal and file
#
echo
echo -e "==============================
"
echo -e "$COL_CYAN Scanning for open ports using lsof $COL_RESET
"
#
if  "$myAns" == "y" ]
    then
    lsof -Pan -i tcp:$myRange1-$myRange2 -i udp:$myRange1-$myrange2 | tee >> $myFile;
fi
#
# Or just to terminal
#
echo -e "$COL_MAGENTA"
lsof -Pan -i tcp:$myRange1-$myRange2 -i udp:$myRange1-$myRange2;
echo -e "$COL_RESET"
#
echo
echo -e "==============================
"
echo -e "$COL_CYAN Total number of ports scanned $COL_RESET"
#
if  "$myRange2" = "$myRange1" ]]
  then
  myScanned=1
  else
  let "myScanned = $myRange2 - $myRange1"
fi
echo -e "$COL_CYAN Ports Scanned = $COL_RESET" $myScanned
echo
#
# Lets us know script is done by sending us a beep.
#
echo -e "\a" > /dev/console;
echo -e "==============================
"
echo -e "$COL_CYAN tata $COL_RESET"
#

When I run it without sending to file, i.e.:

lsof -Pan -i tcp:$myRange1-$myRange2 -i udp:$myRange1-$myRange2;

I get this (I’ll just use a single port- 25, in order to save space):

COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
master  1491 root   12u  IPv4  10838      0t0  TCP 127.0.0.1:25 (LISTEN)
master  1491 root   13u  IPv6  10839      0t0  TCP ::1]:25 (LISTEN)

If I run it just to $myFile (No terminal display)

lsof -Pan -i tcp:$myRange1-$myRange2 -i udp:$myRange1-$myRange2 >> $myFile;

File contents are:

COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
master  1491 root   12u  IPv4  10838      0t0  TCP 127.0.0.1:25 (LISTEN)
master  1491 root   13u  IPv6  10839      0t0  TCP ::1]:25 (LISTEN)

But if I ‘tee’ it so I get screen and file:

lsof -Pan -i tcp:$myRange1-$myRange2 -i udp:$myRange1-$myrange2 | tee >> $myFile;

I get this on screen:

lsof: unacceptable port specification in: -i udp:25-
lsof 4.84
 latest revision: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/
 latest FAQ: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/FAQ
 latest man page: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/lsof_man
 usage: -?abhKlnNoOPRtUvVX] +|-c c] +|-d s] +D D] +|-f[gG]]
 -F [f]] -g [s]] -i *] +|-L [l]] +m [m]] +|-M] -o [o]] -p s]
+|-r [t]] -s [p:s]] -S [t]] -T [t]] -u s] +|-w] -x [fl]] --] [names]
Use the ``-h'' option to get more help information.

COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
master  1491 root   12u  IPv4  10838      0t0  TCP 127.0.0.1:25 (LISTEN)
master  1491 root   13u  IPv6  10839      0t0  TCP ::1]:25 (LISTEN)

and a file content of:

========== Report Generated Wednesday June 11 2014 17:41:29 EDT

Am I missing something with the ‘tee’ command?

Thanks all.*

Oh geez… just noticed after posting:

if  "$myAns" == "y" ]
    then
    lsof -Pan -i tcp:$myRange1-$myRange2 -I udp:$myRange1-$myrange2 | tee >> $myFile;
fi

Didn’t put a capital ‘R’ in udp:$myRange1-$myrange2. >:) :’( :shame:. All freakin day I puzzled over this… *head slap"

While here though, the reason I went with ‘lsof’ rather than nc (netcat), netstat (being deprecated) or ss was lsof provides me with more of the info I’m looking for. I also didn’t go with nmap because it’s not included with my install and I’m trying to stick with those commands and features that come “out-of=the-box”. But, yes, I did try all of them. :slight_smile:

On 2014-06-08 19:46, hcvv wrote:
>
> Sorry, I did not take the time to read through al of it. but I saw your
> question about “wich program is listening to that port” (if I interprete
> correctly). Of course that can not be found out by a portscanner
> (working from “outside”),

nmap does educated guesses analyzing the exact responses it gets. For
instance, a telnet to an openSUSE machine would get the string “Welcome
to openSUSE…”, probably.


Cheers / Saludos,

Carlos E. R.
(from 13.1 x86_64 “Bottle” at Telcontar)

All right then. Here is the final version of my simple port scanner. Added some color (for the Terminal output), removed some columns I wasn’t interested in and added a customized header. I now have the information I wanted without all the ‘extras’ that I have no clue as to what it means. I left in all my comments in case there is another bash noob like me who wants to know what I did and why. I also tried to put as much into variables so it would be easy to change file names and or locations, should one so desire.

Here is the script:

#!/bin/bash
#
# bash ver 4.2-68.1.5
# openSUSE 13.1
# A simple port scanner performed on 'localhost'
# Note: This script has to be run with root permissions to get full benefit
#
# Switch screens to start with a clean slate.
/usr/bin/clear
#
# Set some variables
#
myDate=$(date +"%A %B %d %Y %T %Z %n")
myFile="/root/open_ports.txt"
myTemp="/root/tmp_open_ports.txt"
#
# Declare some integers
#
declare -i myRange1
declare -i myRange2
declare -i myScanned
#
# Colors. Not all colors are used. I just included them should you want to change.
#
ESC_SEQ="\x1b"
COL_RESET=$ESC_SEQ"39;49;00m"
COL_GREEN=$ESC_SEQ"32;01m"
COL_BLUE=$ESC_SEQ"34;01m"
COL_RED=$ESC_SEQ"31;01m"
COL_YELLOW=$ESC_SEQ"33;01m"
COL_MAGENTA=$ESC_SEQ"35;01m"
COL_CYAN=$ESC_SEQ"36;01m"
#
echo -e "==============================
"
if  "$(whoami)" != 'root' ];
    then
        echo -e "$COL_RED I'm sorry "$USER", you must be root to run this script.$COL_RESET"
        exit 1;
fi
echo -e "$COL_GREEN Hello, "$USER". this script will scan for open ports.$COL_RESET
"
echo -e "==============================
"
#
# ++++++++++ CREATE FILE IF NECESSARY ++++++++++
# Make sure they only enter y or n
#
while true
do
    echo -e "$COL_YELLOW"
    read -s -n1 -p " Do you want to save data to a file [y/n]: " myAns
    echo $myAns
    echo -e "$COL_RESET"
    case $myAns in
        y|Y)  # see if file /root/port_scanner.txt exists, If no, then create, if yes, move on
            #echo "
"
            echo -e "
========== Report Generated "$myDate >> $myFile
            echo -e "$COL_CYAN ++++++ File /root/open_ports.txt ready $COL_RESET 
"
            break
            ;;
        n|N)
            echo -e "$COL_CYAN ++++++ No file will be created or modified $COL_RESET 
"
            break
            ;;
        *)  # Presses something other than 'y' or 'n'
            echo -e "$COL_RED Smartass, just press y or n $COL_RESET"
            ;;
    esac
done
#
# ++++++++++ GET PORT OR PORT RANGE ++++++++++
#
echo -e "==============================
"
echo -e "$COL_CYAN"
echo " Ports are generally divided into three groups:"
echo "  **** Well Known Ports: 0-1023" # I am omitting '0' from this range and making 1 the minimum port
echo "  **** Registered Ports: 1024-49151"
echo -e "  **** Dynamic/Private Ports: 49152-65535 
"
echo -e "$COL_RESET"
echo -e "==============================
"
#
while true
do
    echo -e "$COL_YELLOW"
    read -s -n1 -p " Do you want to check a (s)ingle port or a (r)ange of ports [s/r]: " myOpt
    echo $myOpt
    echo -e "$COL_RESET"
    case $myOpt in
        s|S)
        echo -e "$COL_YELLOW"
            read -p " Enter the port you want to check [1-65535]: " myRange1
            echo -e "$COL_RESET"
            if  "$myRange1" -lt "1" ]] ||  "$myRange1" -gt "65535" ]]
                then
                echo -e "$COL_CYAN Must be in range of 1-65535 $COL_RESET"
                continue
            fi
            myRange2=$myRange1
            break
            ;;
        r|R)
            echo -e "$COL_YELLOW"
            read -p " Enter the first port you want to check : " myRange1
            echo -e "$COL_RESET"
            if  "$myRange1" -lt "1" ]] ||  "$myRange1" -gt "65535" ]]
                then
                echo -e "$COL_CYAN Must be in range of 1-65535 $COL_RESET"
                continue
            fi
            echo -e "$COL_YELLOW"
            read -p " Enter the ending port you want to check : " myRange2
            echo -e "$COL_RESET"
            if  "$myRange2" -lt "$myRange1"+1 ]] ||  "$myRange2" -gt "65535" ]]
                then
                echo -e "$COL_CYAN Must be greater than Range 1 and less than or equal to 65535 $COL_RESET"
                continue
            fi
            break
            ;;
        *)
            echo -e "$COL_RED Really? Out of range, try again Einstein $COL_RESET"
            continue
            ;;
    esac
done
#
# ++++++++++ TIME TO SCAN PORTS ++++++++++
#
echo
echo -e "==============================
"
echo -e "$COL_CYAN Scanning for open ports using lsof $COL_RESET
"
#
# Now lets put what we have in a temporary file
#
lsof -cutPn -i tcp:$myRange1-$myRange2 -i udp:$myRange1-$myRange2 >> $myTemp
#
# Remove header so we can create our own
#
sed -i '/COMMAND/d' $myTemp
#
# Print results to terminal and file
#
if  "$myAns" == "y" ]
    then
    awk '
    BEGIN {
    print "========================================================================"
    printf "%-12s %-5s %-6s %-8s %-6s %-40s
","COMMAND","PID","USER","TYPE","PROT","NAME"
    print "========================================================================"
    }
    { printf "%-12s %5d %-6s %-8s %-6s %-40s
",$1,$2,$3,$5,$8,$9 } ' $myTemp | tee >> $myFile
fi
#
# Or just to terminal
#
echo -e "$COL_MAGENTA"
awk '
BEGIN {
print "========================================================================"
printf "%-12s %-5s %-6s %-8s %-6s %-40s
","COMMAND","PID","USER","TYPE","PROT","NAME"
print "========================================================================"
}
{ printf "%-12s %5d %-6s %-8s %-6s %-40s
",$1,$2,$3,$5,$8,$9 } ' $myTemp
#
echo -e "$COL_RESET"
#
echo
echo -e "==============================
"
echo -e "$COL_CYAN Total number of ports scanned $COL_RESET"
#
if  "$myRange2" = "$myRange1" ]]
  then
  myScanned=1
  else
  let "myScanned = $myRange2 - $myRange1"
fi
echo -e "$COL_CYAN Ports Scanned = $COL_RESET" $myScanned
echo
#
# Delete our temporary file
#
rm $myTemp
#
# Lets us know script is done by sending us a beep.
#
echo -e "\a" > /dev/console;
echo -e "==============================
"
echo -e "$COL_CYAN tata $COL_RESET"
#

And here is the output:

[FONT=fixedsys][FONT=courier new]
========== Report Generated Monday June 16 2014 18:06:55 EDT

COMMAND PID USER TYPE PROT NAME

avahi-dae 396 avahi IPv4 UDP *:mdns
avahi-dae 396 avahi IPv6 UDP *:mdns
avahi-dae 396 avahi IPv4 UDP *:50652
avahi-dae 396 avahi IPv6 UDP *:40489
ntpd 1244 ntp IPv4 UDP *:ntp
ntpd 1244 ntp IPv6 UDP *:ntp
ntpd 1244 ntp IPv4 UDP localhost:ntp
ntpd 1244 ntp IPv4 UDP lenovo.local.loc:ntp
ntpd 1244 ntp IPv6 UDP localhost:ntp
ntpd 1244 ntp IPv6 UDP [fe80::221:86ff:fe18:3220]:ntp
nmbd 1261 root IPv4 UDP *:netbios-ns
nmbd 1261 root IPv4 UDP *:netbios-dgm
nmbd 1261 root IPv4 UDP lenovo.local.loc:netbios-ns
nmbd 1261 root IPv4 UDP 192.168.10.255:netbios-ns
nmbd 1261 root IPv4 UDP lenovo.local.loc:netbios-dgm
nmbd 1261 root IPv4 UDP 192.168.10.255:netbios-dgm
hddtemp 1269 root IPv4 TCP *:7634
master 1377 root IPv4 TCP localhost:smtp
master 1377 root IPv6 TCP localhost:smtp
smbd 1395 root IPv6 TCP *:microsoft-ds
smbd 1395 root IPv6 TCP *:netbios-ssn
smbd 1395 root IPv4 TCP *:microsoft-ds
smbd 1395 root IPv4 TCP *:netbios-ssn
miniserv. 1397 root IPv4 TCP *:ndmp
miniserv. 1397 root IPv4 UDP *:ndmp
cupsd 1408 root IPv6 TCP localhost:ipp
cupsd 1408 root IPv4 TCP localhost:ipp
cupsd 1408 root IPv4 UDP *:ipp [/FONT]
[/FONT]

Hope someone finds all or part of it useful :slight_smile:

Don’t know why the table isn’t lined up in the post, but works fine on term.

I’ll bet by now, you’ve realized that multiple spaces and tabs are converted to single spaces when posted on most HTML sites.

Bart

You’d think at my age I’d have learned something lol!

Hi folks,

Since Port scanning is the topic in this thread i would like to share my own “Port Scanner”

#!/bin/bash

#
# Description: Port scanner on a subnet you are in.
# Author: jechisel '<jetchisel@opensuse.org>'
# License: WTFPL  http://www.wtfpl.net/
# Note: Kids make sure you are allowed to scan the subnet :-) 
#

Opts=(-P0 -n -sS --max_hostgroup 1 --max_retries 0 --max_parallelism 10)

Nets=()
while read -r line; do
  Nets+=("$line")
done < <(ip r | awk '//].*dev/{printf("%-20s%s
", $3, $1)}')

gb=$(tput setaf 2 && tput bold)
reset=$(tput sgr0)

for i in "${!Nets@]}"; do
  number+=("$i")
  alphanum+=("${Nets*/#/$gb$i$reset) }") 
done

printf -v num "%s|" "@(${number@]})" 
num=${num%|}
blank=

Networks=(
 "============================================"
 "#   SELECT THE NUMBER OF NETWORK TO SCAN   #"
 "============================================"
)

MainMenu=(
  "${Networks@]}"
  "$blank"
  "${alphanum@]}"
  "$blank"
  "([Enter ${gb}number${reset}] )=> "
)

printf -v menu '%s
' "${MainMenu@]}"
menu=${menu%$'
'}

Checkuid() {
  NotRoot=$'You requested a scan type which requires root privileges.
QUITTING!'
  (( EUID == 0 )) || {
    echo "$NotRoot" >&2
    break
  }
}

while true; do
  clear
  read -r -e -p "$menu" 
    if  ${REPLY} = $num ]]; then
      clear
      Checkuid 
      printf '%s
' "Please wait while we scan $gb${Nets${REPLY}]##* }$reset"
      grep -v "^Warning" < <(nmap "${Opts@]}" "${Nets${REPLY}]##* }") && break
    elif  ${REPLY} = @([Qq]|[Qq][Uu][Ii][Tt]) ]]; then
      clear
      echo 'Bye!'
      exit 0
    else
      echo "Invalid option" >&2
      sleep 2 
    fi
done

# END-OF-SCRIPT

You can download the script in my gisthub and from paste.opensuse.org

https://gist.githubusercontent.com/Jetchisel/36c6dd56a58e84142cac/raw/70340798f4e2aa23cdee5404b2e2ca896f5f9a53/snmap

or

http://paste.opensuse.org/view/raw/41407689

to download you can do:

curl -s  http://paste.opensuse.org/view/raw/41407689  > myscript

make it executable

chmod +x myscript

The nmap code that is used here requires you to be root but you can run the script without being root :slight_smile:
Now the name myscript is arbitary. Just remember kids make sure you have permission to scan the subnet. rotfl!*

Forgot to add, you can quit the script by typing

quit 

case insensitive

but feel free to add that option on the Menu :slight_smile:

Ok, some additional patching :slight_smile:

Replace the awk part from this

awk '//].*dev/{printf("%-20s%s
", $3, $1)}'

To this

awk '//].*dev.*src/{printf("%-20s%s
", $3, $1)}'

Just an additional .*src after the dev :wink: