#!/bin/bash
# nmap-scanner.sh V1.0
# My friend N8 had the great idea of nmaping a netblock and then running a diff to see if any new ports (HACKERS!!)
# appeared on his network or any old ports had vanished (daemon died!!). He posted about it on his blog page at
# http://www.mybrainhurts.com/blog/2009/03/automated-nmap-scans.html
# I really liked his idea but struggled a little with the implementation since i wanted the script to be noisy
# at first (for testing) and quiet later. I started hacking in commandline switches and changing the generic
# Bourne shell code into BASH code, and here is the no longer terse result.
#
#REQUIREMENTS
# *Nmap
# *Cron (for automation)
#
#INSTALL
# The first time you run the script you need to start it with the -i option to initilize the data files. You will get an error otherwise.
# This script is designed to be placed in your root crontab file (via crontab -e), here is an example entry (the -q makes it quiet).
# 32 15 * * * /usr/local/bin/nmap-scanner.sh -q
# The following 4 options are overwriteable via commandline switches, run script with -h for options.
EMAIL="" # Optional email address to notify, defaults to STDOUT if none listed
BASE_DIR="/var/log/nmap/scans" # Directory to store the nmap logfiles
NETWORKS=(127.0.0.1 192.168.0.1-254) # Networks to scan, space delimited for multiple networks
INIT=0 # Set to 1 via -i option upon first run of script to seed the logfile
#function to handle error exiting
die() {
echo "ERROR: $@" 1>&2
exit 1;
}
#processes commandline options
while getopts ":qid:n:e:h" OPT; do
case "$OPT" in
q) QUIET=1;;
i) INIT=1;;
d) DIR="$OPTARG";;
n) NETWORKS="$OPTARG";;
e) EMAIL="$OPTARG";;
h) HELP=1;;
\?) HELP=1;;
esac
done
#print help message and exit
if [[ -n ${HELP} ]];then
echo "$0 usage:"
cat <<EOF
-q Do not print progress messages to stdout
-i Seed logfiles, for first run of script
-d Base directory for logfiles
-n Networks to scan (space delimited) ex. -n "127.0.0.1 192.168.0.1-254"
-e Optional email address to mail to, defaults to STDOUT if none listed
EOF
die "Unrecognized option(s): $@"
fi
#Check that nmap is installed and in their current path
if ! command -v nmap >/dev/null 2>&1; then
die "Sorry $0 requires NMAP to be installed and in your path of ($PATH)"
fi
mkdir -p "${BASE_DIR}" >/dev/null 2>&1
TODAY=$(date +%Y%m%d-%s)
for NETWORK in ${NETWORKS[@]}; do
DIR="$BASE_DIR/${NETWORK}"
mkdir -p "$DIR/state/" #create a directory for each network to be scanned, and a state directory for tracking last scan symlinks
if ! mv -f ${DIR}/state/new ${DIR}/state/old; then #Rename the symlink for the newest scan to old (overwriting the old symlink in the process)
if [[ ${INIT} -eq 0 ]]; then
die "An error occured while trying to roll off the log of the last scan. If this is first time running $0 use the --init option to override"
fi
fi
if [[ -z $QUIET ]];then
echo -ne "\nScanning network:${NETWORK}:\n"
fi
if nmap -R -sS ${NETWORK} -oN $DIR/${TODAY}.nmap >/dev/null 2>&1; then #Actually perform the nmap scan
ln -fs ${DIR}/${TODAY}.nmap ${DIR}/state/new #create the .new symlink for this network
if [[ ${INIT} -eq 1 ]]; then
ln -fs ${DIR}/${TODAY}.nmap ${DIR}/state/old #if this is the first run (--init) then seed old with new
fi
else
die "An errror occured while trying to nmap the network:${NETWORK}"
fi
#check for diffrences between this run and the last run
DIFF=$(diff -C0 --show-function-line='Interesting' --ignore-matching-lines='^#' ${DIR}/state/new ${DIR}/state/old |grep -v '\*\*\*' |grep -v '\-\-\-')
if [[ $? -eq 2 ]];then
die "the diff of ( ${DIR}/state/new ${DIR}/state/old) failed to execute properly"
fi
if [[ -n ${DIFF} ]]; then #if there are diffrences alert the user
if [[ -n ${EMAIL} ]]; then
echo ${DIFF} | mail -s "Change Detected for ${NETWORK}" ${EMAIL}
else
echo "The following changes were detected for network: ${NETWORK}"
echo "${DIFF}"
fi
fi
done