Skip to content

Instantly share code, notes, and snippets.

@JonathanLPoch
Last active October 4, 2022 15:56
Show Gist options
  • Save JonathanLPoch/bb958e0069b6cd604d18381215caa253 to your computer and use it in GitHub Desktop.
Save JonathanLPoch/bb958e0069b6cd604d18381215caa253 to your computer and use it in GitHub Desktop.
Lightweight Nmap Topology Scanning
#!/bin/sh
DEFAULTNMAPOPTIONS="-T4 -sV -Pn --top-ports 5000 -R"
NMAPOPTIONS="$DEFAULTNMAPOPTIONS"
die() {
printf '\033[38;5;9m%s\033[0m\n\n' "$1" >&2
display_usage
exit 1
}
user_abort() {
printf '\033[38;5;9mAborting.\033[0m\n'
exit 0
}
display_usage() {
printf 'Carve Systems Nmap network scanner.\n'
printf 'Usage: ./nmap-diff.sh [options].\n\n'
printf 'REQUIRED\n'
printf '%s: The input file with an IP list.\n' "-f|-f"
printf '%s: Your email address.\n' "-e"
printf '%s: The output directory.\n' "-o|--output"
printf '%s: Public IP. Set a required public IP to scan from.\n\n' "-i"
printf 'OPTIONAL\n'
printf '%s: Display this help summary.\n' "-h|--help"
printf "%s: Nmap options. Default is \"$DEFAULTNMAPOPTIONS\"\n\n" "-n"
printf 'EXAMPLE\n'
printf '%s\n\n' "./nmap-diff.sh -f ips.txt -e email@gmail.com -o output/dir/nmap-scanning/ -i 1.2.3.4"
}
prompt() {
printf "%s (y/n)" "$1"
read -r reply
[ "$reply" == "${reply#[Yy]}" ] && return 1
return 0
}
send_mail() {
diff=$1
date=$2
email=$3
if prompt "Send mail?";
then
printf "Sending %s mail.\n" "$diff"
if [ "$diff" = diff ]
then
if ! printf "Hi,\n\n Note that this is an automated email.\n\nPlease see the following message and attachments for the results of your network topology scan and let us know if you have any questions.\n\n***NDIFF SCAN RESULTS:\n%s and $date***\n\n%s\n\n" "$(readlink scan-prev.xml | cut -d "/" -f1)" "$(tail -n +2 "$date/diff-$date")" | EMAIL="$email" mutt -s "Nmap Scan Results - Periodic Diff Scan" -a "$date/scan-$date.nmap" "$date/diff-$date" -- "$email" > /dev/null;
then
printf 'Mailing failed - ensure mutt is installed properly. You can find your results in \033[1m%s\033[0m\n' "$output_dir/$date/*"
fi
else
if ! printf "Hi,\n\n Note that this is an automated email.\n\nPlease see the following message and attachments for the results of your network topology scan and let us know if you have any questions.\n\n%s\n\n" "$(tail -n +2 "$date/scan-$date.nmap")" | EMAIL="$email" mutt -s "Nmap Scan Results - Initial Baseline Scan" -a "$date/scan-$date.nmap" -- "$email" > /dev/null;
then
printf 'Mailing failed - ensure mutt is installed properly. You can find your results in \033[1m%s\033[0m\n' "$output_dir/$date/*"
fi
fi
else
printf '\nSkipping - you can find your results in \033[1m%s\033[0m\n\n' "$output_dir/$date/*"
fi
}
while getopts 'e:f:hi:n:o:' OPTION; do
case $OPTION in
e )
if [ -n "$OPTARG" ]; then
EMAIL="$OPTARG"
else
die 'ERROR: Missing user email.'
fi
;;
f )
if [ -n "$OPTARG" ]; then
FILE="$OPTARG"
else
die 'ERROR: Missing file argument.'
fi
;;
h )
display_usage
exit
;;
n )
if [ -n "$OPTARG" ]; then
NMAPOPTIONS=$OPTARG
else
die 'ERROR: Missing nmap options.'
fi
;;
i )
if [ -n "$OPTARG" ]; then
PUBLICIP=$OPTARG
else
die 'ERROR: Missing public IP.'
fi
;;
o )
if [ -n "$OPTARG" ]; then
output_dir=$OPTARG
else
die 'ERROR: Missing output directory.'
fi
;;
\? )
display_usage
exit
;;
esac
done
shift "$((OPTIND -1))"
if [ -z "$FILE" ] || [ -z "$EMAIL" ] || [ -z "$output_dir" ] || [ -z "$PUBLICIP" ]; then
die 'ERROR: Missing required arguments.'
display_usage
fi
if [ -n "$PUBLICIP" ];
then
publicip=$(dig @resolver1.opendns.com ANY myip.opendns.com +short)
if ! [ "$publicip" = "$PUBLICIP" ]; then
printf 'Public IP does not match what was specified. Aborting scan.\n'
exit 2
fi
fi
if ! [ "$FILE" ];
then
die 'ERROR: An input file is required.'
fi
if ! [ -e "$FILE" ];
then
die 'ERROR: Invalid input file.'
fi
if ! [ "$EMAIL" ];
then
die 'ERROR: An email is required.'
fi
if ! [ "$output_dir" ];
then
die 'ERROR: An output directory is required.'
fi
case "$output_dir" in
/*) ;;
*) output_dir="$(pwd)/$output_dir" ;;
esac
if [ -d "$output_dir" ]; then
dir_exists=true
else
dir_exists=false
fi
if [ "$dir_exists" = true ] ; then
printf 'Using output directory \033[1m%s\033[0m\n' "$output_dir"
if ! prompt "Are you sure?"; then
user_abort
fi
else
printf 'Directory does not currently exist. Creating directory %s\n' "$output_dir"
if prompt "Are you sure?"; then
printf '\nCreating %s...\n' "$output_dir"
mkdir -p "$output_dir"
else
user_abort
fi
fi
printf '\033[1m\nInitiating scan from public IP: %s for the following IPs:\n%s\033[0m\n\n' "$publicip" "$(cat "$FILE")"
date=$(date '+%Y-%m-%d %I-%M %p')
cd "$output_dir"
mkdir -p "$output_dir/$date"
if ! nmap $NMAPOPTIONS -iL "../$FILE" -oA "$output_dir/$date/scan-$date" > /dev/null;
then
die 'ERROR: nmap scan failed. Rerun scan manually.'
fi
printf 'Nmap scan completed. '
if prompt "Display results?"; then
printf '\n\033[1m*** NMAP RESULTS ***\n\n'
cat "$date/scan-$date.nmap"
printf '\033[0m\n'
else
printf 'Skipping - you can find your results in %s\n' "$date/scan-$date.*"
fi
if [ -L scan-prev.xml ]; then
printf 'Found previous scan. Performing ndiff...\n\n'
ndiff "scan-prev.xml" "$date/scan-$date.xml" > "$date/diff-$date"
code=$?
echo $code
if [ $code -eq 2 ];
then
die 'ERROR: ndiff failed. Ensure ndiff is in your PYTHONPATH and try again.\n\n'
else
if [ $code -eq 0 ];
then
printf "No differences detected. "
send_mail diff "$date" "$EMAIL"
fi
if [ $code -eq 1 ];
then
printf 'Network changes detected. Please review:\n\n'
printf '\033[1m*** NDIFF RESULTS ***\n'
cat "$output_dir/$date/diff-$date"
printf '\033[0m\n'
send_mail diff "$date" "$EMAIL"
fi
fi
printf 'Ndiff scan completed. To automate this process, create a cronjob:\n\n'
else
send_mail baseline "$date" "$EMAIL"
printf 'First scan completed. To automate this process, create a cronjob:\n\n'
fi
printf '\033[38;5;11m1. crontab -e\033[0m\n'
printf '\033[38;5;11m2. Within the crontab:\n# Nmap Scanning - 2AM Daily\n0 2 * * * cd /home/user/nmap-topology-scanner && yes | sh nmap-diff.sh\033[0m\n'
ln -sf "$date/scan-$date.xml" scan-prev.xml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment