Chapter 4. Firewalling

When designing a network, it’s often desirable to define policies governing how and where certain vital network services can be accessed. The firewall is a key technology that is instrumental in enforcing these policies and can allow network administrators to delineate trust relationships between networks and hosts with a fine grain of detail.

By instituting a firewall, you can prevent unauthorized access to services at the network level before an attacker is given the chance to attempt to exploit them. You can use a firewall not only to limit what information flows into a network, but also to prevent the egress of information. Doing so aids in preventing worm propagation and helps stop important confidential information from leaving an enterprise. Additionally, firewall logs can be excellent tools to help you understand where the threats to your network originate.

A variety of firewalls are available today. In addition to the many firewall appliances that are available, Linux, BSD, and Windows all include some form of firewalling support. This chapter shows how to set up firewalls with Linux, FreeBSD, OpenBSD, and Windows, as well as how to test your firewall rulesets. You’ll also see how to perform MAC address filtering and how to create a gateway that will authenticate machines based on login credentials. Finally, you’ll learn a few additional tricks to keep certain types of traffic from exiting your network.

Firewall with Netfilter

Protect your network with Linux’s powerful firewalling features.

Linux has long had the capability for filtering packets, and it has come a long way since the early days in terms of both power and flexibility. The first generation of packet-filtering code, called ipfw (for “IP firewall”), provided basic filtering capability. Since it was somewhat inflexible and inefficient for complex configurations, ipfw is rarely used now. The second generation of IP filtering was called IP chains. It improved greatly on ipfw and is still in common use. The latest generation of filtering, called Netfilter, is manipulated with the iptables command and used exclusively with the 2.4.x and later series of kernels. Although Netfilter is the kernel component and iptables is the user-space configuration tool, these terms are often used interchangeably.

An important concept in Netfilter is the chain , which consists of a list of rules that are applied to packets as they enter, leave, or traverse the system. The kernel defines three chains by default, but new chains of rules can be specified and linked to the predefined chains. The INPUT chain applies to packets that are received by and destined for the local system, and the OUTPUT chain applies to packets that are transmitted by the local system. Finally, the FORWARD chain applies whenever a packet will be routed from one network interface to another through the system. It is used whenever the system is acting as a router or gateway, and it applies to packets that are neither originating from nor destined for the local system.

The iptables command makes changes to the Netfilter chains and rulesets. Using iptables, you can create new chains, delete chains, list the rules in a chain, flush chains (i.e., remove all rules from a chain), and set the default action for a chain. iptables also allows you to insert, append, delete, and replace rules in a chain.

Setting the Filtering Policy

Before we get started with some example rules, it’s important to set a default behavior for all the chains. To do this, use the -P (which stands for “policy”) command-line switch:

# iptables -P INPUT DROP
# iptables -P FORWARD DROP
            

This ensures that only those packets covered by subsequent rules that you specify will make it past your firewall. After all, with the relatively small number of services that your network will likely provide, it is far easier to explicitly specify all the types of traffic that you want to allow than it is to specify all the traffic that you don’t.

Tip

Note that you don’t specify a default policy for the OUTPUT chain. This is because you’ll want to allow traffic to proceed out of the firewall itself in a normal manner.

With the default policy set to DROP, you’ll specify what is actually allowed. Here’s where you’ll need to figure out what services will have to be accessible to the outside world. For the rest of these examples, assume that eth0 is the external interface on the firewall and eth1 is the internal one. The sample network will contain a web server (192.168.1.20), a mail server (192.168.1.21), and a DNS server (192.168.1.18)—a fairly minimal setup for a self-managed Internet presence.

We’ll begin specifying rules momentarily, but first, remove filtering from the loopback interface:

# iptables -A INPUT -i lo -j ACCEPT
# iptables -A OUTPUT -o lo -j ACCEPT
            

Rule Examples

Now, let’s construct some rules to allow this traffic through. First, make a rule to allow traffic on TCP port 80—the standard port for web servers—to pass to the web server unfettered by the firewall:

# iptables -A FORWARD -m state --state NEW -p tcp \ 
               -d 192.168.1.20 --dport 80 -j ACCEPT
            

And now for the mail server, which uses TCP port 25 for SMTP:

# iptables -A FORWARD -m state --state NEW -p tcp \ 
               -d 192.168.1.21 --dport 25 -j ACCEPT
            

You might also want to allow remote POP3, IMAP, and IMAP+SSL access:

POP3
# iptables -A FORWARD -m state --state NEW -p tcp \ 
                        -d 192.168.1.21 --dport 110 -j ACCEPT
                     
IMAP
# iptables -A FORWARD -m state --state NEW -p tcp \ 
                        -d 192.168.1.21 --dport 143 -j ACCEPT
                     
IMAP+SSL
# iptables -A FORWARD -m state --state NEW -p tcp \ 
                        -d 192.168.1.21 --dport 993 -j ACCEPT
                     

Finally, allow DNS access via port 53:

# iptables -A FORWARD -m state --state NEW -p tcp \
               -d 192.168.1.21 --dport 53 -j ACCEPT
            

Unlike the other services, DNS can use both TCP and UDP port 53. Using a default deny policy makes it slightly more difficult to use UDP for DNS. This is because the policy relies on the use of state-tracking rules, and since UDP is a stateless protocol, there is no way to track it. In this case, you can configure the DNS server either to use only TCP, or to use a UDP source port of 53 for any response that it sends back to clients that were using UDP to query the name server.

If the DNS server is configured to respond to clients using UDP port 53, you can allow this traffic through with the following two rules:

# iptables -A FORWARD -p udp -d 192.168.1.18 --dport 53 -j ACCEPT
# iptables -A FORWARD -p udp -s 192.168.1.18 --sport 53 -j ACCEPT
            

The first rule allows traffic destined for the DNS server into your network, and the second rule allows responses from the DNS server to leave the network.

A Word About Stateful Inspection

You might be wondering what the -m state and --state arguments are about. These two options allow us to use Netfilter’s stateful packet-inspection engine. Using these options tells Netfilter that you want to allow only new connections to the destination IP and port pairs that you have specified. When these rules are in place, the triggering packet is accepted and its information is entered into a state table.

Now, you can specify that you want to allow any outbound traffic that is associated with these connections by adding a rule like this:

# iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
            

The only thing left now is to allow traffic from machines behind the firewall to reach the outside world. To do this, use a rule like the following:

# iptables -A FORWARD -m state --state NEW -i eth1 -j ACCEPT
            

This rule enters any outbound connections from the internal network into the state table. It works by matching packets coming into the internal interface of the firewall that are creating new connections. If you were setting up a firewall that had multiple internal interfaces, you could have used a Boolean NOT operator on the external interface (e.g., -i ! eth0). Now, any traffic that comes into the firewall through the external interface that corresponds to an outbound connection will be accepted by the preceding rule, because this rule will have put the corresponding connection into the state table.

Ordering Rules

In these examples, the order in which the rules were entered does not really matter. Since you’re operating with a default DENY policy, all your rules have an ACCEPT target. However, if you had specified targets of DROP or REJECT as arguments to the -j option, you would have had to take a little extra care to ensure that the order of those rules would result in the desired effect. Remember that the first rule that matches a packet is always triggered as the rule chains are traversed, so rule order can sometimes be critically important.

It should also be noted that rule order can have a performance impact in some circumstances. For example, the rule shown earlier that matches ESTABLISHED and RELATED states should be specified before any of the other rules, since that particular rule will be matched far more often than any of the rules that will match only on new connections. Putting that rule first will prevent any packets that are already associated with a connection from having to traverse the rest of the rule chain before finding a match.

