The Brutus server is essentially any machine running Linux that has Portsentry installed. In the case of our reference diagram, let’s assume we’ve made the Web server the Brutus server, and the EMail server the Client. You could of course make Brutus run on its own dedicated host as well. It’s helpful to install postfix on this machine so that you can receive emailed status reports.
On Ubuntu you can install Portsentry with “apt get portsentry”.
The project home page is here at SourceForge: https://sourceforge.net/projects/sentrytools/
Once Portsentry has been installed, place this portsentry.conf file in /etc/portsentry . This is what shapes Portsentry’s behavior and determines what to run in the event a port “touch” is detected.
TCP_PORTS="1,11,15,79,111,119,143,540,635,1080,1524,2000,5742,6667,12345,12346,20034,27665,31337,32771,32772,32773,32774,40421,49724,54320"
UDP_PORTS="1,7,9,69,161,162,513,635,640,641,700,37444,34555,31335,32770,32771,32772,32773,32774,31337,54321"
ADVANCED_PORTS_TCP="65535"
ADVANCED_PORTS_UDP="65535"
ADVANCED_EXCLUDE_TCP="22"
ADVANCED_EXCLUDE_UDP=""
IGNORE_FILE="/etc/portsentry/portsentry.ignore"
HISTORY_FILE="/etc/portsentry/portsentry.history"
BLOCKED_FILE="/etc/portsentry/portsentry.blocked"
RESOLVE_HOST = "0"
SCAN_TRIGGER = "0"
KILL_RUN_CMD_FIRST = "0"
BLOCK_UDP="1"
BLOCK_TCP="1"
# target=source IP, port=touch detected on this port, mode=atcp,tcp,audp,udp
# only target's used for blocking, the rest is for logging
KILL_ROUTE="/etc/portsentry/instructrouter.sh $TARGET$ $PORT$ $MODE$"
In the /etc/portsentry folder, you can place this instructrouter.sh script. The heart of Brutus, this is the script called by portsentry.conf above that instructs your firewall to null-route the offending external IP address (“null-route” means “the route to take to get to this IP is.. yourself.. there is no other route”, effectively dropping this IP in the trashcan.)
In order for the portsentry process to SSH into your firewall, you need to create a public-private keypair for the root account on your Brutus server. You can do it with the commands at right. You don’t need a key larger than 1024 bits as it’s being used in an internal control channel.
sudo su –
ssh-keygen -t rsa -b 1024
In the /root/.ssh folder, copy the contents of id-rsa.pub to the Firewall’s admin user public key field, per the “Firewall” article instructions.
This script is also called via root’s crontab as per the string at right.
45 23 * * * /etc/portsentry/instructrouter.sh r
For firewalls other than PFSense, the command to actually execute the block will vary based on the operating system but you only have to test and change a single line (about line #89 in the * section).
#!/bin/bash
# designed for PFSense, meant to work with apt package 'portsentry'
# this script works by placing hostile external IP's into a null-route in the router's manual table.
# note that it'll only block scanning / hostile IP's externally, you can still reach them
# from the inside, ie, the null-route is only effective for inbound requets to WAN
# target=source IP, port=touch detected on port, mode=atcp,tcp,audp,udp
#KILL_ROUTE="/etc/portsentry/instructrouter.sh $TARGET$ $PORT$ $MODE$"
# note : root's public key needs to be in pfsense user 'admin'
# $hoursold = after these hours, unblock IP address
# usage:
# instructrouter.sh r
# instructrouter.sh 1.2.3.4
# updated 100517 to use routing table instead of reconstructing config.xml, infinitely superior
# copy ssh key into pfsense admin account
homedir=/etc/portsentry
blocklog=$homedir/blocklog.log
curdate=$(date +%m%d%y.%H%M%S)
curdateepoch=$(date +%s) # epoch time used for numerical comparison to find old records
tmpfolder="/tmp/$(basename $0).$$"
remuser="root" #root is actually called admin on pfsense
fwrouter=192.168.1.1
binlogger=/usr/bin/logger
hoursold=48
alertemail="[email protected]"
function statusis {
echo "status : $1"
$binlogger -t PORTSENTRY: $1
}
function mailout {
grep PORTSENTRY /tmp/log/syslog | tail -n10 > $tmpfolder/sysout
mail -s BrutusError $alertemail < $tmpfolder/sysout
}
function traperr {
if [ $? -ne 0 ]; then
errdata=$1
statusis "Error found, $errdata"
mailout
exit 1
fi
}
function cleanup {
rm -rf $tmpfolder
}
mkdir $tmpfolder
case $1 in
"")
echo "Help : append with ip address to block or 'r' to remove stale"
statusis "exiting due to null parameter"
mailout
cleanup
exit 1
;;
r)
if [ -n "$2" ]; then
rmip="$2"
# remove the IP provided via command line
ssh -f [email protected]$fwrouter "route del $rmip" 2>&1 > /dev/null
cleanup
exit 0
else
if [ ! -f $blocklog ] || [ -z "$(cat $blocklog)" ]; then
statusis "Nothing in the blocklog, exiting"
exit 0
fi
# figure out from $blocklog which IP's are more than $hoursold and remove from fw
while read blockline; do #blockline format : ip blockdate blockepoch
lesshrsold=$(date --date "now -$hoursold hours" +%s)
if [ $(echo $blockline | cut -d " " -f3) -lt $lesshrsold ]; then
rmip=$(echo $blockline | cut -d " " -f1)
# the -f is crucial lest the script abort after an ssh error if route missing
ssh -f [email protected]$fwrouter "route del $rmip" 2>&1 > /dev/null
fi
done < $blocklog
cleanup
exit 0
fi
;;
*)
iptoblock=$1
ssh [email protected]$fwrouter "route add $iptoblock 127.0.0.1 -blackhole"
if [ $? -ne 0 ]; then
statusis "can't add IP to $fwrouter, exiting"
exit 1
fi
echo "$iptoblock $curdate $curdateepoch" >> $blocklog
cleanup
exit 0
;;
esac
The fwreport.sh script can go in /etc/portsentry too. This is executed by hand whenever you want to:
a) confirm root’s key is working on the firewall, and
b) provide a current list of IP’s being blocked by Brutus.
#!/bin/bash
# This script intended to work with PFSense
# the root user's public key needs to be in the router's authorized_keys
# or assigned to the PFSense admin user
remuser="root"
fwrouter=192.168.1.1
echo -n "IP's currently in FW table: "
ssh [email protected]$fwrouter "netstat -r -n | grep 127.0.0.1 | grep UGHSB" > /tmp/reporttmp
cat /tmp/reporttmp | wc -l
cat /tmp/reporttmp
rm /tmp/reporttmp
blockreport.sh is also placed in /etc/portsentry, and it’s executed via crontab every day at about 11:55p (before midnight) so as to email you with a daily status report. It also performs some maintenance, purging records older than a few days.
Root’s crontab line looks like the command at right:
47 23 * * * /etc/portsentry/blockreport.sh
#!/bin/bash
# generate report to show blocks within $withinlast hours
tmpfolder="/tmp/$(basename $0).$$"
homedir=/etc/portsentry
histfile=$homedir/portsentry.history
withinlast=24
# think days you want +2 including today, must be 1 day greater than purge value in the manager
purgeafterday=4
[email protected]
mkdir $tmpfolder
#truncate history file first, less to deal with overall
cat $histfile | while read lineitem; do
if [ $(echo "$lineitem" | awk '{ print $1 }') -ge $(date --date "now -$purgeafterday days" +%s) ]; then
echo "$lineitem" >> $tmpfolder/keeplist
fi
done
mv $tmpfolder/keeplist $histfile
#from the cleaned up list extract current data
cat $histfile | while read lineitem; do
if [ $(echo "$lineitem" | awk '{ print $1 }') -ge $(date --date "now -$withinlast hours" +%s) ]; then
echo "$lineitem" >> $tmpfolder/linelist
fi
done
linecount=$(cat $tmpfolder/linelist | wc -l)
#port statistics
cat $tmpfolder/linelist | awk '{ print $8 }' > $tmpfolder/portlist
cat $tmpfolder/portlist | sort | uniq > $tmpfolder/uniqueports
cat $tmpfolder/uniqueports | while read lineitem; do
portcount=$(cat $tmpfolder/portlist | grep $lineitem | wc -l)
svcname="$(grep -w "$lineitem/tcp" /etc/services | awk '{ print $1 }')"
echo "$portcount $lineitem $svcname" >> $tmpfolder/portout
done
echo "Count Port Service Name" >> $tmpfolder/portreport
echo "-------------------------------" >> $tmpfolder/portreport
cat $tmpfolder/portout | sort -nr >> $tmpfolder/portreport
mail -s "Brutus report - $linecount IP's blocked" $emailto < $tmpfolder/portreport
rm -rf $tmpfolder
To test, google “online port scan”, you’ll see offerings by MXToolbox and others – use their services to scan your external IP address and watch your routing table (or run fwreport.sh) and you should see the source IP get dropped very quickly!
After all this is done, Portsentry is now able to instruct your firewall to drop inbound portscans automatically, resulting in a network that’s MUCH harder to map by attackers.
This is the heart of Brutus, you could be done if you have a home network with no servers or have a single server behind a firewall in your DMZ that provides service. If you have more than one server you need to protect, proceed to the next article for Brutus client configuration.