oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Exploring the Mac OS X Firewall

by Peter Hickman

All Unix systems have a firewall. But on the Mac, it's installed by default and you can't turn it off. To keep things simple, and keep users from having to build their own firewall rules, OS X packages the firewall as a sub-menu of Sharing within System Preferences. The benefit of this approach is that the firewall can be tied to services. Turn on Personal Web Sharing, for example, and a new rule appears in the firewall to control access to the service.

Presently, I have Personal Web Sharing turned on in the Services menu. The firewall is turned on. And in the Firewall menu, there's a checkbox to allow access to my web server. Opening a terminal and typing sudo ipfw list allows me to see the actual firewall rules that Mac OS X has created.

02000 allow ip from any to any via lo*
02010 deny ip from to any in
02020 deny ip from any to in
02030 deny ip from to any in
02040 deny tcp from any to in
02050 allow tcp from any to any out
02060 allow tcp from any to any established
02070 allow tcp from any to any 80 in
02080 allow tcp from any to any 427 in
12190 deny tcp from any to any
65535 allow ip from any to any

ipfw is the heart of the Macintosh firewall; the listing is the set of rules that Mac OS X built based on the services I wanted to run. Apache runs on port 80 (for http), while port 427 is the service locator protocol. The first column is the rule number, and the rest is the rule itself. I will go into greater detail later. But for the moment, I will turn on Remote Login. A quick glance at the Firewall menu reveals that the Remote Login rules have been turned on. Looking at the output of sudo ipfw list shows how the rules were changed to allow the new service to run.

02000 allow ip from any to any via lo*
02010 deny ip from to any in
02020 deny ip from any to in
02030 deny ip from to any in
02040 deny tcp from any to in
02050 allow tcp from any to any out
02060 allow tcp from any to any established
02070 allow tcp from any to any 22 in
02080 allow tcp from any to any 80 in
02090 allow tcp from any to any 427 in
12190 deny tcp from any to any
65535 allow ip from any to any

The rules have been rewritten, and a new rule has been added to handle Remote Login. Remote Login is handled by ssh, which operates over port 22. I will turn the firewall off as a final test before I put everything back the way it was.

65535 allow ip from any to any

So even when we turn the firewall off, it is still running. It's just the rules that have changed. What I'm going to do in this article is explore ipfw to see how I can use it to better secure my Macintosh and learn something of the threats that assail our computers every day. But first, a few words of caution.

Don't Play with Fire

The firewall that comes with the Mac is a good and robust tool and will protect you from many things, and you should have it turned on all the time. Just because we can handcraft rules does not mean that we should. If we get it wrong, we could stop our computer from functioning and create security holes. In this article, we're going to play with the firewall, learn a few things about our Mac, and then just let the System Preferences take care of things (in most cases).

This is one area where a little knowledge can be dangerous. And it's important to remember that after reading this article you will not be an expert on firewalls.

Say Hello to ipfw

To get a feel for what your firewall is having to deal with, you'll get it to log all the decisions that it makes over the course of a few hours. This will show you what's really happening. First, create a file that contains a copy of the rules you're running. Call this file rules.


IPFW='/sbin/ipfw -q'

$IPFW -f flush
$IPFW add 2000 allow ip from any to any via lo*
$IPFW add 2010 deny log ip from to any in
$IPFW add 2020 deny log ip from any to in
$IPFW add 2030 deny log ip from to any in
$IPFW add 2040 deny log tcp from any to in
$IPFW add 2050 allow log tcp from any to any out
$IPFW add 2060 allow log tcp from any to any established
$IPFW add 2070 allow log tcp from any to any 22 in
$IPFW add 2080 allow log tcp from any to any 80 in
$IPFW add 2090 allow log tcp from any to any 427 in
$IPFW add 12190 deny log tcp from any to any

These rules are a copy of the rules that Mac OS X generated for Personal Web Sharing and Remote Login with logging turned on. You do not need to enter rule 65535 as this is hard-coded into the firewall and cannot be changed. To run the rules just type in sudo sh rules followed by sudo ipfw list to make sure that they have loaded correctly. The final step is to turn logging on. Despite the rules requiring that the output be logged, this only actually happens when it is turned on with sysctl.

sudo sysctl -w net.inet.ip.fw.verbose=1

This might seem complicated, but it allows us to have logging permanently turned on in our rules, only writing to the log file when we want to. The logging is being written to /var/log/system.log. Logging can then be turned on and off without changing or even reloading the rules. To turn logging off, change the 1 to a 0. Leave this running for a few hours as we do all sorts of things that may generate traffic over the Internet. Just one little note of caution at this point: There will be one line in the log file for each packet processed by the firewall. The log file can get very large very quickly! Remember to come back and turn the logging off before your disk fills up.

Looking at the Log Results

/var/log/system.log now contains a large number of lines—one per packet that was handled by the firewall rules we created. Here is an example:

Nov  9 21:12:18 Peter-Hickmans-Computer kernel: ipfw: 2060
Accept TCP in via en0