To complete the firewall configuration, you’ll want to enable packet forwarding. Run this command:

# echo 1 > /proc/sys/net/ipv4/ip_forward
            

This tells the kernel to forward packets between interfaces whenever appropriate. To have this done automatically at boot time, add the following line to /etc/sysctl.conf :

net.ipv4.ip_forward=1

If your system doesn’t support /etc/sysctl.conf, you can put the preceding echo command in one of your startup rc scripts, such as /etc/rc.local.

Another useful kernel parameter is rp_filter, which helps prevent IP spoofing. Running the following command enables source address verification by checking that the IP address for any given packet has arrived on the expected network interface:

# echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
            

You can also enable source address verification by editing /etc/sysctl.conf on systems that support it, or else putting the changes in your rc.local. To enable rp_filter in your sysctl.conf, add the following line:

net.ipv4.conf.all.rp_filter=1

To save all of the rules, either write them to a shell script or use your Linux distribution’s particular way of saving them. Do this in Red Hat by running the following command:

# /sbin/service iptables save
            

This saves all currently active filter rules to /etc/sysconfig/iptables. To achieve the same effect under Debian, edit /etc/default/iptables and set enable_iptables_initd=true.

After doing this, run the following command:

# /etc/init.d/iptables save_active
            

When the machine reboots, your iptables configuration will automatically be restored.

Firewall with OpenBSD’s PacketFilter

Use OpenBSD’s firewalling features to protect your network.

PacketFilter, commonly known as PF, is the firewalling system available in OpenBSD. While it is a relatively new addition to the operating system, it has already surpassed IPFilter, the system it replaced, in both features and flexibility and has even become a part of FreeBSD as of 5.3-RELEASE. PF shares many features with Linux’s Netfilter, and while Netfilter is more easily extensible with modules, PF outshines it in its traffic normalization capabilities and enhanced logging features.

OpenBSD supports PF out of the box. However, under FreeBSD, you’ll need to enable at minimum the following kernel configuration options:

device pf
device pflog

If you don’t have these options enabled, add them in and rebuild and reinstall your kernel. For more information on how to do that, see the “Building and Installing a Custom Kernel” section of the FreeBSD Handbook.

To communicate with the kernel portion of PF, you’ll need to use the pfctl command. Unlike the iptables command that is used with Linux’s Netfilter, pfctl is not used to specify individual rules, but instead uses its own configuration and rule specification language. To actually configure PF, you must edit /etc/pf.conf.

Configuring PF

PF’s rule specification language is actually very powerful, flexible, and easy to use. The pf.conf file is split up into seven sections, each of which contains a particular type of rule. You don’t need to use all of the sections; if you don’t need a specific type of rule, you can simply omit that section from the file.

The first section is for macros. In this section, you can specify variables to hold either single values or lists of values for use in later sections of the configuration file. Like an environment variable or a programming-language identifier, a macro must start with a letter and may contain digits and underscores.

Here are some example macros:

EXT_IF="de0"
INT_IF="de1"
RFC1918="{ 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }"

You can reference a macro later by prefixing it with the $ character:

block drop quick on $EXT_IF from any to $RFC1918

The second section allows you to specify tables of IP addresses to use in later rules. Using tables for lists of IP addresses is much faster than using a macro, especially for large numbers of IP addresses, because when a macro is used in a rule, it will expand to multiple rules, with each one matching on a single value contained in the macro. Using a table adds just a single rule when it is expanded.

Thus, rather than using the macro from the previous example, you could define a table to hold the nonroutable RFC 1918 IP addresses:

table <rfc1918> const { 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }

The const keyword ensures that this table cannot be modified once it has been created. Specify tables in a rule in the same way that they were created:

block drop quick on $EXT_IF from any to <rfc1918>

You can also load a list of IP addresses into a table by using the file keyword:

table <spammers> file "/etc/spammers.table"

If you elect not to use the const keyword, you can add addresses to a table by running a command such as this:

pfctl -t spammers -T add 10.1.1.1

Additionally, you can delete an address by running a command like this:

pfctl -t spammers -T delete 10.1.1.1

To list the contents of a table, you can run this command:

pfctl -t spammers -T show

In addition to IP addresses, you can also specify hostnames. In this case, all valid addresses returned by the resolver will be inserted into the table.

Global Options

The next section of the configuration file contains options that affect the behavior of PF. By modifying options, you can control session timeouts, defragmentation timeouts, state-table transitions, statistic collection, and other behaviors. Specify options by using the set keyword. The available options are too numerous to discuss all of them in any meaningful detail; however, we will discuss the most pertinent and useful ones.

One of the most important options is block-policy . This option specifies the default behavior of the block keyword and can be configured to silently drop matching packets by specifying drop . Alternatively, you can use return to specify that packets matching a block rule will generate a TCP reset or an ICMP unreachable packet, depending on whether the triggering packet is TCP or UDP. This is similar to the REJECT target in Linux’s Netfilter.

For example, to have PF drop packets silently by default, add a line like this to /etc/pf.conf:

set block-policy drop

In addition to setting the block-policy, you can collect other statistics, such as packet and byte counts, for an interface. To enable this for an interface, add a line similar to this to the configuration file:

set loginterface de0

Note that you can collect these statistics on only a single interface at a time. If you do not want to collect any statistics, you can replace the interface name with the none keyword.

To better utilize resources on busy networks, you can also modify the session-timeout values. Setting the timeout interval to a low value can help improve the performance of the firewall on high-traffic networks, but at the expense of dropping valid idle connections.

To set the session timeout (in seconds), put a line similar to this one in /etc/pf.conf:

set timeout interval 20

With this setting in place, any TCP connection that is idle for 20 seconds will automatically be reset.

PF can also optimize performance on low-end hardware by tuning its memory use regarding how many states can be stored at any one time or how many fragments may reside in memory for fragment reassembly. For example, to set the number of states to 20,000 and the number of entries used by the fragment reassembler to 15,000, you could put these lines in your pf.conf file:

set limit states 20000
set limit frags 15000

Alternatively, you could combine these entries into a single statement like this:

set limit { states 20000, frags 15000 }

Traffic Normalization Rules

The next section is for traffic normalization rules. Rules of this type ensure that packets passing through the firewall meet certain criteria regarding fragmentation, IP IDs, minimum TTLs, and other attributes of a TCP datagram. Rules in this section are all prefixed by the scrub keyword. In general, just putting scrub all is fine. However, if necessary, you can get quite detailed in specifying what you want normalized and how you want to normalize it. Since you can use PF’s general filtering-rule syntax to determine what types of packets a scrub rule will match, you can normalize packets with a great deal of control.

One of the more interesting possibilities is to randomize all IP IDs in the packets leaving your network for the outside world. In doing this, you can make sure that passive operating-system-determination methods based on IP IDs will break when trying to figure out the operating system of a system protected by the firewall. Because such methods depend on analyzing how the host operating system increments the IP IDs in its outgoing packets, and your firewall ensures that the IP IDs in all the packets leaving your network are totally random, it will be pretty hard to match them against a known pattern for an operating system.

IP ID randomization also helps to prevent enumeration of machines in a network address translated (NAT) environment. Without random IP IDs, someone outside the network can perform a statistical analysis of the IP IDs being emitted by the NAT gateway in order to count the number of machines on the private network.

To enable random ID generation on an interface, put a line like this in /etc/pf.conf:

scrub out on de0 all random-id

You can also use the scrub directive to reassemble fragmented packets before forwarding them to their destinations. This helps prevent specially fragmented packets (such as packets that overlap) from evading intrusion-detection systems that are sitting behind the firewall.

