Re: Strange ? keep state behaviour
On Fri, Jan 07, 2005 at 11:06:18AM -0500, Dave Anderson wrote: I hadn't realized it until I started thinking this through, but it appears that, if floating state is enabled, there's no way to prevent PF from passing *some* spoofed packets -- though this probably isn't a significant issue in practice. Perhaps the right way to deal with all of these issues is to provide more explicit control over floating state -- e.g., eliminate the current general floating state and add to each rule specifying keep state (or its relatives) an optional list of interfaces to which those states may float with a direction for each such interface. The state entries contain the direction of the connection precisely to prevent this kind of spoofing. Let's say I allow a connection from A to B in on interface I, by creating a state entry when the first packet of the connection (from A to B) arrives in on I. This state will only allow - incoming packets from A to B - outgoing packets from B to A but not - incoming packets from B to A - outgoing packets from A to B Let's say A is some external host and B is myself, so the firewall is an endpoint of the connection. You clearly don't want to allow any outgoing packets from A to B. Such packets would be spoofed (by the firewall). Neither do you want to allow incoming packets from B to A. Such packets would be spoofed by some external host. The second case is where both A and B are other hosts, i.e. the firewall is filtering a connection that merely passes through, and the firewall is not an endpoint of the connection. In this case, you'll have a second interface J, and see legitimate traffic - incoming packets from A to B on I - outgoing packets from A to B on J - incoming packets from B to A on J - outgoing packets from B to A on I Since a single state entry does not cover all four cases, you can either - filter statefully only on I, passing everything statelessly on J - filter statefully on both I and J, creating TWO state entries for each connection In the second case, you'll get two state entries, passing 1) on I, incoming from A to B, outgoing from B to A 2) on J, outgoing from A to B, incoming from B to A Whether these states are floating or if-bound is completely irrelevant as long as all packets only pass through interfaces I and J. In other words, if you make the first state above floating, that does not mean it will match packets outgoing from A to B or incoming from B to A. You still need a second state for those packets. That's what's surprising to some users. If you make the first state 1) on I, incoming from A to B, outgoing from B to A floating, that merely makes it ignore the interface, becoming 1) on any interface, incoming from A to B, outgoing from B to A Packets outgoing from A to B (or incoming from B to A) on any interface still do not match this state. A floating state is not a 'pass-through' permission for a connection, in the sense that you allow a connection to pass through your firewall on any path possible. You always need a second state for that (unless you pass packets on some interfaces statelessly). The purpose of floating states is setups where packets of one connection may legitimately flow through more than one pair of interfaces, like when you have dynamic routing. For instance, you might have two uplink interfaces I1 and I2. If a connection comes in on I1 (you create a state for a packet from A to B incoming on I1), you might want to allow - incoming packets from A to B - outgoing packets from B to A on I1 and I2, so when one uplink dies (or your routing table changes), the connection initiated through I1 continues to work through I2. Hence, you'd make the state floating (instead of if-bound to I1). Instead of binding a state to a single interface or floating among all interfaces, you can also bind it to a group of interfaces (group-bound), like make I1 and I2 an interface group and bind the state to that group only. But all of this is orthogonal to the fact that you still need a second state if the connection is forwarded THROUGH your firewall and you want to allow that. One state (even if floating) will only cover one side (which might include multiple interfaces), the side facing the first peer of the connection. If you chose to allow the connection to get forwarded to a second peer, you create a second state for that side of the connection (again, possibly through multiple interfaces). This might sound overly complicated. You might want to create a single state entry that allows passage of the connection THROUGH the firewall, not caring for the two sides facing the two peers. But if such a construct would exists, it would suffer precisely the vulnerability against some spoofed packets you mentioned. Because it doesn't exist, the two states can deal with that case. HTH, Daniel
Re: Strange ? keep state behaviour
On Fri, Jan 07, 2005 at 11:06:18AM -0500, Dave Anderson wrote: I hadn't realized it until I started thinking this through, but it appears that, if floating state is enabled, there's no way to prevent PF from passing *some* spoofed packets -- though this probably isn't a significant issue in practice. Perhaps the right way to deal with all of these issues is to provide more explicit control over floating state -- e.g., eliminate the current general floating state and add to each rule specifying keep state (or its relatives) an optional list of interfaces to which those states may float with a direction for each such interface. Adding to the previous explanation, if you know that one side of the connection should pass only through one specific interface (like a single uplink interface), you should use an if-bound state for that side. There's no reason to use a floating state in that case. If you're using a floating state (maybe just because it's the default, and you didn't realize the difference), there is one form of spoofing that becomes possible: The state entry says: on any interface, pass incoming from A to B, outgoing from B to A Such packets should only occur on interface I, but you made the state floating (instead of if-bound to I), so theoretically, someone might inject a packet on another interface (not I), like incoming from A to B on J outgoing from B to A on K but NOT incoming from B to A on L outgoing from A to B on K because the state will only match the former two, but not the latter two. Now, to successfully match a state entry, an attacker needs to know (or guess) more than just the source and destination addresses. For TCP, he needs to know both ports and, more importantly, the current sequence numbers. We rely on the unguessability of sequence numbers, only the endpoints of a connection (and the few legitimate hops along the path between them) know these secrets. Obviously, if an attack were able to guess the secrets, he could attack your connections in a number of ways, anyway. So, try to think of a case where an attacker gains any advantage by being able to pass a packet incoming from A to B on J outgoing from B to A on K where he knows the connection secrets and couldn't just as well pass the packet on I (where it would pass, anyway). I guess it would have to be some very specific case where, for some reason, the attacker can only inject packets on interfaces other than I. I haven't seen a single example where this would matter in practice. But, yes, if worry about this, make your state if-bound to I, and this becomes a non-issue. My guess is that we might eventually switch to if-bound as default. I'm in favor of changing all examples in documentation to use it explicitely until then. But if anyone is claiming that the default of floating states leads to holes in rulesets of people unaware of the issue, I'd like to see an practical example attack. Daniel
Re: Strange ? keep state behaviour
On Thursday, Jan 6, 2005, at 16:21 US/Pacific, Jason Murray wrote: If I understand things properly when the packet comes in on $ext_if it creates the state. Because the state is floating it should be picked up when the packet tries to go out on $uat_if. Since it is in the state table it should pass no problem. Though the state is floating with respect to interfaces, the _direction_ is still locked. It sees that out.side.add.ress sent a packet IN to in.side.web.server, and so will only match future packets with that address pair if they are also coming IN. (And packets OUT for the reverse address pair, of course.) The floating part just means it doesn't care what interface that happens on. This particular point seems to have gotten lost in the documentation since the if- and group-bound options were introduced.
Re: Strange ? keep state behaviour
** Reply to message from Trevor Talbot [EMAIL PROTECTED] on Fri, 7 Jan 2005 04:35:36 -0800 On Thursday, Jan 6, 2005, at 16:21 US/Pacific, Jason Murray wrote: If I understand things properly when the packet comes in on $ext_if it creates the state. Because the state is floating it should be picked up when the packet tries to go out on $uat_if. Since it is in the state table it should pass no problem. Though the state is floating with respect to interfaces, the _direction_ is still locked. It sees that out.side.add.ress sent a packet IN to in.side.web.server, and so will only match future packets with that address pair if they are also coming IN. (And packets OUT for the reverse address pair, of course.) The floating part just means it doesn't care what interface that happens on. This particular point seems to have gotten lost in the documentation since the if- and group-bound options were introduced. I've no knowledge of PF's internals and am not a firewall guru, but this seems strange to me -- I'd have expected state floated to another interface to lock in the *reverse* direction since (at least in what I'd think of as the common cases) packets which are inbound on one interface will be outbound on any other interface they traverse (and vice versa). I suppose that in cases where, for redundancy, multiple interfaces operate more or less in parallel locking in the same direction makes sense; this seems to me like a *much* less common setup, though if packets for the same state could traverse any of those interfaces a floating state with the same direction might be the only way to properly configure the firewall. Hmmm. For a firewall properly protected against IP address spoofing, there's only one possible direction on each interface for an allowed packet with any given source (or destination) IP address. Perhaps states shouldn't include direction information at all? But this would require that anti-spoofing rules be processed *before* state lookup which would require major changes to PF and might significantly slow down processing. I hadn't realized it until I started thinking this through, but it appears that, if floating state is enabled, there's no way to prevent PF from passing *some* spoofed packets -- though this probably isn't a significant issue in practice. Perhaps the right way to deal with all of these issues is to provide more explicit control over floating state -- e.g., eliminate the current general floating state and add to each rule specifying keep state (or its relatives) an optional list of interfaces to which those states may float with a direction for each such interface. If this doesn't make sense, I'd appreciate a brief explanation (or pointer to one) of what false assumptions or inferences I've made and why they're false. Dave -- Dave Anderson [EMAIL PROTECTED]
Strange ? keep state behaviour
Hello new to the list, but not exactly new to pf. I've got a 3 interface firewall and I'm seeing what I would call strange behaviour. Here is the scenario. I want to allow http in from the Internet to a web server on an isolated segment. I have a rdr rule set up and it works just fine (traffic flows when no filtering is being done). If I have a rule set like the following: block log all antispoof quick for { lo0 $uat_if $dev_if } # Allow web traffic to the UAT (marlin) box. pass in log quick on $ext_if proto tcp from any to $marlin port { 80, 443 } flags S/SA keep state Traffic does not flow. I get the following in the logs: Jan 06 12:11:56.324068 rule 13/0(match): pass in on ext_if: out.side.add.ress.61005 in.side.web.server.80: S 3708921981:3708921981(0) win 16384 mss 1460,nop,nop,sackOK (DF) Jan 06 12:11:56.324104 rule 0/0(match): block out on uat_if: out.side.add.ress.61005 in.side.web.server.80: S 3708921981:3708921981(0) win 16384 mss 1460,nop,nop,sackOK (DF) Jan 06 12:11:59.353276 rule 0/0(match): block out on uat_if: out.side.add.ress.61005 in.side.web.server.80: S 3708921981:3708921981(0) win 16384 mss 1460,nop,nop,sackOK (DF) Jan 06 12:12:05.361189 rule 0/0(match): block out on uat_if: out.side.add.ress.61005 in.side.web.server.80: S 3708921981:3708921981(0) win 16384 mss 1460,nop,nop,sackOK (DF) This does not make any sense to me. AFAIK (from reading man pf.conf numerous times) the keep state should allow the traffic to pass once the pass in on $ext_if... rule is matched. Regardless, it does not work. To get it to work I have to add the following: pass out log quick on $uat_if proto tcp from any to $marlin port {80, 443 } flags S/SA keep state Then my logs show: Jan 06 12:19:32.244139 rule 13/0(match): pass in on ext_if: out.side.add.ress.56709 in.side.web.server.80: S 457881634:457881634(0) win 16384 mss 1460,nop,nop,sackOK (DF) Jan 06 12:19:32.244176 rule 15/0(match): pass out on uat_if: out.side.add.ress.56709 in.side.web.server.80: S 457881634:457881634(0) win 16384 mss 1460,nop,nop,sackOK (DF) Jan 06 12:19:32.567937 rule 13/0(match): pass in on ext_if: out.side.add.ress.63950 in.side.web.server.80: S 3954645361:3954645361(0) win 16384 mss 1460,nop,nop,sackOK (DF) Jan 06 12:19:32.567955 rule 15/0(match): pass out on uat_if: out.side.add.ress.63950 in.side.web.server.80: S 3954645361:3954645361(0) win 16384 mss 1460,nop,nop,sackOK (DF) And it works and all is better. Except for the fact that this is not the behaviour I expect from my reading of the docs. Can anyone shed any light on this for me? Thanks in advance.
Re: Strange ? keep state behaviour
I'm cc'ing the pf list so that the whole thread is archived. I've included the whole pf.conf file. There isn't much more than what you already have. Your suggestion does work, but it weakens the rule set. Instead of a default deny stance, I have a default deny inbound on the external interface. It also doesn't provide any clue as to why keep state isn't carrying across the interfaces. Sven wrote: snip On Thu, 06 Jan 2005 16:48:50 -0500, Jason Murray [EMAIL PROTECTED] wrote: Without seeing the rest of your ruleset (hint) I can't say for sure but does it work if you change block log all to block log all on $ext_if ? # macros uat_if = rl0 # UAT dev_if = xl0 # DEV ext_if = fxp0 tcp_services = { 22 } icmp_types = echoreq priv_nets = { 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 } marlin = 10.245.245.130 simba = 10.245.245.2 # options set block-policy return set loginterface $ext_if set state-policy floating # scrub scrub in all # nat/rdr nat on $ext_if from $uat_if:network to any - ($ext_if) nat on $ext_if from $dev_if:network to any - ($ext_if) #rdr on $uat_if proto tcp from any to any port 21 - 127.0.0.1 port 8021 #rdr on $dev_if proto tcp from any to any port 21 - 127.0.0.1 port 8021 rdr on $ext_if proto tcp from any to any port 80 - $marlin rdr on $ext_if proto tcp from any to any port 443 - $marlin # filter rules # Default policy is to block traffic if it is not specifically allowed block log all antispoof quick for { lo0 $uat_if $dev_if } # Traffic is allowed from Simba and Marlin pass in log quick on $dev_if from any to $marlin keep state # Traffic is not allowed from Marlin to Simba block in log quick on $uat_if from any to $simba # ssh vpn stuff # Allow inbound ssh from the Internet only to the external interface. pass in quick on $ext_if inet proto tcp from any to ($ext_if) port { 22 } flags S/SA keep state # Allow traffic from the loopback to pass. pass log quick on lo0 all keep state # Allow web traffic to the UAT (marlin) box. pass in log quick on $ext_if proto tcp from any to $marlin port { 80, 443 } flag s S/SA keep state # this rule should not be need since the keep state above should obviate it, # however I could not get the traffic to pass unless it was there. pass out log quick on $uat_if proto tcp from any to $marlin port {80, 443 } flag s S/SA keep state # Allow ping from any box to pass. pass in log quick inet proto icmp all icmp-type $icmp_types keep state
Re: Strange ? keep state behaviour
On Thu, 2005-01-06 at 16:48, Jason Murray wrote: Hello new to the list, but not exactly new to pf. I've got a 3 interface firewall and I'm seeing what I would call strange behaviour. Here is the scenario. I want to allow http in from the Internet to a web server on an isolated segment. I have a rdr rule set up and it works just fine (traffic flows when no filtering is being done). If I have a rule set like the following: block log all antispoof quick for { lo0 $uat_if $dev_if } # Allow web traffic to the UAT (marlin) box. pass in log quick on $ext_if proto tcp from any to $marlin port { 80, 443 } flags S/SA keep state snip i *really* hope someone will smack me if i'm off-base here, because i'm not sure i'm 100% clear on this...BUT...as *i* understand it, as soon as you use on $if in a rule--the state that is created is if-bound even if your state-policy is floating. so you either (a) create 2 rules, one pass in for the inbound interface and one pass out for the outbound interface, (b) create strict rules on the inbound interface and a single lax rule on the outbound interface or (c) don't use the on $if construct in your rules. personally--i use (b) for internal-external rules and (a) for external-internal rules. i always assumed that if i needed to build a pf.conf to support an enormous number of states--i would use (c). -j -- I'm having the best day of my life, and I owe it all to not going to Church! --The Simpsons
Re: Strange ? keep state behaviour
Sven wrote: On Thu, 06 Jan 2005 18:06:48 -0500, Jason Murray [EMAIL PROTECTED] wrote: I'm cc'ing the pf list so that the whole thread is archived. I've included the whole pf.conf file. There isn't much more than what you already have. Your suggestion does work, but it weakens the rule set. Instead of a default deny stance, I have a default deny inbound on the external interface. It also doesn't provide any clue as to why keep state isn't carrying across the interfaces. Because you block all traffic on $uat_if snip I think you misunderstand keep state. From man pf.conf: If a packet matches a pass ... keep state rule, the filter creates a state for this connection and automatically lets pass all subsequent packets of that connection. The emphasis is on subsequent. In your case the first packet, the one that's supposed to create the state, is blocked on $uat_if because of the block log all rule. I must have read that statement a dozen times. If I understand things properly when the packet comes in on $ext_if it creates the state. Because the state is floating it should be picked up when the packet tries to go out on $uat_if. Since it is in the state table it should pass no problem. To have to create a keep state rule on each interface a packet traverses seems counter intuitive to the way it is described. But, that does seem to be what is needed. This suggests that if I do: pass quick log from any to $marlin flags S/SA keep state It should also solve my problems. I'll try this tomorrow and see what results I get. The rule you added is a good solution to your problem. However, the implication is that if you want to be highly granular with your ruleset, then you will have to add more rules.
Re: Strange ? keep state behaviour
On Thu, 06 Jan 2005 18:06:48 -0500, Jason Murray [EMAIL PROTECTED] wrote: I'm cc'ing the pf list so that the whole thread is archived. I've included the whole pf.conf file. There isn't much more than what you already have. Your suggestion does work, but it weakens the rule set. Instead of a default deny stance, I have a default deny inbound on the external interface. It also doesn't provide any clue as to why keep state isn't carrying across the interfaces. Because you block all traffic on $uat_if Sven wrote: snip On Thu, 06 Jan 2005 16:48:50 -0500, Jason Murray [EMAIL PROTECTED] wrote: Without seeing the rest of your ruleset (hint) I can't say for sure but does it work if you change block log all to block log all on $ext_if ? # macros uat_if = rl0 # UAT dev_if = xl0 # DEV ext_if = fxp0 tcp_services = { 22 } icmp_types = echoreq priv_nets = { 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 } marlin = 10.245.245.130 simba = 10.245.245.2 # options set block-policy return set loginterface $ext_if set state-policy floating # scrub scrub in all # nat/rdr nat on $ext_if from $uat_if:network to any - ($ext_if) nat on $ext_if from $dev_if:network to any - ($ext_if) #rdr on $uat_if proto tcp from any to any port 21 - 127.0.0.1 port 8021 #rdr on $dev_if proto tcp from any to any port 21 - 127.0.0.1 port 8021 rdr on $ext_if proto tcp from any to any port 80 - $marlin rdr on $ext_if proto tcp from any to any port 443 - $marlin # filter rules # Default policy is to block traffic if it is not specifically allowed block log all antispoof quick for { lo0 $uat_if $dev_if } # Traffic is allowed from Simba and Marlin pass in log quick on $dev_if from any to $marlin keep state # Traffic is not allowed from Marlin to Simba block in log quick on $uat_if from any to $simba # ssh vpn stuff # Allow inbound ssh from the Internet only to the external interface. pass in quick on $ext_if inet proto tcp from any to ($ext_if) port { 22 } flags S/SA keep state # Allow traffic from the loopback to pass. pass log quick on lo0 all keep state # Allow web traffic to the UAT (marlin) box. pass in log quick on $ext_if proto tcp from any to $marlin port { 80, 443 } flag s S/SA keep state # this rule should not be need since the keep state above should obviate it, # however I could not get the traffic to pass unless it was there. pass out log quick on $uat_if proto tcp from any to $marlin port {80, 443 } flag s S/SA keep state # Allow ping from any box to pass. pass in log quick inet proto icmp all icmp-type $icmp_types keep state I think you misunderstand keep state. From man pf.conf: If a packet matches a pass ... keep state rule, the filter creates a state for this connection and automatically lets pass all subsequent packets of that connection. The emphasis is on subsequent. In your case the first packet, the one that's supposed to create the state, is blocked on $uat_if because of the block log all rule. The rule you added is a good solution to your problem. /Sven -- Why are the pretty ones always insane? -- J.G. Thirlwell