building a powerful and statefull firewall under bsd to protect an enterprise network from evil...
TRANSCRIPT
Building a powerful and statefull firewall under BSD to protect an
enterprise network from evil hackers under drugs effects.
Facundo M. de la Cruz (tty0)[email protected]
"Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning."
What is a firewall?
Don't play with Penguins, play with Daemons!.
What about Windows?
What about Linux?
➔ Linux has Netfilter, and in the userland it's iptables.
➔ But iptables is ugly and difficult.
➔ Iptables dont know about states (except with conntrack).
➔ So ugly (yes, again).
What about Cisco?
What about Cisco (chapter II)?
and AIX, HP-UX or Solaris?
➔ AIX, HP-SUX and Solaris has IP Filter (from now IPF).
➔ IPF is a statefull firewall (yes, this feature rocks!).
➔ IPF is the grandfather of PF.
➔ IPF has a BSD license and is FREE SOFTWARE.
Others commercial solutions
Firewall performance
✗ CPU speed.✗ Data bus speed.✗ Network Cards speed.✗ Ruleset.
Sent 256 bytes packets with 100 rules. Non-statefull.PF degradation is worst than iptables.
Firewall performance
Firewall performance
Sent 256 bytes packets with 100 rules. Statefull.PF performance is better than iptables and IPF.
➔ Anchors
➔ AuthPF
➔ Built in network Traffic Shapper (ALTQ).
➔ CARP + pfsync failover and load balancing.
➔ NAT and routing.
➔ Passive OS Fingerprints.
➔ Packet tagging.
➔ Statefull by default.
➔ SYN proxy.
➔ Tables (for fast IP address lookup).
➔ TCP normalization (scrub, modulate)
PF features
IPTables Syntax example
A INPUT p tcp m state state NEW m tcp dport 3389 j ACCEPT A INPUT p tcp m state state NEW m tcp dport 25 j ACCEPT A INPUT p tcp m state state NEW m tcp dport 143 j ACCEPT A INPUT p tcp m state state NEW m tcp dport 21 j ACCEPT A INPUT p tcp m state state NEW m tcp dport 2049 j ACCEPT A INPUT p tcp m state state NEW m tcp dport 22 j ACCEPT A INPUT p tcp m state state NEW m tcp dport 50 j ACCEPT A INPUT p tcp m state state NEW m tcp dport 51 j ACCEPT A INPUT p udp m state state NEW m udp dport 137 j ACCEPT
PF Configuration file
The PF configuration file is /etc/pf.conf and has five parts:
➔ Macros: User defined variables.
➔ Tables: A structure to hold list of IP address
➔ Options: Various options to control PF
➔ Queueing: Provides bandwitch control
➔ Filter Rules: Allow the selective filtering
PF Syntax example
# Network interfacesint_if = “xl0”ext_if = “xl1”
# Table containing all IP addresses assigned to the firewalltable <firewall> const { self }
# Default policy for inbound packets.block in all
# Allowed ports from the WANpass in quick on $ext_if proto { tcp, udp } from any to <firewall> \
port {21, 22, 25, 50, 51, 137, 143, 2049, 3389} \flags S/SA keep state
PF Syntax example
# Network interfacesint_if = “xl0”ext_if = “xl1”
allowed_ports = { 21, 22, 25, 50, 51, 137, 143, 2049, 3389 }
# Table containing all IP addresses assigned to the firewalltable <firewall> const { self }
# Default policy for inbound packets.block in all
# Allowed ports from the WANpass in quick on $ext_if proto { tcp, udp } from any to <firewall> \
$allowed_ports flags S/SA keep state
PF Syntax example
● table <abusive_hosts> persistblock in quick from <abusive_hosts>
pass in on $ext_if proto tcp to $http_server port www flags S/SA \ keep state (maxsrcconn 100, maxsrcconnrate 15/5 \
overload <abusive_hosts> flush
PF Syntax example
● table <abusive_hosts> persistblock in quick from <abusive_hosts>
pass in on $ext_if proto tcp to $http_server port www flags S/SA \ keep state (maxsrcconn 100, maxsrcconnrate 15/5 \
overload <abusive_hosts> flush
Statefull by default
➔ TCP States➔ UDP States➔ ICMP States
Statefull by default
➔ keep state.➔ modulate state (only works with TCP).➔ no state.
Network Address Translation
➔ Directional mapping
➔ Bidirectional mapping (1:1 mapping)
➔ Port forwarding
web_serv_int = "192.168.1.100"web_serv_ext = "24.5.0.6"pass on tl0 from $web_serv_int to any binatto $web_serv_ext
pass out on tl0 from 192.168.1.0/24 to any natto 24.2.74.79pass out on tl0 from 192.168.1.208 to any
pass in on tl0 proto tcp from any to any port 80 \rdrto 192.168.1.20 port 8080
TCP Proxying
A generic TCP proxy can be setup on the firewall, either listening on the port to be forwarded or getting connections on the internal interface redirected to the port it's listening on. When a local client connects to the firewall, the proxy accepts the connection, establishes a second connection to the internal server, and forwards data between those two connections.
127.0.0.1:5000 stream tcp nowait proxy /usr/bin/nc nc w \ 20 192.168.1.10 80
pass in on $int_if proto tcp from $int_net to $ext_if \ port 80 rdrto 127.0.0.1 port 5000
Queues
PF supports two schedulers (Yeah! this rocks)
➔ Class Based Queueing (CBQ)➔ Priority Queueing
queue std bandwidth 50% cbq(default)queue ssh bandwidth 25% { ssh_login, ssh_bulk } queue ssh_login bandwidth 25% priority 4 cbq(ecn) queue ssh_bulk bandwidth 75% cbq(ecn)queue ftp bandwidth 500Kb priority 3 cbq(borrow red)
pass out on fxp0 proto tcp to port 22 queue ssh
Tagging
Packet tagging is a way of marking packets with an internal identifier that can later be used in filter and translation rule criteria. With tagging, it's possible to do such things as create "trusts" between interfaces and determine if packets have been processed by translation rules. It's also possible to move away from rule-based filtering and to start doing policy-based filtering.
pass in on $int_if from 172.16.8.0/24 to any tag WIFI_NET pass out on $ext_if ! tagged WIFI_NET
OS Fingerprinting
$ext_if = "em0"
$webwin="10.0.0.10"$weblinux="10.0.0.20"
pass in on $ext_if proto tcp from any os "Windows" to any port 80\ modulate state flags S/SA rdrto $webwin port 8081
pass in on $ext_if proto tcp from any os "Linux 2.6" to any port 80\modulate state flags S/SA rdrto $weblinux port 8081
You can make some filter rules based in the operative system type and version.
Why did I started with PF?
● I has a happy network gateway under FreeBSD 7.2 i386.● It was using IPF and IPNAT. ● The network gateway has 2 NIC (xl0 and xl1).● Was running Sendmail as SMTP server.
And they were very happy, until one day ...
The system crashed
Fatal trap 12: page fault while in kernel mode
cpuid = 0; apic id = 00
fault virtual address = 0x4
fault code = supervisor read, page not present
instruction pointer = 0x20:0xc33ac8ab
stack pointer = 0x28:0xc2f909bc
frame pointer = 0x28:0xc2f90a38
code segment = base 0x0, limit 0xfffff, type 0x1b = DPL 0, pres 1, def32 1, gran 1
processor eflags = interrupt enabled, resume, IOPL = 0
current process = 21 (irq17: xl1)
trap number = 12
panic: page fault
cpuid = 0
Uptime: 11h41m30s
Physical memory: 499 MB
Dumping 83 MB: 68 52 36 20 4
#0 doadump () at pcpu.h:196
196 __asm __volatile("movl %%fs:0,%0" : "=r" (td));
Coredump
# ifconfig xl1 xl1: flags=8843 metric 0 mtu 1500options=9ether 00:10:4b:c6:65:3binet 190.122.08.91 netmask 0xfffffff8 broadcast 190.122.08.124media: Ethernet autoselect (100baseTX )status: active#
xl1 NIC
(kgdb) bt
#0 doadump () at pcpu.h:196#1 0xc07ec1f7 in boot (howto=260) at /usr/src/sys/kern/kern_shutdown.c:418#2 0xc07ec4c9 in panic (fmt=Variable "fmt" is not available) at /usr/src/sys/kern/kern_shutdown.c:574#3 0xc0b18f2c in trap_fatal (frame=0xc2f9097c, eva=4) at /usr/src/sys/i386/i386/trap.c:939#4 0xc0b191b0 in trap_pfault (frame=0xc2f9097c, usermode=0, eva=4) at /usr/src/sys/i386/i386/trap.c:852#5 0xc0b19c2c in trap (frame=0xc2f9097c) at /usr/src/sys/i386/i386/trap.c:530#6 0xc0afe20b in calltrap () at /usr/src/sys/i386/i386/exception.s:159#7 0xc33ac8ab in nat_new () from /boot/kernel/ipl.ko#8 0xc33b0574 in fr_checknatin () from /boot/kernel/ipl.ko#9 0xc33c9723 in fr_check () from /boot/kernel/ipl.ko#10 0xc33c170e in fr_check_wrapper () from /boot/kernel/ipl.ko#11 0xc0897418 in pfil_run_hooks (ph=0xc0d03100, mp=0xc2f90be8) at /usr/src/sys/net/pfil.c:78#12 0xc08d76e2 in ip_input (m=0xc32eaa00) at /usr/src/sys/netinet/ip_input.c:416#13 0xc0895bb5 in netisr_dispatch (num=2, m=0xc32eaa00) at /usr/src/sys/net/netisr.c:185#14 0xc088bb51 in ether_demux (ifp=0xc3191400, m=0xc32eaa00) at /usr/src/sys/net/if_ethersubr.c:834#15 0xc088bf43 in ether_input (ifp=0xc3191400, m=0xc32eaa00) at /usr/src/sys/net/if_ethersubr.c:692#16 0xc09ec818 in xl_rxeof (sc=0xc3199000) at /usr/src/sys/pci/if_xl.c:2022#17 0xc09eed24 in xl_intr (arg=0xc3199000) at /usr/src/sys/pci/if_xl.c:2257
Backtrace analysis
The root cause
The nat_new() function defined in /usr/src/sys/contrib/ipfilter/netfilter/ip_nat.c
What does nat_new()?➔ Create a new NAT struct for MAP rule in the inbound
connections.➔ Create a new NAT struct for the RDR fule in the
outbound connections.➔ Build this struct and put this rules in the NAT table.
About the nat_new() function
➔ np(I) -> Pointer to the NAT rule.➔ fin(I) -> Pointer to the packet information.➔ flags(i) -> Flags in the last packet.➔ direction(i) -> Direction (IN/OUT) for the
new_nat function to apply MAP or RDR.
What were in the /etc/ipnat.rules
/etc/ipnat.rules
bimap xl1 172.18.2.12/32 > 190.122.08.91/32bimap xl1 172.18.2.13/32 > 190.122.08.91/32bimap xl1 172.18.2.14/32 > 190.122.08.91/32bimap xl1 172.18.2.15/32 > 190.122.08.91/32
Testing the bug
-------BEGIN tcpfrag.pkt -------[out,xl1]
4500 00a0 0000 0100 3f06 7555 0101 0101 0201 01010401 0019 0000 0000 0000 0000 5010 2000 86b7 00000000 0000 0000 0000 0000 0000 0000 0000 0000 00000000 0000 0000 0000 0000 0000 0000 0000 0000 00000000 0000 0000 0000 0000 0000 0000 0000 0000 00000000 0000 0000 0000 0000 0000 0000 0000 0000 00000000 0000 0000 0000 0000 0000 0000 0000 0000 00000000 0000 0000 0000 0000 0000 0000 0000 0000 0000
-------EOF-------
#: ipftest -F hex -N tcpfrag.nat -i tcpfrag.pkt
Thank you for your attention.