To enable fragment reassembly on all interfaces, simply put the following line in the configuration file:

scrub fragment reassemble

If you want to limit reassembly to just a single interface, change it to something like:

scrub in on de0 all fragment reassemble

This will enable fragment reassembly for the de0 interface.

Filtering Rules

The next two sections of the pf.conf file involve packet queuing and address translation, but since this hack focuses on packet filtering, we’ll skip those. This brings us to the last section, which contains the actual packet-filtering rules. In general, the syntax for a filter rule can be defined by the following:

action direction [log] [quick] on int [af] [proto protocol] \
  from src_addr [port src_port] to dst_addr [port dst_port] \
  [tcp_flags] [state]

In PF, a rule can have only two actions: block and pass. As discussed previously, the block policy affects the behavior of the block action. However, you can modify the block action’s behavior for specific rules by specifying block along with the action, as in block drop or block return. Additionally, you can use block return-icmp, which will return an ICMP unreachable message by default. You can also specify an ICMP type, in which case that type of ICMP message will be returned.

For most purposes, you’ll want to start out with a default deny policy; that way, you can later add rules to allow the specific traffic that you want through the firewall.

To set up a default deny policy for all interfaces, put the following line in /etc/pf.conf:

block all

Now you can add rules to allow traffic through the firewall. First, keep the loopback interface unfiltered by using this rule:

pass quick on lo0 all

Notice the use of the quick keyword. Normally, PF will continue through the rule list even if a rule has already allowed a packet to pass, in order to see whether a more specific rule that appears later in the configuration file will drop the packet. The use of the quick keyword modifies this behavior, causing PF to stop processing the packet at this rule if it matches the packet and to take the specified action. With careful use, this can greatly improve the performance of a ruleset.

To prevent external hosts from spoofing internal addresses, you can use the antispoof keyword:

antispoof quick for $INT_IF inet

Next, you’ll want to block any packets that have a nonroutable RFC 1918 IP address from entering or leaving your external interface. Such packets, unless explicitly allowed later, would be caught by your default deny policy. However, if you use a rule to specifically match these packets and use the quick keyword, as follows, you can increase performance:

block drop quick on $EXT_IF from any to <rfc1918>

If you want to allow traffic destined for a specific web server (say, 192.168.1.20) into the network, use a rule like this:

pass in on $EXT_IF proto tcp from any to 192.168.1.20 port 80 \
  modulate state flags S/SA

This will allow packets destined to TCP port 80 at 192.168.1.20 only if they are establishing new connections (i.e., if the SYN flag is set), and will enter the connections into the state table. The modulate keyword ensures that a high-quality initial sequence number (ISN) is generated for the session, which is important if the operating system in use at either end of the connection uses a poor algorithm for generating its ISNs.

Similarly, if you want to pass traffic to and from a particular email server (say, IP address 192.168.1.21), use this rule:

pass in on $EXT_IF proto tcp from any to 192.168.1.21 \
  port { smtp, pop3, imap2, imaps } modulate state flags S/SA

Notice that you can specify multiple ports for a rule by separating them with commas and enclosing them in curly braces. You can also use service names, as defined in /etc/services, instead of specifying the services’ port numbers.

To allow traffic to a specific DNS server (say, 192.168.1.18), add a rule like this:

pass in on $EXT_IF proto tcp from any to 192.168.1.18 port 53 \
  modulate state flags S/SA

This still leaves the firewall blocking UDP DNS traffic. To allow it through, add a rule like this:

pass in on $EXT_IF proto udp from any to 192.168.1.18 port 53 \
  keep state

Notice that even though this is a rule for UDP packets, you have still used the state keyword. In this case, PF will keep track of the connection using the source and destination IP address and port pairs. Also, since UDP datagrams do not contain sequence numbers, the modulate keyword is not applicable. Using keep state instead specifies stateful inspection when not modulating ISNs. In addition, since UDP datagrams do not contain flags, simply omit them.

Now you’ll want to allow connections initiated within the network to pass through the firewall. To do this, you need to add the following rules to let the traffic into the internal interface of the firewall:

pass in on $INT_IF from $INT_IF:network to any
pass out on $INT_IF from any to $INT_IF:network 
pass out on $EXT_IF proto tcp all modulate state flags S/SA
pass out on $EXT_IF proto { icmp, udp } all keep state

In the past few releases, the popular passive OS fingerprinting tool p0f has been integrated into PF. This enables PF to ascertain the operating systems running on hosts sending traffic to or through a system running PF. Consequently, you can create PF rules that are operating-system-specific. For instance, if you want to block traffic from any system that isn’t running Linux, you can use something like this:

block in
pass in from any os "Linux"

