Linux Firewalls: Attack Detection and Response with iptables, psad, and fwsnort
A firewall is implemented as a series of packet-filtering rules defined by options on the iptables command line. iptables is executed once for each individual rule. (Different firewalls can range from a dozen rules to hundreds.) The iptables invocations should be made from an executable shell script, not directly from the command line. You should invoke the complete firewall shell script. Do not attempt to invoke specific iptables rules from the command line because this could cause your firewall to accept or drop packets inappropriately. When the chains are initialized and the default drop policy is enabled, all network services are blocked until acceptance filters are defined to allow the individual service. Ideally, you should execute the shell script from the console. Only the brave execute the firewall shell script from either a remote machine or an X Window xterm session. Not only is remote network traffic blocked, but access to the local loopback interface used by X Windows is blocked until access to the interface is explicitly reenabled. Ideally, X Windows should not be running nor even installed on a firewall. It is a typical example of software that is not necessary and has been used as a means to exploit servers in the past. As someone who manages Linux computers that are geographically hundreds to thousands of miles away, I can activate a firewall script only from a remote location. In these instances, it's advisable to do two things. First, change the default policy to ACCEPT for the first or first few executions of the firewall script. Do this to debug the syntax of the script itself, not the rules. After the script is syntactically correct, change that policy back to DROP. A second and just as important tip for working with firewall scripts from remote locations is to create a cron job to stop the firewall at some point in the near future. Doing so will effectively allow you to enable the firewall and perform some testing but also enable you to get back into the computer if you lock yourself out through misplaced (or missing) rules. For example, when debugging a firewall script, I'll create a cron entry to disable the firewall every 2 minutes. I can then safely run the firewall script and find out whether I've locked out my SSH session. If indeed I have locked myself out, I merely wait a few minutes for the firewall script to run and shut the firewall down, giving me the opportunity to fix the script and try again. Furthermore, remember that firewall filters are applied in the order in which you've defined them on the INPUT or OUTPUT chain. The rules are appended to the end of their chain in the order in which you define them. The first matching rule wins. Because of this, firewall rules must be defined in a hierarchical order from most specific to more general rules. Firewall initialization is used to cover a lot of ground, including defining global constants used in the shell script, enabling kernel support services (when necessary), clearing out any existing rules in the firewall chains, defining default policies for the INPUT and OUTPUT chains, reenabling the loopback interface for normal system operation, denying access from any specific hosts or networks you've decided to block, and defining some basic rules to protect against bad addresses and to protect certain services running on unprivileged ports. Symbolic Constants Used in the Firewall Examples
A firewall shell script is easiest to read and maintain if symbolic constants are used for recurring names and addresses. The following constants either are used throughout the examples in this chapter or are universal constants defined in the networking standards. This example also includes the "shebang" interpreter line from above as a friendly reminder: #!/bin/sh IPT="/sbin/iptables" # Location of iptables on your system INTERNET="eth0" # Internet-connected interface LOOPBACK_INTERFACE="lo" # however your system names it IPADDR="my.ip.address" # your IP address MY_ISP="my.isp.address.range" # ISP server & NOC address range SUBNET_BASE="my.subnet.network" # Your subnet's network address SUBNET_BROADCAST="my.subnet.bcast" # Your subnet's broadcast address LOOPBACK="127.0.0.0/8" # reserved loopback address range CLASS_A="10.0.0.0/8" # class A private networks CLASS_B="172.16.0.0/12" # class B private networks CLASS_C="192.168.0.0/16" # class C private networks CLASS_D_MULTICAST="224.0.0.0/4" # class D multicast addresses CLASS_E_RESERVED_NET="240.0.0.0/5" # class E reserved addresses BROADCAST_src="/books/3/251/1/html/2/0.0.0.0" # broadcast source address BROADCAST_DEST="255.255.255.255" # broadcast destination address PRIVPORTS="0:1023" # well-known, privileged port range UNPRIVPORTS="1024:65535" # unprivileged port range
Constants not listed here are defined in the context of the specific rules they are used with. Enabling Kernel-Monitoring Support
Operating system support for various types of packet checking often overlaps with what the firewall can test for. When in doubt, aim for redundancy or defense in depth. From the commands shown in the following lines, icmp_echo_ignore_broadcasts instructs the kernel to drop ICMP echo-request messages directed to broadcast or multicast addresses. (Another facility, icmp_echo_ignore_all, drops any incoming echo-request message. It should be noted that ISPs often rely on ping to help diagnose local network problems, and DHCP sometimes relies on echo-request to avoid address collision.) # Enable broadcast echo Protection echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts Source routing is rarely used legitimately today. Firewalls commonly drop all source-routed packets. These commands disable source routed packets: # Disable Source Routed Packets for f in /proc/sys/net/ipv4/conf/*/accept_source_route; do echo 0 > $f done
TCP SYN cookies are a mechanism to attempt speedier detection of and recovery from SYN floods. This command enables SYN cookies: # Enable TCP SYN Cookie Protection echo 1 > /proc/sys/net/ipv4/tcp_syncookies
ICMP redirect messages are sent to hosts by their adjacent routers. Their purpose is to inform the host that a shorter path is available. That is, the host and both routers are on the same network, and the new router is the router to which the original would send the packet as its next hop. Routers generate redirect messages for hosts; hosts do not. Hosts are required to honor redirects and add the new gateway to their route cache, except in the cases indicated in RFC 1122, "Requirements for Internet HostsCommunication Layers," Section 3.2.2.2: "A Redirect message SHOULD be silently discarded if the new gateway address it specifies is not on the same connected (sub-) net through which the Redirect arrived [INTRO:2, Appendix A], or if the source of the Redirect is not the current first-hop gateway for the specified destination (see Section 3.3.1)." These commands enable redirects: # Disable ICMP Redirect Acceptance for f in /proc/sys/net/ipv4/conf/*/accept_redirects; do echo 0 > $f done # Don't send Redirect Messages for f in /proc/sys/net/ipv4/conf/*/send_redirects; do echo 0 > $f done
rp_filter attempts to implement source address validation as described in RFC 1812, "Requirements for IP Version 4 Routers," Section 5.3.8. In short, packets are silently dropped if their source address is such that the host's forwarding table would not route a packet with that destination address out the same interface on which the packet was received. According to RFC 1812, if implemented, routers should enable this feature by default. This form of address validation is often not enabled on routers, so these commands disable it: # Drop Spoofed Packets coming in on an interface, which if replied to, # would result in the reply going out a different interface. for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 1 > $f done
log_martians logs packets received with impossible addresses, as defined in RFC 1812, Section 5.3.7. Impossible source addresses include multicast or broadcast addresses, addresses in the 0 and 127 networks, and the Class E reserved space. Impossible destination addresses include address 0.0.0.0, host 0 on any network, any host on the 127 network, and Class E addresses. Currently, the Linux network code checks for the previously mentioned addresses. It does not check for private class addresses (nor could it do so without knowledge of the network a given interface was connected to). log_martians does not affect packet validity checking; it merely affects logging, which is set here: # Log packets with impossible addresses. for f in /proc/sys/net/ipv4/conf/*/log_martians; do echo 1 > $f done
Removing Any Preexisting Rules
The first thing to do when defining a set of filtering rules is to remove any existing rules from their chains. Otherwise, any new rules that you define will be added to the end of existing rules. Packets could easily match a preexisting rule before reaching the point in the chain that you are defining from this point on. Removal is called flushing the chain. Without an argument referring to a specific chain, the following command flushes all rules from all chains at once: # Remove any existing rules from all chains $IPT --flush $IPT -t nat --flush $IPT -t mangle --flush The chains are empty, but any user-defined chains still exist. Flushing the chains does not affect the default policy state currently in effect. The next step would be to delete any user-defined chains. They can be deleted with the following commands: $IPT -X $IPT -t nat -X $IPT -t mangle -X
Resetting Default Policies and Stopping the Firewall
So far, the firewall has set some defaults that can be used regardless of the state of the Netfilter firewall. Before setting the default policies to DROP, I'll first reset the default policies to ACCEPT. This is useful for stopping the firewall completely, as you'll see shortly. These lines set the default policy: # Reset the default policy $IPT --policy INPUT ACCEPT $IPT --policy OUTPUT ACCEPT $IPT --policy FORWARD ACCEPT $IPT -t nat --policy PREROUTING ACCEPT $IPT -t nat --policy OUTPUT ACCEPT $IPT -t nat --policy POSTROUTING ACCEPT $IPT -t mangle --policy PREROUTING ACCEPT $IPT -t mangle --policy OUTPUT ACCEPT
Here's a final addition to what I term to be the beginning of the firewall script, namely the code to enable the firewall to be stopped easily. With this code placed below the previous code, when you call the script with an argument of "stop" the script will flush, clear, and reset the default policies and the firewall will effectively stop: if [ "$1" = "stop" ] then echo "Firewall completely stopped! WARNING: THIS HOST HAS NO FIREWALL RUNNING." exit 0 fi Enabling the loopback Interface
You need to enable unrestricted loopback traffic. This enables you to run any local network-based services that you chooseor that the system depends onwithout having to worry about getting all the firewall rules specified. Local services rely on the loopback network interface. After the system boots, the system's default policy is to accept all packets. Flushing any preexisting chains has no effect. However, if the firewall is being reinitialized and had previously used a deny-by-default policy, the drop policy would still be in effect. Without any acceptance firewall rules, the loopback interface would still be inaccessible. Because the loopback interface is a local, internal interface, the firewall can allow loopback traffic immediately: # Unlimited traffic on the loopback interface $IPT -A INPUT -i lo -j ACCEPT $IPT -A OUTPUT -o lo -j ACCEPT Defining the Default Policy
By default, you want the firewall to drop everything. The two available options for the built-in chains are ACCEPT and DROP. REJECT is not a legal policy in iptables. User-defined chains cannot be assigned default policies. Using a default policy of DROP, unless a rule is defined to either explicitly allow or reject a matching packet, packets are silently dropped. What you more likely want is to silently drop unwanted incoming packets, but to reject outgoing packets and return an ICMP error message to the local sender. The difference for the end user is that, for example, if someone at a remote site attempts to connect to your web server, that person's browser hangs until his system returns a TCP timeout condition. There is no indication whether your site or your web server exists. On the other hand, if you attempt to connect to a remote web server, your browser receives an immediate error condition indicating that the operation isn't allowed: # Set the default policy to drop $IPT --policy INPUT DROP $IPT --policy OUTPUT DROP $IPT --policy FORWARD DROP
At this point, all network traffic other than local loopback traffic is blocked. This firewall host has only one network interface. The FORWARD policy isn't necessary. Defining the FORWARD policy is a precaution for the future. For that reason, the following definitions should be added as well: $IPT -t nat --policy PREROUTING DROP $IPT -t nat --policy OUTPUT DROP $IPT -t nat --policy POSTROUTING DROP $IPT -t mangle --policy PREROUTING DROP $IPT -t mangle --policy OUTPUT DROP
Stealth Scans and TCP State Flags
Testing for common forms of TCP stealth scans is possible because iptables gives access to all the TCP state flags. The following rules block common stealth scan probes. None of the TCP state combinations tested for are legal combinations. In addition, the unclean match is used first in order to match packets with bad headers and other problems. This module has been experimental for a while, so use with caution. Should you see an error when attempting to load this module, it may not be available with your kernel version. In such an event, comment out the unclean match line by using a single pound sign "#" in front of the line loading the unclean module. To reiterate statements made in Chapter 3, the first list of state flags lists the bits to be tested. Out of those bits, the second list of state flags lists the bits that must be set to match the test: # Unclean $IPT -A INPUT -m unclean -j DROP # All of the bits are cleared $IPT -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # SYN and FIN are both set $IPT -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP # SYN and RST are both set $IPT -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP # FIN and RST are both set $IPT -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP # FIN is the only bit set, without the expected accompanying ACK $IPT -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP # PSH is the only bit set, without the expected accompanying ACK $IPT -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j DROP # URG is the only bit set, without the expected accompanying ACK $IPT -A INPUT -p tcp --tcp-flags ACK,URG URG -j DROP
The ACK,FIN FIN test implicitly includes xmastree packets that contain FIN-PSH, FIN-URG, and FIN-PSH-URG as well. Using Connection State to Bypass Rule Checking
Specifying the state match for previously initiated and accepted exchanges enables you to bypass the firewall tests for the ongoing exchange. The initial client request remains controlled by the service's specific filters, however. Notice that both the INPUT and the OUTPUT filters are necessary to bypass the rules in both directions. A connection isn't treated as a two-way exchange by the state module, and a symmetric dynamic rule is not generated. Because the state module can require more RAM than some Linux firewall machines have, the firewall example developed in this chapter provides the rules for both alternatives, with and without the state module. Remember that use of the state module allows you to bypass the standard firewall rules for ongoing exchanges under normal operation. The standard rules must be included, however, if the state table entry for a connection is recycled or times out.
Source Address Spoofing and Other Bad Addresses
This section establishes some INPUT chain filters based on source and destination addresses. These addresses will never be seen in a legitimate incoming packet from the Internet. At the packet-filtering level, one of the few cases of source address spoofing that you can identify with certainty as a forgery is your own IP address. This rule drops incoming packets claiming to be from you: # Refuse spoofed packets pretending to be from # the external interface's IP address $IPT -A INPUT -i $INTERNET -s $IPADDR -j DROP
There is no need to block outgoing packets destined to yourself. They won't return, claiming to be from you and appearing to be spoofed. Remember, if you send packets to your own external interface, those packets arrive on the loopback interface's input queue, not on the external interface's input queue. Packets containing your address as the source address never arrive on the external interface, even if you send packets to the external interface.
As explained in Chapter 1 and Chapter 2, private IP addresses are set aside in each of the Class A, B, and C address ranges for use in private LANs. They are not intended for use on the Internet. Routers are not supposed to route packets with private source addresses. Nevertheless, some routers do forward packets containing private source addresses. Additionally, if someone on your ISP's subnet (that is, on your side of the router that you share) is leaking packets with private IP addresses, you'll see them even if the router doesn't forward them. Machines on your own LAN could also leak private addresses if your NAT or proxy configuration is set up incorrectly. The next three sets of rules disallow incoming packets containing source addresses from any of the Class A, B, or C private network addresses. None of these packets should be seen on a public network: # Refuse packets claiming to be from a Class A private network $IPT -A INPUT -i $INTERNET -s $CLASS_A -j DROP # Refuse packets claiming to be from a Class B private network $IPT -A INPUT -i $INTERNET -s $CLASS_B -j DROP # Refuse packets claiming to be from a Class C private network $IPT -A INPUT -i $INTERNET -s $CLASS_C -j DROP The next rule disallows packets with a source address in the loopback network: # Refuse packets claiming to be from the loopback interface $IPT -A INPUT -i $INTERNET -s $LOOPBACK -j DROP
Because loopback addresses are assigned to an internal, local software interface, any packet claiming to be from such an address is intentionally forged. As with addresses set aside for use in private LANs, routers are not supposed to forward packets originating from the loopback address range. A router cannot forward a packet with a loopback destination address. The next two rules primarily serve to log matching packets. The firewall's default policy is to deny everything. As such, broadcast addresses are dropped by default and must be explicitly enabled if they are wanted: # Refuse malformed broadcast packets $IPT -A INPUT -i $INTERNET -s $BROADCAST_DEST -j LOG $IPT -A INPUT -i $INTERNET -s $BROADCAST_DEST -j DROP $IPT -A INPUT -i $INTERNET -d $BROADCAST_SRC -j LOG $IPT -A INPUT -i $INTERNET -d $BROADCAST_SRC -j DROP
The first pair of rules logs and denies any packet claiming to come from 255.255.255.255, the address reserved as the broadcast destination address. A packet will never legitimately originate from address 255.255.255.255. The second pair of rules logs and denies any packet directed to destination address 0.0.0.0, the address reserved as a broadcast source address. Such a packet is not a mistake; it is a specific probe intended to identify a UNIX machine running network software derived from BSD. Because most UNIX operating system network code is derived from BSD, this probe is effectively intended to identify machines running UNIX.
The next two rules block two forms of directed broadcasts: # Refuse directed broadcasts # Used to map networks and in Denial of Service attacks $IPT -A INPUT -i $INTERNET -d $SUBNET_BASE -j DROP $IPT -A INPUT -i $INTERNET -d $SUBNET_BROADCAST -j DROP With the deny-by-default policy and the firewall rules explicitly accepting packets based in part by matching on destination address, neither of these directed broadcast messages will be accepted by the firewall. These rules become more critical in larger setups in which the LAN uses real-world addresses. With the use of variable-length network prefixes, a site's network and host fields may or may not fall on a byte boundary. For the sake of simplicity, the SUBNET_BASE is your network address, such as 192.168.1.0. The SUBNET_BROADCAST is your network's broadcast address, as in 192.168.1.255. Just as with directed broadcast messages, limited broadcasts, confined to your local network segment, are likewise not accepted with the deny-by-default policy and the firewall rules explicitly accepting packets based in part by matching on destination address. Again, the following rule becomes more critical in larger setups in which the LAN uses real-world addresses: # Refuse limited broadcasts $IPT -A INPUT -i $INTERNET -d $BROADCAST_DEST -j DROP
It should be noted that an exception must be made in later chapters for DHCP clients. Broadcast source and destination addresses are used between the DHCP client and server ports initially. Multicast addresses are legal only as destination addresses. The next rule drops spoofed multicast network packets: # Refuse Class D multicast addresses # illegal as a source address $IPT -A INPUT -i $INTERNET -s $CLASS_D_MULTICAST -j DROP
Legitimate multicast packets are always UDP packets. As such, multicast messages are sent point-to-point, just as any other UDP message is. The difference between unicast and multicast packets is the class of destination address used (and the protocol flag carried in the Ethernet header). The next rule denies multicast packets carrying a non-UDP protocol: $IPT -A INPUT -i $INTERNET -p ! udp -d $CLASS_D_MULTICAST -j DROP
Multicast functionality is a configurable option when you compile the kernel, and your network interface card can be initialized to recognize multicast addresses. The functionality is enabled by default in the default kernel from many newer distributions of Linux. You might want to enable these addresses if you subscribe to a network conferencing service that provides multicast audio and video broadcasts. (Multicast is also sometimes used on the local network for global resource discovery, such as with DHCP or routing.) You won't generally see multicast destination addresses unless you've registered yourself as a recipient. Multicast packets are sent to multiple, but specific, targets by prior arrangement. I have seen multicast packets sent out from machines on my ISP's local subnet, however. The default policy drops multicast packets, even if you have registered as a recipient. You have to define a rule to accept the multicast address. The next rule allows incoming multicast packets for the sake of completeness: $IPT -A INPUT -i $INTERNET -p udp -d $CLASS_D_MULTICAST -j ACCEPT Multicast registration and routing is a complicated process managed by its own IP layer-control protocol, the Internet Group Management Protocol (IGMP, protocol 2). For more information on multicast communication, refer to the "Multicast over TCP/IP HOWTO" at http://www.tldp.org/HOWTO/Multicast-HOWTO.html. Additional resources include RFC 1458, "Requirements for Multicast Protocols"; RFC 1112, "Host Extensions for IP Multicasting" (updated by RFC 2236, "Internet Group Management Protocol Version 2"); and RFC 2588, "IP Multicast and Firewalls." Class D IP addresses range from 224.0.0.0 to 239.255.255.255. The CLASS_D_MULTICAST constant, 224.0.0.0/4, is defined to match on the first 4 bits of the address. As shown in Figure 4.2, in binary, the decimal values 224 (11100000B) to 239 (11101111B) are identical through the first 4 bits (1110B). Figure 4.2. The matching first 4 bits in the masked Class D multicast address range 224.0.0.0/4.
The next rule in this section drops packets claiming to be from a Class E reserved network: # Refuse Class E reserved IP addresses $IPT -A INPUT -i $INTERNET -s $CLASS_E_RESERVED_NET -j DROP
Class E IP addresses range from 240.0.0.0 to 247.255.255.255. The CLASS_E_RESERVED_NET constant, 240.0.0.0/5, is defined to match on the first 5 bits of the address. As shown in Figure 4.3, in binary, the decimal values 240 (11110000B) to 247 (11110111B) are identical through the first 5 bits (11110B). Figure 4.3. The matching first 5 bits in the masked Class E reserved address range 240.0.0.0/5.
The IANA ultimately manages the allocation and registration of the world's IP address space. For more information on IP address assignments, see http://www.iana.org/_assignments/ipv4-address-space. Some blocks of addresses are defined as reserved by the IANA. These addresses should not appear on the public Internet. Out of the entire set of the IANA's reserved address blocks, the 0.0.0.0/8, Link Local Network, and Test Net spaces are blocks that will not be assigned as public addresses. The remaining reserved blocks are not routable until assigned. In the meantime, they can be used as source addresses in denial-of-service attacks. The firewall could block the remaining reserved blocks as source addresses, but it turns out to be impractical for most sites because the IANA has been actively allocating these blocks since year 2000. The following lines drop packets from those sources: # Refuse addresses defined as reserved by the IANA # 0.*.*.* - Can't be blocked unilaterally with DHCP # 169.254.0.0/16 - Link Local Networks # 192.0.2.0/24 - TEST-NET $IPT -A INPUT -i $INTERNET -s 0.0.0.0/8 -j DROP $IPT -A INPUT -i $INTERNET -s 169.254.0.0/16 -j DROP $IPT -A INPUT -i $INTERNET -s 192.0.2.0/24 -j DROP
|
Категории