After all the date and time cruft, we come to the action that was recorded, which says that rule number 2060 accepted an inbound TCP connection via the primary Ethernet interface from address on port 119 to my computer (on address port 54609. Port 119 is used to connect to Usenet news servers, and port 54609 was just a random port Mac OS X allocated for my program to make the connection. To make any sense of the 341,310 lines that the firewall logged while I had it turned on, we need a program to summarize the log. Let's call this program

#!/usr/bin/perl -w

use strict;
use warnings;

my %connections;

while ( my $line = <> ) {
  next unless $line =~ m/ ipfw: /;

  $line =~ s/.* ipfw: //g;

  my ( $action, $from, $to, $direction ) 
    = ( split( ' ', $line, 7 ) )[ 1, 3, 4, 5 ];

  if ( check_address($from) and check_address($to) ) {
    my $key = ( $direction eq 'out' ) ? 
      "$from $to" : "$to $from";

foreach my $action ( sort keys %connections ) {
  report( $action, %{ $connections{$action} } );

sub report {
  my ( $action, %data ) = @_;

  print "$action\n";
  printf( "%21s Dir %21s : %8s : %8s\n",
    'Inside IP', 'Outside IP', 'In', 'Out' );
  print '-' x 69 . "\n";

  foreach my $k ( sort keys %data ) {
    my ( $inside, $outside ) = split( ' ', $k );

    my $direction = '-->';
    if ( $data{$k}->{in} ) {
      $direction = ( $data{$k}->{out} ) ? '<->' : '<--';
    printf( "%21s %s %21s : %8d : %8d\n", 
      $data{$k}->{in} || 0, 
      $data{$k}->{out} || 0 );
  print "\n";

## Filter the broken lines in the log

sub check_address {
  my $text = shift;

  # There should only be 1 colon
  my $count = $text =~ tr/://;
  return undef if $count != 1;

  # There should only be three .s
  $count = $text =~ tr/.//;
  return undef if $count != 3;

  my ( $ip, $port ) = split( ':', $text );

  # Is the port number sensible
  return undef if $port < 1 or $port > 65535;

  # Are the address digits sensible
  foreach my $x ( split( '\.', $ip ) ) {
    return undef if $x < 0 or $x > 255;

  # All is fine
  return 1;

When run from the command line with the system.log file for input, it reads all the lines related to the firewall and extracts the action (Accept or Deny), from address, to address, and direction from the rule, then builds up a hash to summarize the data. The rules are always expressed as "from address" followed by "to address," so we need to switch them around so that packets going from B to A inbound are correctly paired with lines for A to B outbound. The check_address function is to weed out the few lines that logging seems to screw up every now and then. Here's an edited sample of the output. Let's first look at the traffic we accepted:

Inside IP Dir            Outside IP :       In :         Out
------------------------------------------------------------    <-> :    80 :       81 <-> :      1 :        1 <-> : 19768 :    13114 <-> :    15 :       17 <-> :    15 :       14 <-> : 24924 :    16792 <-> :   89 :       82    <-> : 11 :        6    <-> :   11 :       10

First, an assumption: Any port number that is four or less digits long is a legitimate port. If it is five digits long, then it was just a port allocated to the process that initiated the connection. This is not always the case but it is a very useful rule of thumb.

The first line is a Remote Login connection (ssh on port 22) to my computer, and the next is a connection to a web site (http on port 80) from my computer. The next four are Usenet connections (nntp on port 119) from my computer followed by a connection from my computer to my ISP's mail server (pop3 on port 110). The next two are connections to the web server on my computer by another computer.

If you see a port number and are not sure what it is used by, look in /etc/services where most of the legitimate port numbers are listed. If you can't find it there or feel that you are not using the service, then Google for "port xxxx". Now let's look at the traffic we rejected:

Inside IP Dir            Outside IP :           In :     Out
------------------------------------------------------------ <-- :     1 :       0 <-- :    1 :       0 <-- :     1 :       0   <-- :  2 :       0   <-- :    2 :       0 <-- :  3 :       0 <-- :    3 :       0 <-- :  2 :       0 <-- :    1 :       0 <-- :      1 :       0 <-- :  2 :       0 <-- :    1 :       0

Related Reading

Essential Mac OS X Panther Server Administration
Integrating Mac OS X Server into Heterogeneous Networks
By Michael Bartosh, Ryan Faas

As these are all denied packets we are only interested in the port number that they were trying to connect to. Let's go over them:

The Sasser worm runs an FTP server on port 1023 of infected machines. is scanning for unpatched Windows machines still infected by the Sasser worm.
Officially the port used by blackjack servers but also used by a backdoor trojan called PWSteal.ABCHlp. Again, I am being scanned by a computer looking for a backdoor.
Officially used by the SOCKS proxy server. is scanning me, hoping to find a proxy server that it can use to cover its tracks with.
At last a legitimate port, FTP. Here I am being scanned to see if I am running an FTP server that can be hacked by someone wanting somewhere to store some files or a place from which to run a warez site. I don't run an FTP server.
Another legitimate port, this time SMTP. This is someone looking for an open mail server to send spam through.
Officially the port used by the urbisnet service but more commonly a backdoor installed by the W32.Beagle.E@mm worm.

You get the picture. It just goes on and on. Despite being immune to all these WinTel worms and viruses, we should note that there are people out there who will notice very quickly if we turn on our own mail or FTP servers. The same scripts that probe your computer for these services are designed to exploit them the moment they get a positive result. This is why we run a firewall and why we don't run unnecessary services.

Pages: 1, 2

Next Pagearrow