But beware that OS fingerprinting is far from perfect. It’s entirely possible for someone to modify the characteristics of her TCP/IP stack to mimic another operating system [Hack #65].

Once you’re done editing pf.conf, you can enable PF by running the following commands:

# pfctl -e
# pfctl -f /etc/pf.conf
            

The first line enables PF, and the second line loads your configuration. If you want to make changes to your configuration, just run pfctl -f /etc/pf.conf again. To enable PF automatically at startup under OpenBSD, add the following line to /etc/rc.conf.local:

pf=YES

FreeBSD is slightly different. You’ll instead need to add the following line to /etc/rc.conf:

pf_enable="YES"

The next time you reboot, PF should be enabled.

As you can see, OpenBSD has a very powerful and flexible firewalling system. There are too many features and possibilities to discuss here. For more information, look at the excellent PF documentation available online, or the pf.conf manpage.

Protect Your Computer with the Windows Firewall

Windows XP SP2 turns on the Windows Firewall by default, so you’re automatically protected from incoming attacks. Here’s how to configure the Windows Firewall for maximum protection and flexibility and use it to log potential attacks and send information about the intruders to your ISP.

The moment you connect to the Internet, you’re in some danger of intrusion, especially if you have a broadband connection. PCs with broadband connections are tempting targets, because their high-speed connections are ideal springboards for attacking other networks or web sites.

Whenever you’re connected, your system is among many constantly being scanned for weaknesses by crackers (malicious hackers) and wannabes (often called script kiddies) sending out automated probes looking for vulnerable PCs. In fact, these kinds of probes are so common and incessant, you can think of them as the background radiation of the Internet.

One of the best ways to protect yourself against these probes and more targeted attacks is to use a firewall. Firewall software sits between you and the Internet and acts as a gatekeeper of sorts, allowing only nonmalicious traffic through.

In this hack, we’ll look at how to get the most out of the Windows Firewall, the firewall built into XP SP2, which is turned on by default when you install SP2.

Tip

Before SP2, the firewall was called the Internet Connection Firewall (ICF). It was much the same as the Windows Firewall, with some notable differences in how the firewall and its features were accessed.

The Windows Firewall offers basic Internet security by stopping all unsolicited inbound traffic and connections to your PC and network, unless your PC or another PC on the network initially makes the request for the connection. However, it will not block outgoing requests and connections, so you can continue to use the Internet as you normally would for browsing the Web, getting email, using FTP, or similar services.

The Windows Firewall has one serious drawback: it won’t protect you against Trojans, such as the Back Orifice Trojan. Trojans let other users take complete control of your PC and its resources. For example, someone could use your PC as a launchpad for attacking web sites, making it appear as though you were the culprit, or could copy all your files and find out personal information about you, such as your credit card numbers if you store them on your PC. The Windows Firewall won’t stop Trojans because it blocks only incoming traffic, and Trojans work by making outbound connections from your PC.

Tip

To stop Trojans, get a third-party firewall, such as CORE FORCE [Hack #48].

When you install XP SP2, you’re automatically protected because it turns on the Windows Firewall. However, it is possible to turn off the firewall. To make sure it’s turned on, click Security Center from the Control Panel. When the Security Center appears, there should be a green light next to the Firewall button and it should say ON, as shown in Figure 4-1.

Making sure the Windows Firewall is turned on
Figure 4-1. Making sure the Windows Firewall is turned on

If it’s not on, click the Windows Firewall icon at the bottom of the screen, click ON, and then click OK.

Allow Programs to Bypass the Firewall

The Windows Firewall offers protection from inbound threats, but it can also cause problems. A variety of software needs to be able to accept inbound connections, and the firewall will initially prevent these programs from working. Instant messaging programs and FTP programs, for example, both need to be able to accept these kinds of connections, and the Windows Firewall blocks them.

Usually, but not always, the first time you run one of these programs, you’ll get the warning from the Windows Firewall shown in Figure 4-2.

A warning from the Windows Firewall
Figure 4-2. A warning from the Windows Firewall

The warning will show you the name of the program and the publisher and will ask if you want to keep blocking the program. If you’d like the Windows Firewall to let the program function, click Unblock. To keep blocking the program, click Keep Blocking. The Ask Me Later choice lets the program accept incoming connections for just this one time; after you exit, the next time you run the program, you’ll get the same warning.

That’s well and good, but the Windows Firewall won’t always pop up this alert. So, you might find that some programs don’t work with the firewall on, but you won’t get a warning about them. Once you figure that out, you can manually tell the Windows Firewall to let those programs through by adding them to its exceptions list.

To do so, choose Control Panel→Security Center→Windows Firewall. Then click the Exceptions tab, shown in Figure 4-3.

The Windows Firewall Exceptions tab
Figure 4-3. The Windows Firewall Exceptions tab

Tip

When you get a warning from the Windows Firewall and click Ask Me Later, the temporarily allowed program will be listed on the Exceptions tab, with no check next to it.

To add a program to the exceptions list, click Add Program to bring up the window shown in Figure 4-4.

This tab lists all the programs for which the firewall will accept inbound connections. If a program is listed here but doesn’t have a check next to it, it means the firewall will block it. To tell the firewall to stop blocking inbound connections for the program, check the box next to it and click OK.

Choose a program from the list and click OK, and then click OK again to add it to your list. If the program you want to add isn’t listed in the Add a Program dialog box, click the Browse button to find it and then add it.

There might be some programs for which you want to grant access to only certain people and not others. Maybe, for example, you want to allow an instant messaging program to work only with people on your own network. There’s a way to do that.

First, add the program to the exceptions list. Then, highlight the program and choose Edit→Change Scope. The Change Scope dialog box appears, as shown in Figure 4-5.

Choose “My Network (subnet) only,” click OK, and then click OK again, and the firewall will allow only inbound connections originating from your network. To allow inbound connections for the program for only specific IP addresses, choose “Custom list,” type in the IP addresses you want to allow, and then click OK and OK again.

Choosing a program to add to your exceptions list
Figure 4-4. Choosing a program to add to your exceptions list
Granting access to your network to specific people only
Figure 4-5. Granting access to your network to specific people only

Tracking Firewall Activity with a Windows Firewall Log

The Windows Firewall can do more than just protect you from intruders; it can also keep track of all intrusion attempts so that you know whether your PC has been targeted and what kinds of attacks the Windows Firewall has turned back. Then you can send that information to your ISP so they can track down the intruders.

First, create a Windows Firewall log. From the Security Center, choose Windows Firewall→Advanced, and click the Settings button in the Security Logging section. The dialog box shown in Figure 4-6 appears.

Creating a Windows Firewall log
Figure 4-6. Creating a Windows Firewall log

Choose whether to log dropped packets, successful connections, or both. A dropped packet is a packet that the Windows Firewall has blocked. A successful connection refers to any connection you have made over the Internet, such as to a web site; it doesn’t mean an intruder has successfully connected to your PC. Because of this, there’s usually no reason for you to log successful connections. If you do log them, your log will become large quickly, and it will be more difficult to track only potentially dangerous activity. So, your best bet is to log only dropped packets.

After you’ve made your choices, choose a location for the log, set its maximum size, and click OK. I don’t let my log get larger than 1 MB, but depending on how much you care about disk space and how much you plan to use the log, you might want yours to be larger or smaller.

The log will be created in a W3C Extended Log (.log) format that you can examine with Notepad or another text editor or by using a log analysis program such as the free AWStats (http://awstats.sourceforge.net). Figure 4-7 shows a log generated by the Windows Firewall, examined in Notepad.

A log generated by the Windows Firewall
Figure 4-7. A log generated by the Windows Firewall

Each log entry has a total of up to 16 pieces of information associated with each event, but the most important columns for each entry are the first 8. (In a text editor, the names of the columns don’t align over the data, but they will align in a log analyzer.)

Table 4-1 describes the most important columns.

Table 4-1. The columns in the Windows Firewall log
NameDescription
DateDate of occurrence, in year-month-date format
TimeTime of occurrence, in hour:minute:second format
ActionThe operation that was logged by the firewall, such as DROP for dropping a connection, OPEN for opening a connection, or CLOSE for closing a connection
ProtocolThe protocol used, such as TCP, UDP, or ICMP
Source IP (src-ip)The IP address of the computer that started the connection
Destination IP (dst-ip)The IP address of the computer to which the connection was attempted
Source Port (src-port)The port number on the sending computer from which the connection was attempted
Destination Port (dst-port)The port to which the sending computer was trying to make a connection
sizeThe packet size
tcpflagsInformation about TCP control flags in TCP headers
tcpsynThe TCP sequence of a packet
tcpackThe TCP acknowledgment number in the packet
tcpwinThe TCP window size of the packet
icmtypeInformation about the ICMP messages
icmcodeInformation about the ICMP messages
infoInformation about an entry in the log

The source IP address is the source of the attack. If you notice the same source IP address continually cropping up, an intruder might be targeting you. It’s also possible that the intruder is sending out automated probes to thousands of PCs across the Internet and your PC is not under direct attack. In either case, you can send the log information to your ISP and ask them to follow up by tracking down the source of the connection attempts. Either forward the entire log or cut and paste the relevant sections to a new file.

Problems with Email and the Windows Firewall

Depending on the email program you use and how it gets notification of new messages, the Windows Firewall could interfere with the way you retrieve your email. It won’t stop you from getting your mail, but it could disable your email program’s notification feature.

The Windows Firewall won’t interfere with the normal notification feature of Outlook Express, because the initial request asking for notification of new email comes from Outlook Express, which is inside the firewall. When the server responds to the request, the firewall recognizes that the server is responding to the request from Outlook Express, so it lets the communication pass through.

However, if you use Outlook and connect to a Microsoft Exchange server using a remote procedure call (RPC) to send email notifications (which is usually the case with Exchange), you’ll run into problems. Because the RPC initially comes from the server, not from Outlook, the firewall won’t allow the notification to pass to you. In this case, you can still retrieve your email, but you’ll have to check for new messages manually; you won’t be able to get automatic notifications from the server. So, if you find that you stop getting new mail notifications after you install the Windows Firewall, it’s not that coworkers, friends, and spammers are suddenly ignoring you; you’ll just have to check for new mail manually.

Hacking the Hack

The Windows Firewall Exceptions tab is especially useful for anyone who uses file sharing on a home or corporate network but wants to turn it off when on a public network connection, such as a WiFi hotspot. When you get to a hotspot, before connecting, go to the Exceptions tab, uncheck the box next to File and Printer Sharing, and click OK. File sharing will be turned off. Then, when you get back to your home or business network, turn it back on again.

See Also

  • For more information about the Windows Firewall, see Microsoft Knowledge Base Article 875357 (http://support.microsoft.com/kb/875357)

Preston Gralla

Close Down Open Ports and Block Protocols

You don’t need a firewall to protect your PC; you can manually close down ports and block certain protocols.

As noted in “Protect Your Computer with the Windows Firewall” [Hack #46], firewalls can protect your PC and your network from intruders. But if you don’t want to use a firewall and you still want protection, you can manually close down ports and block protocols.

Some of these ports and protocols are more dangerous than others. For example, if you leave open the port commonly used by telnet (port 23), someone could use that service to take control of your PC. Other risky ports include those used by the infamous Back Orifice Trojan, which also can give malicious users complete control of your PC. Back Orifice uses a variety of ports, including 31337 and 31338, among others. For a list of ports used by Trojans, go to http://www.sans.org/resources/idfaq/oddports.php.

In this hack, you’ll need to know which ports you want to be open on your PC—such as port 80 for web browsing—and you’ll close down all the others. For a complete list of well-known ports, go to http://www.iana.org/assignments/port-numbers.

To close down ports and protocols manually, right-click My Network Places and choose Properties to open the Network Connections folder. Right-click the connection for which you want to close ports and choose Properties. Highlight the Internet Protocol (TCP/IP) listing and choose Properties. On the General tab, click the Advanced button. Click the Options tab, highlight “TCP/IP filtering,” and choose Properties. The TCP/IP Filtering dialog box appears. To block TCP ports, UDP ports, and IP protocols, choose the Permit Only option for each. Doing this will effectively block all TCP ports, UDP ports, and IP protocols.

You don’t want to block all ports, though, so you have to add the ports you want to allow to pass. For example, you need to keep port 80 open if you want to browse the Web. Click Add to add the ports or protocols you will allow to be used, as shown in Figure 4-8. Keep adding as many ports and protocols as you want to be enabled, and click OK when you’re done. You’ll be able to use only the ports and protocols that are listed.

Blocking TCP ports, UDP ports, and IP protocols
Figure 4-8. Blocking TCP ports, UDP ports, and IP protocols

Keep in mind that Internet applications and services use hundreds of TCP and UDP ports. If, for example, you enable only web access, you won’t be able to use other Internet resources, such as FTP, email, file sharing, listening to streaming audio, viewing streaming video, and so on. So, use this hack only if you want your PC to use a very limited number of Internet services and applications.

Preston Gralla

Replace the Windows Firewall

Block both incoming and outgoing traffic with CORE FORCE.

As of Windows XP SP2, Microsoft has done the world a favor by enabling the Windows Firewall [Hack #46] by default. However, the Windows Firewall can often give users a false sense of security, especially as the plagues of malware targeting Windows systems grow. While Windows XP’s firewall is great at protecting systems from attacks against services, it does little to prevent keyloggers and other forms of malware from phoning home with your vital private information.

Where XP’s firewall fails, CORE FORCE (http://force.coresecurity.com) from CORE Security excels. CORE FORCE includes a Windows port of OpenBSD’s Packet Filter [Hack #45] in addition to a file and registry access monitor and an application binary integrity checker.

Installing CORE FORCE

Before you install CORE FORCE, exit any applications that need to maintain network connectivity in order to function properly, because the installation process might disrupt them. Then, install CORE FORCE by downloading the installer and launching it. Other than the previously noted caveat of losing network connectivity, the installation process is pretty normal; you select where to install the package files and it installs them.

As part of the process, CORE FORCE installs custom firewalling drivers into your network stack, so a dialog like the one shown in Figure 4-9 might alert you that hardware is being installed and ask if you want to stop the installation. This is a normal warning, so you can just click Continue Anyway. (You might be prompted multiple times; if so, just click the Continue Anyway button each time.)

Window XP’s hardware installation prompt
Figure 4-9. Window XP’s hardware installation prompt

The Configuration Wizard

After the installation has finished, you’ll need to restart your computer. Once your system has finished booting up and you have logged in, you’ll be presented with CORE FORCE’s setup wizard, shown in Figure 4-10. This wizard helps you choose how restrictive you’d like the system to be and tell it about your network, DNS servers, and other servers that you access regularly, so that it can apply this information to its firewall rules.

CORE FORCE’s setup wizard
Figure 4-10. CORE FORCE’s setup wizard

After clicking Next, you’ll be presented with a choice of which security level to use, Medium or High, as shown in Figure 4-11. For now, go ahead and select Medium. You can change this setting and make additional tweaks to your system’s policy in CORE FORCE’s configuration tool when the wizard’s finished.

Choosing a security level
Figure 4-11. Choosing a security level

During the next step, the wizard prompts you to enter basic information about your network, as shown in Figure 4-12. This information includes the Classless Inter-Domain Routing (CIDR) address block your local network occupies, your broadcast address, and up to two name servers that you use.

Entering basic network information
Figure 4-12. Entering basic network information

The next step (shown in Figure 4-13) prompts you for information about servers that you use, such as the hostnames of your incoming and outgoing mail servers, your news server and web proxy server (if you have them), and your domain controller.

Entering server information
Figure 4-13. Entering server information

Items that require a port should have a default filled in for you, but you can change the port used if necessary. For instance, if you use IMAP or IMAP+SSL instead of POP3 for your incoming mail, change the port for your incoming mail server to either port 143 or 993, respectively.

After you’ve entered your server’s information and clicked Next, the setup wizard scans your system for programs for which CORE FORCE has preconfigured profiles. For the most part, these preconfigured profiles limit their corresponding applications to performing only their core purposes. For instance, your email client should be able to connect to your incoming and outgoing mail servers only and, with the exception of saving attachments, it should write files only to certain well-known locations on your system. If someone attempts to exploit your mail client, this setting limits what the intruder can do.

When the setup wizard has finished looking for programs, you’ll be presented with a dialog like the one shown in Figure 4-14.

Selecting preconfigured application profiles
Figure 4-14. Selecting preconfigured application profiles

CORE FORCE adds a checkmark next to any program it locates. You might also see that some programs were found but that their signatures were invalid. Don’t be alarmed about this. CORE FORCE maintains a database of valid program hashes for the applications for which it has preconfigured profiles, and it might display signatures as invalid simply because it does not have the signatures for the most recent versions of programs that are updated frequently (e.g., Internet Explorer and Firefox. You can update CORE FORCE’s database to contain the signatures of what’s currently on your system by checking the “Update unrecognized signatures...” box.

After you’ve finished selecting which profiles to install, click Next to import all of the profiles you have selected. Click Next again, then Finish, and you’re done. CORE FORCE’s configuration interface should appear, as shown in Figure 4-15.

CORE FORCE’s configuration interface
Figure 4-15. CORE FORCE’s configuration interface

Manual Configuration

CORE FORCE’s configuration consists of a system-wide default security level and application-specific security levels. In turn, each security level consists of policies that have been assigned to it, such as “Cannot execute from temp,” which define restrictions to be placed on the system or application. Things that can be defined for each policy include firewall rules, Registry access restrictions, and filesystem restrictions.

When going through the setup wizard, you selected a security level to use for the whole system. If you want to view exactly what policies a security level has, expand the Security Levels item in the tree view and then expand the security level that you want to examine. Figure 4-16 shows the policies comprising the Medium security level.

Viewing policies for the Medium security level
Figure 4-16. Viewing policies for the Medium security level

Look over the policies that your current security level enforces. If you decide that you want to change the security level for the entire system, right-click the System item in the tree view and choose Change Security Level. You should see a dialog like Figure 4-17. Use the slider to change the level.

Changing the security level
Figure 4-17. Changing the security level

If you want to make the current configuration more or less restrictive, you can modify it at either the system or application level by selecting the Permissions item in the tree view. For instance, Figure 4-18 shows the firewall permissions that are applied to any program that doesn’t have its own settings defined.

System firewall settings
Figure 4-18. System firewall settings

To add a firewall rule, right-click within the pane displaying the firewall rules and choose New to insert a rule that allows any packet to pass through the firewall. You can then modify the rule by using the widgets located to the right of the rule list.

One thing that CORE Security added when porting PF to Windows was a new rule action: Ask. Choosing this action displays a dialog like Figure 4-19, alerting you that an application is attempting to make a connection. From the dialog, you can choose to either allow or deny the connection.

Allowing or denying a connection
Figure 4-19. Allowing or denying a connection

This feature is especially useful for catching software phoning home, but you can also use it to spot an exploited program making network connections that it normally wouldn’t.

As you can see, CORE FORCE is a powerful firewall and system-monitoring package. It provides a great deal of flexibility in terms of which system operations it can track and limit and is very configurable. Unfortunately, there’s not enough space here to cover everything you can do with it, so be sure to take a look at CORE FORCE’s excellent help file.

Create an Authenticated Gateway

Use PF to keep unauthorized users off the network.

Firewalling gateways have traditionally been used to block traffic from specific services or machines. Instead of watching IP addresses and port numbers, an authenticated gateway allows you to regulate traffic to or from machines based on a user’s credentials. With an authenticated gateway, users have to log in and authenticate themselves to the gateway in order to gain access to the protected network. This can be useful in many situations, such as restricting Internet access or restricting a wireless segment to be used only by authorized users.

With the release of OpenBSD 3.1, you can implement this functionality via PF and the authpf shell. Using authpf also provides an audit trail by logging usernames and originating IP addresses, the time that each user authenticates with the gateway, and when users log off the network.

To set up authentication with authpf, you’ll first need to create an account on the gateway for each user. Specify /usr/sbin/authpf as the shell, and be sure to add authpf as a valid shell to /etc/shells. When a user logs in through SSH, authpf will obtain the user’s name and IP address through the environment.

After doing this, a template file containing NAT and filter rules is read in, and the username and IP address are applied to it. The resulting rules are then added to the running configuration. When the user logs out (i.e., types ^C), the rules that were created are unloaded from the current ruleset.

For user-specific rule templates, authpf looks in /etc/authpf/users/$USER/authpf.rules. Global rule templates are stored in /etc/authpf/authpf.rules. Similarly, NAT entries are stored in authpf.nat, in either of these two directories. When a user-specific template is present for the user who has just authenticated, the template completely replaces the global rules, instead of just adding to them. When loading the templates, authpf will expand the $user_ip macro to the user’s current IP address:

pass in quick on wi0 proto { tcp, udp } from $user_ip to any \
  keep state flags S/SA

This particular rule will pass in all traffic on the wireless interface from the newly authenticated user’s IP address. This works particularly well with a default deny policy, where only the initial SSH connection to the gateway and DNS have been allowed from the authenticating IP address.

You could be much more restrictive and allow only HTTP-, DNS-, and email-related traffic through the gateway:

pass in quick on wi0 proto tcp from $user_ip to any \
  port { smtp, www, https, pop3, pop3s, imap, imaps } \
  keep state flags S/SA
pass in quick on wi0 proto udp from $user_ip to any port domain

After the template files have been created, you must then provide an entry point into pf.conf for the rules that authpf will create for evaluation by PF. These entry points are added to your pf.conf with the various anchor keywords:

nat-anchor authpf
rdr-anchor authpf
binat-anchor authpf
anchor authpf

Note that each anchor point needs to be added to the section to which it applies; you cannot just put them all at the end or beginning of your pf.conf file. Thus, the nat-anchor, rdr-anchor, and binat-anchor entries must go into the address translation section of pf.conf, while the anchor entry, which applies only to filtering rules, should be added to the filtering section.

When a user logs into the gateway, he should now be presented with a message like this:

Hello andrew, You are authenticated from host "192.168.0.61"

The user will also see the contents of /etc/authpf/authpf.message if it exists and is readable.

If you examine /var/log/daemon, you should also see log messages similar to these for when a user logs in and out:

Dec  3 22:36:31 zul authpf[15058]: allowing 192.168.0.61, \
  user andrew
Dec  3 22:47:21 zul authpf[15058]: removed  192.168.0.61, \
  user andrew- duration 650 seconds

Note that, since it is present in /etc/shells, any user that has a local account is capable of changing his shell to authpf. If you want to ensure that a particular user cannot do this, you can create a file named after that user and put it in the /etc/authpf/banned directory. The contents of this file will be displayed when the user logs into the gateway. Conversely, you can explicitly allow users by listing their usernames, one per line, in /etc/authpf/authpf.allow. However, any bans that have been specified in /etc/authpf/banned take precedence over entries in authpf.allow.

Since authpf relies on the SSH session to determine when the rules pertaining to a particular user are to be unloaded, care should be taken in configuring your SSH daemon to time out connections. Timeouts should happen fairly quickly, to revoke access as soon as possible once a connection has gone stale. This also helps prevent connections to systems outside the gateway from being held open by those conducting ARP spoof attacks.

You can set up OpenSSH to guard against this by adding these to lines to your sshd_config:

ClientAliveInterval 15
ClientAliveCountMax 3

This will ensure that the SSH daemon will send a request for a client response 15 seconds after it has received no data from the client. The ClientAliveCountMax option specifies that this can happen three times without a response before the client is disconnected. Thus, after a client has become unresponsive, it will disconnect after 45 seconds. These keepalive packets are sent automatically by the SSH client software and don’t require any intervention on the part of the user.

authpf is powerful in its flexibility and integration with PF, OpenBSD’s native firewalling system. It is easy to set up and has very little performance overhead, since it relies on SSH and the operating system to do authentication and manage sessions.

Keep Your Network Self-Contained

Use egress filtering to mitigate attacks and information leaks coming from your network.

By now you should be familiar with the concept of firewalling as it applies to blocking traffic coming into your network. But have you considered the benefits of filtering traffic that leaves your network? For instance, what would happen if someone compromised a host on your network and used it as a platform to attack other networks? What if a worm somehow made it onto your network and tried to infect hosts across the Internet? At the very least, you would probably receive some angry phone calls and emails.

Luckily, filtering your outbound traffic—otherwise known as egress filtering—can help to contain such malicious behavior. Egress filtering not only can protect others from attacks originating from your network, but also can be used to enforce network usage policies and make sure information doesn’t leak out of your network onto the wider Internet. In many situations, egress filtering is just as important as filtering inbound traffic.

The general guideline when crafting egress-filtering rules is the same as when constructing any inbound-filtering rule: devices should be allowed to do only what they were meant to do. That is, a mail server should be allowed to serve and relay mail only, a web server should be allowed to serve web content only, a DNS server should service DNS requests only, and so on. By ensuring that this policy is implemented, you can better contain the threats mentioned earlier.

It might also be a good idea to force users to use internal services rather than Internet services wherever possible. For example, if you are using your own DNS servers, clients shouldn’t be able to connect to external DNS servers to resolve hostnames. If clients are allowed to do this, you risk the chance that they will reveal intranet hostnames to outside parties when they attempt to resolve internal hostnames through an external DNS server.

This restriction can be accomplished in OpenBSD with a rule like this:

rdr on $INT_IF inet proto { tcp, udp } from $INT_IF:network to any port 53 
-> $DNS_SERVER port 53

Of course, you’ll need to set INT_IF to the interface facing your internal network and set DNS_SERVER to the IP address of your internal DNS server.

If you’re using Netfilter [Hack #44], you’ll have to use four rules to accomplish the same goal:

# iptables -t nat -A PREROUTING -p tcp -i 
            
               $INT_IF 
            
            --dport 53 -j DNAT \ 
            --to-destination 
            
               $DNS_SERVER
            
            :53
# iptables -t nat -A PREROUTING -p udp -i 
            
               $INT_IF
             
            --dport 53 -j DNAT \
            --to-destination 
            
               $DNS_SERVER
            
            :53 
# iptables -t nat -A POSTROUTING -p tcp -o 
            
               $EXT_IF
             
            --sport 53 -j SNAT \
            --to-source 
            
               $SNAT_IP
            
# iptables -t nat -A POSTROUTING -p udp -o 
            
               $EXT_IF 
            
            --sport 53 -j SNAT \
            --to-source 
            
               $SNAT_IP
            
         

The first two rules specify that the destination address of any incoming packet destined for TCP or UDP port 53 should be rewritten to DNS_SERVER. However, this will cause any response to the rewritten packet to be sent to the host that initiated the connection. If the server to which the host originally intended to connect is not DNS_SERVER, the response from DNS_SERVER will be silently dropped.

The next two rules fix this by performing address translation on the source address of the packet before it is sent out. That sends DNS_SERVER’s response back to the host running Netfilter, and the host then translates the destination address back to the host that initiated the connection. You should set SNAT_IP to the IP address on the machine running Netfilter that is visible to DNS_SERVER.

Similarly, if you’re running an internal mail server and want to monitor email that exits your enterprise, you’ll need to prevent your users from sending email through external mail servers. In OpenBSD, can do this by using a similar rule to force all SMTP traffic to be redirected to your own SMTP server:

rdr on $INT_IF inet proto tcp from $INT_IF:network to any port 25 -> $SMTP_HOST port 25

For Netfilter, the same result can be accomplished with these two rules:

# iptables -t nat -A PREROUTING -p tcp -i 
            
               $INT_IF 
            
            --dport 25 -j DNAT \ 
            --to-destination 
            
               $SMTP_HOST
            
            :25
# iptables -t nat -A POSTROUTING -p tcp -i 
            
               $EXT_IF 
            
            --sport 25 -j SNAT \ 
            --to-source 
            
               $SNAT_IP
            
         

Egress filtering can also prevent IP spoofing. By filtering on your external interface at the border of your network, you can verify that packets leaving your network have source addresses that match your address space. By filtering all other traffic, you can ensure that any IP spoofing attack performed from your network or routed through it will be dropped before the packets are able to leave.

Test Your Firewall

Find out if your firewall really works the way you think it should.

So, you’ve set up a firewall and done a few cursory tests to make sure it’s working, but have you tested the firewall to be sure that it’s blocking everything it’s supposed to? You might not have done this because you think it will take too long or be too difficult. Luckily, there’s FTester (http://dev.inversepath.com/trac/ftester/), a free tool for doing extensive firewall tests.

FTester consists of three Perl scripts. The ftest script is used for injecting custom packets as defined in the configuration file ftest.conf. If you are testing how the firewall behaves with ingress traffic, you should run this script on a machine outside of your firewalled network. If you want to test your firewall’s behavior toward egress traffic, you will need to run ftest from a machine within your firewall’s protected network.

One of the other scripts, ftestd, listens for the packets injected with ftest that come through the firewall that you are testing. You should run this script on a machine within your internal network if you are testing the firewall’s ingress behavior. If you are testing egress behavior, you’ll need to run it on a machine external to your network. Both of these scripts keep a log of what they send or receive. After a test run, their respective logs can be compared using the freport script, to quickly see what packets were able to get through the firewall.

Before you can use FTester, you will need the Net::RawIP , Net::PcapUtils, and NetPacket Perl modules. You will also need the Net::Pcap module if it is not already installed, since the Net::PcapUtils module depends on it. If you have the CPAN Perl module available, you can install these modules with the following commands:

# perl -MCPAN -e "install Net::RawIP"
# perl -MCPAN -e "install Net::PcapUtils"
# perl -MCPAN -e "install NetPacket"
         

Once these modules are available on the systems you will be using to conduct your firewall test, you will need to create a configuration file to tell ftest what packets it should generate.

Here’s the general form for a TCP or UDP packet in ftest.conf, where source addr and source port are the source IP address and port, and dest addr and dest port are the destination IP address and port:

            source addr:source port:dest addr:dest port:flags:proto:tos
         

Address ranges can be specified in the low - high format or by using CIDR notation. You can also specify port ranges using the low - high format. The flags field is where you specify the TCP flags that you want set for the packet. Valid values for this field are S for SYN, A for ACK, P for PSH, U for URG, R for RST, and F for FIN. The proto field specifies which protocol to use (either TCP or UDP), and tos contains the number to set for the Type-of-Service (ToS) field in the IP header. Sometimes, routers use the contents of this field to make decisions about traffic prioritization. You can get more information on the ToS field by reading RFC 791 (http://www.ietf.org/rfc/rfc0791.txt), which defines the Internet Protocol.

You can define ICMP packets in a similar manner. Here’s the general form for one:

            source addr::dest addr:::ICMP:type:code
         

As you can see, the main difference between the two forms is the omission of port numbers and flags, which ICMP does not use. Instead, it uses types and codes (hence the addition of the type and code fields). Currently, there are over 40 ICMP types. The ones used by the ping utility, echo (type 8) and echo reply (type 0), or the type used by the traceroute command (type 30), might be familiar to you. ICMP codes are like subclassifications of ICMP types. Not all ICMP types have ICMP codes associated with them, although there are roughly the same number of ICMP codes as types. You can find out more about ICMP types and codes by reading the Internet Assigned Numbers Authority’s assignments for them at http://www.iana.org/assignments/icmp-parameters.

Here’s an ftest.conf that will check all of the privileged TCP ports on a machine with the IP address 10.1.1.1:

192.168.1.10:1025:10.1.1.1:1-1024:S:TCP:0
stop_signal=192.168.1.10:1025:10.1.1.1:22:S:TCP:0

stop_signal creates a payload for the packet that will tell ftestd that the testing is over. For quick tests, you can use the -c option and specify a packet to send using the syntax described previously. For instance, the following command sends a packet with the source IP address and port of 192.168.1.10:1025 to port 22 on 10.1.1.1.1:

# ./ftest -c 192.168.1.10:1025:10.1.1.1:22:S:TCP:0
         

Before starting ftest, you should start ftestd:

# ./ftestd -i eth0
         

Then, run ftest:

# ./ftest -f ftest.conf
         

This command creates a log file called ftest.log containing an entry for every packet ftest sent. When ftestd receives the signal to stop, it will exit. You can then find its log of what packets it received in ftestd.log.

Now, you can copy the logs to the same machine and run them through freport. If you used a configuration file like the one shown earlier and were allowing SSH, SMPTP, and HTTP traffic, you might get a report similar to this:

# ./freport ftest.log ftestd.log

Authorized packets:
-------------------

22 - 192.168.1.10:1025 > 10.1.1.1:22 S TCP 0
25 - 192.168.1.10:1025 > 10.1.1.1:25 S TCP 0
80 - 192.168.1.10:1025 > 10.1.1.1:80 S TCP 0

Modified packets (probably NAT):
--------------------------------

Filtered or dropped packets:
----------------------------

1 - 192.168.1.10:1025 > 10.1.1.1:1 S TCP 0
2 - 192.168.1.10:1025 > 10.1.1.1:2 S TCP 0
3 - 192.168.1.10:1025 > 10.1.1.1:3 S TCP 0

If you are using a stateful firewall and want to test this functionality, you can also specify packets that have flags other than SYN set. For instance, if the previous example had used ACK or some other flag instead of SYN, it would have been dropped by the firewall because only packets with the SYN flag set are used to initiate connections.

It’s a good idea to run ftest each time you make changes to your firewall, or periodically just to make sure that your firewall works as you expect. While complex rulesets on your firewall can sometimes make it difficult to predict exactly how it will behave, ftest will tell you with good authority exactly what kinds of traffic are permitted.

MAC Filter with Netfilter

Keep unwanted machines off your network with MAC address whitelisting.

Media Access Control (MAC) address filtering is a well-known method for protecting wireless networks. This type of filtering works on the default deny principle: you specify the hosts that are allowed to connect, while leaving unknown ones behind. MAC addresses are unique 48-bit numbers that have been assigned to every Ethernet device that has ever been manufactured, including 802.11 devices, and are usually written as six 8-bit hexadecimal digits separated by colons.

In addition to Linux’s native IP packet filtering system, Netfilter contains MAC address filtering functionality. While many of the wireless access points on the market today already support this, there are many older ones that do not. MAC filtering is also important if your access point is actually the Linux machine itself, using a wireless card. If you have a Linux-based firewall already set up, it’s a trivial modification to enable it to filter at the MAC level. MAC address filtering with iptables is much like IP-based filtering and is just as easy to do.

The following example demonstrates how to allow a particular MAC address if your firewall policy is set to DROP [Hack #44]:

# iptables -A FORWARD -m state --state NEW \
-m mac --mac-source 00:DE:AD:BE:EF:00 -j ACCEPT

This command allows any traffic sent from the network interface with the address 00:DE:AD:BE:EF:00. Using rules like this along with a default deny policy enables you to create a whitelist of the MAC addresses that you want to allow through your gateway. To create a blacklist, you can employ a default accept policy and change the MAC address matching rule’s target to DENY.

This is all pretty straightforward if you already know the MAC addresses for which you want to create rules, but what if you don’t? If you have access to the system, you can find out the MAC address of an interface by using the ifconfig command:

$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:0C:29:E2:2B:C1  
          inet addr:192.168.0.41  Bcast:192.168.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:132893 errors:0 dropped:0 overruns:0 frame:0
          TX packets:17007 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100 
          RX bytes:46050011 (43.9 Mb)  TX bytes:1601488 (1.5 Mb)
          Interrupt:10 Base address:0x10e0

Here you can see that the MAC address for this interface is 00:0C:29:E2:2B:C1. The output of ifconfig differs slightly on other operating systems, but much the same information is provided (this output is from a Linux system).

Finding the MAC address of a remote system is slightly more involved and can be done using the arp and ping commands. Pinging the remote system resolves its IP address to a MAC address, which can then be looked up using the arp command.

For example, to look up the MAC address that corresponds to the IP address 192.168.0.61, run the following commands:

$ ping -c 1 192.168.0.61
$ /sbin/arp 192.168.0.61 | awk '{print $3}'
         

Or use this very small and handy shell script:

#!/bin/sh
ping -c $1 >/dev/null && /sbin/arp $1 | awk '{print $3}' \
  | grep -v Hwaddress

When implementing MAC address filtering, be aware that it is not foolproof. Under many circumstances, it is quite trivial to change the MAC address that an interface uses by simply instructing the driver to do so. It is also possible to send out link-layer frames with forged MAC addresses by using raw link-layer sockets. Thus, MAC address filtering should be considered only an additional (rather than a primary) network-protection mechanism. It’s more like a “Keep Out” sign than a good deadbolt!

Block Tor

Keep your users from bypassing egress filtering by blocking access to Tor.

Tor [Hack #37] is a great tool for protecting your privacy when using the Internet, but it can also provide a way for your users to circumvent security measures that you’ve put in place on your network, such as egress filtering [Hack #50] or proxies. Therefore, you might want a way to block your users from using it.

One simple way to do this is to block access to Tor’s directory servers. When Tor starts up for the first time, it connects to one of these servers to get a list of all the possible nodes through which Tor can construct a virtual circuit. Logically, if you block access to all of these servers at the border, Tor will be unable to download the node list and won’t be able to function.

If you look at src/or/config.c in the Tor source tree, you’ll see a function called add_default_trusted_dirservers(). This function contains the list of the directory servers:

const char *dirservers[] = {
    "moria1 v1 18.244.0.188:9031 "
      "FFCB 46DB 1339 DA84 674C 70D7 CB58 6434 C437 0441",
    "moria2 v1 18.244.0.114:80 "
      "719B E45D E224 B607 C537 07D0 E214 3E2D 423E 74CF",
    "tor26 v1 86.59.21.38:80 "
      "847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
    "lefkada 140.247.60.64:80 "
      "38D4 F5FC F7B1 0232 28B8 95EA 56ED E7D5 CCDC AF32",
    "dizum 194.109.206.212:80 "
      "7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755",
    NULL
  };

Tip

This list of servers can change, so be sure to check new releases of Tor to see if any have been added.

All you have to do is block them at your border firewall. For instance, you could use the following rules in PF:

table <tor_dirservers> { 18.244.0.188, 18.244.0.114, 86.59.21.38, 140.247.60.64, 194.109.206.212 }
block from any to <tor_dirservers>

However, it will still be possible for someone who’s already obtained a copy of the Tor node list to use Tor. To combat this situation, you can download the list manually from one of the directory servers, then individually block each Tor node, like so:

$ links -source http://18.244.0.114:80/ | egrep '^router '
router moria2 18.244.0.114 443 0 80
router anselcomputers 24.170.55.120 9001 0 0
router sic4gh 84.172.97.158 443 0 0
router Sivusto9022 80.222.75.74 9001 0 9030
router vader 149.9.0.21 9001 0 9030
router duglha 82.227.178.224 9001 0 9002
router nycbug 64.90.179.108 443 0 80
router BlueNeedle 12.222.100.156 6571 0 0
router 1984jhb 84.58.246.2 43567 0 0
router Pastis 82.67.175.80 9001 0 9030
...

The first item after the router keyword is the router’s nickname, the next field is its IP address, and the remaining fields are the ports on which that particular router is listening for Tor connections. Here’s a quick little Perl script to transform this into a more easily readable form:

#!/usr/bin/perl

while (<>) {

  if (/^router\ /) {
    @router_stmt = split();
    for($i = 3; $i < $#router_stmt; $i++) {
      if ($router_stmt[$i] != 0) {
        print "$router_stmt[2]:$router_stmt[$i]\n";
      }  
    }
  }
}

Here is what the output looks like when the script is executed:

$ links -source http://18.244.0.114:80/ | ~/src/tor_routers.pl
18.244.0.114:443
24.170.55.120:9001
84.172.97.158:443
80.222.75.74:9001
149.9.0.21:9001
82.227.178.224:9001
64.90.179.108:443
12.222.100.156:6571
84.58.246.2:43567
154.35.254.172:9001
...

This script can easily be modified to output firewall rules for whatever firewall you’re using, be it Netfilter [Hack #44], PF [Hack #45], or something else. You’ll also want to update the rules periodically to cover new nodes that have joined the Tor network.

Get Network Security Hacks, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.