How frequently are the honesty and integrity of a man disposed of by a smile or a shrug. How many good and generous actions have been sunk into oblivion by a distrustful look, or stamped with the imputation of bad motives, by a mysterious and seasonable whisper!”

Brutus- Brutus Server

Google+ Pinterest LinkedIn Tumblr +

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:

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. 






# 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/ $TARGET$ $PORT$ $MODE$"

In the /etc/portsentry folder, you can place this 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 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/ 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).

# 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/ $TARGET$ $PORT$ $MODE$"
# note : root's public key needs to be in pfsense user 'admin'
# $hoursold = after these hours, unblock IP address
# usage:
# r
# updated 100517 to use routing table instead of reconstructing config.xml, infinitely superior
#       copy ssh key into pfsense admin account

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
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
                statusis "Error found, $errdata"
                exit 1

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"
                exit 1
                if [ -n "$2" ]; then
                        # remove the IP provided via command line
                        ssh -f [email protected]$fwrouter "route del $rmip" 2>&1 > /dev/null
                        exit 0
                        if [ ! -f $blocklog ] || [ -z "$(cat $blocklog)" ]; then
                                statusis "Nothing in the blocklog, exiting"
                                exit 0

                        # 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
                        done < $blocklog
                        exit 0
                ssh [email protected]$fwrouter "route add $iptoblock -blackhole"
                if [ $? -ne 0 ]; then
                        statusis "can't add IP to $fwrouter, exiting"
                        exit 1
                echo "$iptoblock $curdate $curdateepoch" >> $blocklog
                exit 0

The 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.
# 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


echo -n "IP's currently in FW table: "
ssh [email protected]$fwrouter "netstat -r -n | grep | grep UGHSB" > /tmp/reporttmp
cat /tmp/reporttmp | wc -l
cat /tmp/reporttmp
rm /tmp/reporttmp 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/
# generate report to show blocks within $withinlast hours

tmpfolder="/tmp/$(basename $0).$$"
# think days you want +2 including today, must be 1 day greater than purge value in the manager
[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
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

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

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 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.


Leave A Reply

Secured By miniOrange