Skip to content

Instantly share code, notes, and snippets.

@FlyingFathead
Last active February 2, 2024 18:47
Show Gist options
  • Save FlyingFathead/944ee5194eb1526d125aff5d06eb0cb8 to your computer and use it in GitHub Desktop.
Save FlyingFathead/944ee5194eb1526d125aff5d06eb0cb8 to your computer and use it in GitHub Desktop.
scanlan / a quick LAN scanning script for diagnostics
#!/bin/bash
#
# >>> SCANLAN <<<
#
# this script can used for i.e. checking out what's on your LAN
# requires `nmap`, `xmlstarlet` and `lolcat` (just because)
# adjust to your own ip range as needed.
# NO WARRANTIES, use only for your own LAN diagnostics and at your own risk
#
#
# program info
scanlan_ver='v0.82 -- 02 Feb. 2024 (c) 2021-2024 FlyingFathead'
# set horizontal line
function viibla() {
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - ;
}
function viivo() {
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - | lolcat -f;
}
# set different echo modes
function ekho() {
echo -e "::: \e[0m$1"
}
function ekko() {
echo -e "::: \e[1m$1\e[0m"
}
function ekk0() {
echo -e "\e[1m$1\e[0m" | lolcat -f ;
}
# redefine terminal colors using octal escape
prn_pun="\033[31m" # red
prn_vih="\033[32m" # green
prn_kel="\033[93m" # yellow
prn_val="\033[97m" # white
prn_reset="\033[0m" # reset
# function to check for required dependencies
check_dependency() {
if ! command -v "$1" &> /dev/null
then
echo "Error: $1 is not installed." >&2
echo "Please install $1 and try again." >&2
exit 1
fi
}
# check each dependency
check_dependency "nmap"
check_dependency "xmlstarlet"
check_dependency "lolcat"
# set our log files + their directory
scanlanlog_dir="$HOME/.logs/scanlan"
scanlanlog_file="$(date +"%Y_%m_%d___%H_%M-%S").log"
export scanlanlog_dir
export scanlanlog_file
# nmap in xml output mode
nmap_xml_output="$scanlanlog_dir/nmap_scan_$(date +"%Y_%m_%d___%H_%M-%S").xml"
export nmap_xml_output
# Define the IP range to scan
ip_range="192.168.100.0/24"
export ip_range
# define the scan command and its arguments as an array
scan_command=(sudo nmap -sS -O -v -oX "$nmap_xml_output" "$ip_range")
function set_log_dirs() {
# create logfile directory if it's not around
if [ ! -d "$scanlanlog_dir" ]; then
viibla &&
echo "[INFO] $scanlanlog_dir -- directory doesn't exist, creating it." &&
mkdir -p "$scanlanlog_dir" &&
viibla
fi
##
if [ ! -d "$scanlanlog_dir" ]; then
viibla &&
ekko "$prn_pun" '[ERROR!]' "$scanlanlog_dir -- unable to create directory!" &&
ekko "$prn_pun" '[ERROR!]' "Exiting!" &&
viibla &&
exit 0
fi
# file-friendly date = $(date +"%Y_%m_%d___%H_%M-%S").log
export scanlanlog_file
}
# function to highlight default gateway and _gateway entries
highlight() {
local default_gw="$1"
local red="${prn_pun}"
local yellow="${prn_kel}"
local reset="${prn_reset}"
awk -v default_gw="$default_gw" -v red="$red" -v yellow="$yellow" -v reset="$reset" '
{
# Check if the line contains the default gateway as a whole word
if ($0 ~ "\\<" default_gw "\\>") {
gsub("\\<" default_gw "\\>", red default_gw reset);
}
if (index($0, "_gateway") != 0) {
gsub("_gateway", yellow "_gateway" reset);
}
print;
}'
}
# function to list local ip's
function list_local_ips() {
ekko "Local Network Interfaces and IP Addresses:" &&
viivo &&
# Use `ip` command to list IPs for all interfaces
ip -br address | awk '$1 != "lo" {print $1, $3}' | while read -r interface ip_address; do
echo -e "Interface: \e[1m$interface\e[0m, IP Address: $ip_address"
done
viivo &&
echo ""
}
# function to scan the LAN
function scanlan() {
echo "" && echo "" &&
viivo &&
ekk0 "::: SCANLAN - $scanlan_ver" &&
viivo &&
# run the sudo check
ekko "This tool must be run as 'sudo'. If prompted, enter your sudo password." &&
viibla &&
sudo echo "" &&
echo "" &&
# notes ...
# export output to log _with_ ANSI codes ===>>>
# exec > >(tee -i "$scanlanlog_dir/$scanlanlog_file")
# export output to log _without_ ANSI codes ===>>>
#
# logging ...
exec > >( tee >( sed 's/\x1B\[[0-9;]*[JKmsu]//g' >> "$scanlanlog_dir/$scanlanlog_file" ) )
exec 2>&1
viibla &&
ekho "Log started into: $scanlanlog_dir/$scanlanlog_file"
viibla &&
# Read default gateways into an array and remove duplicates
readarray -t defugateways < <(/sbin/ip route | awk '/default/ { print $3 }' | sort -u)
# Output the gateways and check for multiple gateways
if [ ${#defugateways[@]} -gt 1 ]; then
# Join the array elements into a comma-separated string
gateway_str=$(IFS=, ; echo "${defugateways[*]}")
# Display a warning message in red if multiple gateways are detected
ekko "$prn_pun[WARNING] Multiple default gateways detected with 'ip route': $gateway_str"
elif [ ${#defugateways[@]} -eq 1 ]; then
# Display the single gateway
ekko "Default gateway according to 'ip route': ${defugateways[0]}"
else
# No gateways found
ekko "No default gateway found"
fi
viibla &&
ekko "Plain results from 'ip route':" &&
viibla &&
ip route &&
viibla &&
ekko "ARP check, from 'arp':"
viibla &&
arp | highlight "$default_gateway"
viibla &&
ekko "Note: Default gateway should be shown highlighted in red, other gateways (i.e. ones marked as '_gateway') in yellow."
viibla &&
echo "(NOTE: The table shows the IP addresses in the left column, and MAC addresses in the middle. If the table contains two different IP addresses that share the same MAC address, then you are probably undergoing an ARP poisoning attack [unless you are using very special types of routings or setups, such as multiple NIC's etc.])" &&
viibla &&
ekko "Scanning the LAN with: $(printf '%s ' "${scan_command[@]}")" &&
viibla &&
"${scan_command[@]}"
echo "" &&
echo "" &&
viivo &&
ekko "SCANLAN finished at: $(date)"
ekko "SCANLAN results log at: $scanlanlog_dir/$scanlanlog_file"
viivo &&
# process the XML output to list active systems and their details
if command -v xmlstarlet >/dev/null; then
ekko "Active LAN IP's and their details:"
viivo &&
xmlstarlet sel -t -m "//host[status/@state='up']" \
-v "address[@addrtype='ipv4']/@addr" -o " " \
-v "address[@addrtype='mac']/@addr" -o " (" \
-v "address[@addrtype='mac']/@vendor" -o ")" -n \
-m "ports/port" -o " Open Port: " -v "@portid" \
-o " (" -v "state/@state" -o ", " -v "service/@name" -o ")" -n \
-m "os/osmatch" -o " OS Estimate: " -v "@name" -n \
"$nmap_xml_output" | while read -r line; do
if [[ $line =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]]; then
echo -e "\e[1mIP: $line\e[0m" # Highlight IP in bold
else
echo "$line" # Normal output for other lines
fi
done
else
echo "xmlstarlet is not installed. Can't process the XML output. You can install xmlstarlet with: 'sudo apt-get install xmlstarlet'"
fi
# call the function to list local IPs
list_local_ips
viivo &&
# done.
# query to read the scan log
while true
do
read -r -p "Read the log now (answering [n]o will quit)? [Y/n] " input
case $input in
[yY][eE][sS]|[yY])
less "$scanlanlog_dir/$scanlanlog_file" &&
echo "" &&
viivo &&
echo ""
break
;;
[nN][oO]|[nN])
exit 0
break
;;
*)
echo "Invalid input!"
;;
esac
done
echo ""
}
# run the main program =>
# set the log file + directory
set_log_dirs
# run the scan
scanlan
# display results
viivo &&
ekko "SCANLAN results log at: $scanlanlog_dir/$scanlanlog_file"
viivo &&
# more reading
function info() {
printf "
More reading at:
https://nmap.org/book/osdetect-usage.html -- Usage.
"
viivo
}
# EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment