Mac OS X PF Firewall: Protecting SSH from Brute Force Attacks

Apple-like gate (CC BY-SA 2.0 Licensed image by Dennis Jarvis, Flickr user archer10)
Apple-like gate (CC BY-SA 2.0 Licensed image by Dennis Jarvis, archer10)

The OpenBSD‘s PF provides a great many features for packet filtering and network address translation. Mac OS X includes a version of PF that can be used to protect network services. In an article called “Mac OS X pf: Avoiding known bad guys“, I talk about using the Mac OS X PF firewall to protect against known bad sites. In this article, we explore a technique to protect SSH from attackers trying to gain remote access to your Mac by guessing passwords by brute force.

Most of what I learned about PF was used at the office to protect our network. We built redundant firewalls using FreeBSD and PF rules. I started to experiment with the Mac OS X PF implementation once I learned that it was shipped in Lion (Mac OS X 10.7). I am specifically concerned about my Mac laptop systems. The information in this article can also be applied to Mac server and desktop systems too.

Any administrator that has an open SSH service to the internet is probably familiar with the significant number of attempts that attackers try to gain access to your system by guessing account names and passwords. I once watched a system get more than 700 attempts in the matter of a few minutes. At my previous job, this was a daily problem. In fact, we used PF to block hundreds of attempts a day to access our external SSH services. This article shows the technique we used.

SSH on Mac OS X is not enabled by default. In fact, it’s not even called SSH. Go to the System Preferences and you will find it called “Remote Login”.

Dropping the PF anchor

Mac OS X uses PF anchors to divide up filter rules into functional groups. We can also use this technique and create an anchor. Edit your /etc/pf.conf file to include the following lines:

anchor "sshd"
load anchor "sshd" from "/etc/pf.anchors/sshd"

Now we need to write the lines for the anchor we defined. From the pf.conf configuration we just added, those should go into the file /etc/pf.anchors/sshd. Create that file and add these lines:

table <attackers> persist
 block log quick from <attackers>
pass in quick proto tcp from any port ssh flags S/SA keep state \
 (max-src-conn 3, max-src-conn-rate 5/60, \
 overload <attackers> flush global)

The first rule creates a persistent table, named attackers, where we can store IP addresses of attackers. The second rule blocks and logs the inbound packets from IPs listed in the attackers table. The third rule allows inbound ssh connections from any host and monitors the state of those connections. It also prevents more than three simultaneous connections (max-src-conn 3) and more than five connections within 60 seconds (max-src-conn-rate 5/60). This is accomplished by pushing any source IP that violates those rules into the attackers table (overload <attackers>) and then canceling (flush) all (global) established connections in the state table. This effectively terminates all of the established ssh connections for that source IP address.

You can tweak the numbers for the maximum number of simultaneous connections and the connection rate to suit your needs.

To load the new PF rules, execute the following command:

$ sudo pfctl -f /etc/pf.conf

On system start, launchd uses the /System/Library/LaunchDaemons/ file to load the /etc/pf.conf file. This will load the default rule set which now contains our anchor for sshd.

Managing your attackers table

At some point, you may to check to see if you have attackers in your attackers table. To do that, use this command:

$ sudo pfctl -a sshd -t attackers -Tshow

This command lists the IPs in the attackers table for the sshd anchor.

If you accidentally (or during testing) get a legitimate IP in the attackers table, you can remove it with this command (replace a.b.c.d with your IP):

$ sudo pfctl -a sshd -t attackers -Tdelete a.b.c.d

You can also save the existing attackers table and reload it later. This can be useful for saving the table through a system restart. Use the following commands to save the table:

$ sudo pfctl -a sshd -t attackers -Tshow > /etc/pf.attackers-table.txt

To load the table later, use this command:

$ sudo pfctl -a sshd -t attackers -Tload -f /etc/pf.attackers-table.txt

Keep in mind that the -Tload commands clears any existing entries already in the table. Any new entries in the table will be lost when you load the table from a file.

There are several other table operations with using the -T option to pfctl. You can test to see if an IP exists in the table (-Ttest) or delete everything in the table (-Tflush). See the manual page pfctl(8) for more details.

Catching dropped packets

I mentioned that in the previous blog post that pflogd is missing from the Mac OS X PF implementation. Unfortunately, there is not an easy way to collect dropped packets for later review at this point. You can only watch the packets as they are captured in real time. Use the following tcpdump command to see them as they are blocked:

$ sudo tcpdump -i pflog0 -n -e -ttt

You can run this command in a terminal window all the time to monitor blocked packets. It can also be scripted to run silently in the background and write the packets to a file for later review. I hope to find a better solution to share in an upcoming blog post.

Next Time

After you understand the basics of PF, you can use most of the available PF resources online to manage your Mac OS X PF rules. If you have something specific you are interested in doing with the Mac OS X PF implementation, please comment below.

I am working (slowly) on re-creating some of the scripts I wrote to manage tables from FreeBSD PF to Mac OS X PF. Hopefully, I can share those soon.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: