217 lines
6.3 KiB
Bash
Executable file
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'>↑</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"
|