code-library/Network/Investigation/tcp-investigation.sh
2022-02-14 12:10:12 +01:00

217 lines
6.3 KiB
Bash
Executable file

#!/bin/bash
: '
Author: netico <netico@riseup.net>
Thu 13 Jan 2022 05:43:29 PM CET
Mon 17 Jan 2022 11:33:08 AM CET
Fri 11 Feb 2022 10:26:56 AM CET
TCP investigation script
'
# CONFIGURATION
# Sender
SENDER="someone@domain.tld"
# Recipient
RECIPIENT="someone.else@domain.tld"
# Mail relay URL
RELAY="smtp://powerful.mail.server:587"
# ERRORS HANDLING
E=false
if [[ $EUID -ne 0 ]]
then
E=true
ERROR="This script must be run as root!"
fi
# FUNCTIONS
# See https://stackoverflow.com/questions/19059944/convert-kb-to-mb-using-bash
hprint() {
local -i bytes=$1;
if [[ $bytes -lt 1024 ]]
then
echo "${bytes}B"
elif [[ $bytes -lt 1048576 ]]
then
echo "$(( (bytes + 1023)/1024 ))kB"
else
echo "$(( (bytes + 1048575)/1048576 ))MB"
fi
}
pidinfo() {
local PID="$1"
local STAT=$(pidstat -p $PID -d | tail -1)
local READ=$(echo $STAT | awk '{print $5}')
local WRITE=$(echo $STAT | awk '{print $6}')
local DELETE=$(echo $STAT | awk '{print $7}')
local IODELAY=$(echo $STAT | awk '{print $8}')
echo "<h5>IO</h5>"
echo "<table>"
echo "<tr>"
echo "<th>READ/S</th><th>WRITE/S</th><th>DEL/S</th><th>IO DELAY</th>"
echo "</tr>"
echo "<tr>"
echo "<td>${READ}kB</td><td>${WRITE}kB</td><td>${DELETE}KB</td><td>$IODELAY</td>"
echo "</tr>"
echo "</table>"
echo "<h5>FILES</h5>"
echo "<table>"
echo "<tr>"
echo "<th>TYPE</th><th>SIZE</th><th>NAME</th>"
echo "</tr>"
while read -r FIELD; do
case ${FIELD::1} in
s)
local SIZE=$(echo -n ${FIELD:1})
;;
t)
local TYPE=$(echo -n ${FIELD:1})
;;
n)
local NAME=$(echo -n ${FIELD:1})
;;
esac
if [ -e "$NAME" ] && [ -n "$TYPE" ] && [ "$SIZE" -gt 0 ]
then
echo "<tr><td>$TYPE</td><td>" $(hprint $SIZE) "</td><td>$NAME</td></tr>"
SIZE=0
TYPE=""
NAME=""
fi
done < <(lsof -p $PID -w -F pstn)
echo "</table>"
}
# MAIN
# IP address
# IP="192.168.0.100"
IP=$(hostname -I | awk {'print $1'})
# HTML report
HTML="/tmp/report.html.$RANDOM"
echo "<!DOCTYPE html>" > $HTML
echo "<html>" >> $HTML
echo "<head>" >> $HTML
echo "<meta charset='UTF-8'>" >> $HTML
echo "<title>TCP investigation</title>" >> $HTML
echo "<style>" >> $HTML
echo "body {margin: 0pt; padding: 10pt;}" >> $HTML
echo "div, p {width: 100%; padding: 0pt; color: MidnightBlue; font-size: 11pt;}" >> $HTML
echo "table {width: 100%; font-size: 10pt; color: DarkSlateGrey; border: 1pt solid LightGrey; margin: 0pt 0pt 10pt 0pt; padding: 1.2pt; border-radius: 5pt;}" >> $HTML
echo "th, td {text-align: left; padding: 1.2pt}" >> $HTML
echo "a {text-decoration: none;}" >> $HTML
echo "h1, h2, h3, h4, h5 {text-align: left; margin: 0pt 0pt 10pt 0pt; padding: 0pt;}" >> $HTML
echo "</style>" >> $HTML
echo "</head>" >> $HTML
echo "<body>" >> $HTML
echo "<h1>TCP investigation</h1>" >> $HTML
echo "<p><b>IP</b>: $IP</p>" >> $HTML
echo "<p><b>Date</b>: "$(date)"</p>" >> $HTML
# Are there any active connections?
ITEMS=$(netstat -pant 2>/dev/null | grep "$IP" | grep ESTABLISHED | wc -l)
if [[ $ITEMS -eq 0 ]]
then
E=true
ERROR="There are no active connections"
fi
# Is everything okay?
if [ "$E" = true ]
then
echo "<p><b>Error</b>: $ERROR</p>" >> $HTML
else
# Active connections
declare -a P
declare -a C
I=0
echo "<h3 id='connections'>ACTIVE TCP CONNECTIONS</h3>"$'\n' >> $HTML
echo "<table>" >> $HTML
echo "<tr>" >> $HTML
echo "<th>PID</th><th>REMOTE IP</th><th>REMOTE PORT</th><th>USER</th><th>GROUP</th><th>COMMAND</th>" >> $HTML
echo "</tr>" >> $HTML
while read -r CONNECTION; do
if [[ $CONNECTION ]]
then
PID=$(echo $CONNECTION | awk '{print $7}' | cut -d/ -f1)
if [[ "$PID" =~ ^[0-9]+$ ]]
then
USER=$(ps -p $PID -o user,group=GROUP | tail -1 | awk '{print $1}')
GROUP=$(ps -p $PID -o user,group=GROUP | tail -1 | awk '{print $2}')
COMMAND=$(ps -p $PID -o args=COMMAND | tail -1)
REMOTE=$(echo $CONNECTION | awk '{print $5}' | cut -d: -f1)
PORT=$(echo $CONNECTION | awk '{print $5}' | cut -d: -f2)
C[$I]="$PID|$REMOTE|$PORT|$USER|$GROUP|$COMMAND"
P[$I]=$PID
I=$(expr $I + 1)
fi
fi
done < <(netstat -pant 2>/dev/null | grep $IP | grep ESTABLISHED)
readarray -t C < <(printf '%s\n' "${C[@]}" | sort -n | uniq)
for L in "${C[@]}"
do
echo "<tr>" >> $HTML
LPID=$(echo $L | cut -d'|' -f1)
REMOTE=$(echo $L | cut -d'|' -f2)
PORT=$(echo $L | cut -d'|' -f3)
USER=$(echo $L | cut -d'|' -f4)
GROUP=$(echo $L | cut -d'|' -f5)
COMMAND=$(echo $L | cut -d'|' -f6)
echo "<td><a href='#pid-"$LPID"'>$LPID</a></td><td>$REMOTE</td><td>$PORT</td><td>$USER</td><td>$GROUP</td><td>$COMMAND</td>" >> $HTML
echo "</tr>" >> $HTML
done
echo "</table>" >> $HTML
echo "<h3>PROCESSES INVOLVED</h3>" >> $HTML
readarray -t P < <(printf '%s\n' "${P[@]}" | sort -n | uniq)
for PID in "${P[@]}"
do
echo "<h4 id='pid-"$PID"'><a href='#connections'>&uarr;</a> PROCESS ID $PID</h4>" >> $HTML
pidinfo $PID >> $HTML
done
fi
echo '</body>' >> $HTML
echo '</html>' >> $HTML
# REPORT
# Internet Message Format
MSG="/tmp/report.eml.$RANDOM"
# Message headers
FROM="From: \"TCP investigation\" <$SENDER>"
TO="To: \"Report\" <$RECIPIENT>"
SUBJECT="Subject: TCP investigation"
MIME="MIME-Version: 1.0"
TYPE="Content-Type: text/html; charset=UTF-8"
DATE="Date: $(date -R)"
ENCODING="Content-Transfer-Encoding: base64"
DISPOSITION="Content-Disposition: inline"
# Message-ID ?
# https://en.wikipedia.org/wiki/Message-ID
# https://www.jwz.org/doc/mid.html
# https://datatracker.ietf.org/doc/html/rfc2392
echo $FROM > $MSG
echo $TO >> $MSG
echo $SUBJECT >> $MSG
echo $DATE >> $MSG
echo $MIME >> $MSG
echo $TYPE >> $MSG
echo $ENCODING >> $MSG
echo $DISPOSITION >> $MSG
echo >> $MSG
# Encode HTML
echo $(base64 <<< "$(cat $HTML)") >> $MSG
# https://serverfault.com/questions/1036588/error-message-on-emailing-998-octets
sed -i -E 's/.{998}/&\n/g' $MSG
# https://cr.yp.to/docs/smtplf.html
unix2dos -q $MSG
# Sending e-mail
curl -s -n --ssl-reqd $RELAY --mail-from $SENDER --mail-rcpt $RECIPIENT --upload-file $MSG
# Cleaning
rm -f "$MSG"
rm -f "$HTML"