Re: Strange ? keep state behaviour

2005-01-08 Thread Daniel Hartmeier
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

2005-01-08 Thread Daniel Hartmeier
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

2005-01-07 Thread Trevor Talbot
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

2005-01-07 Thread Dave Anderson
** 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

2005-01-06 Thread Jason Murray
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

2005-01-06 Thread Jason Murray
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

2005-01-06 Thread Jason Opperisano
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

2005-01-06 Thread Jason Murray

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

2005-01-06 Thread Sven
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