|
@@ -0,0 +1,217 @@
|
|
|
+#!/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'>↑</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"
|