Subject: RE: Matching addresses in INIT and INIT ACK

From: <[email-masked]>
Date: Wed, 12 Mar 2003 12:26:43 +0200

Randall, Brian,

	... snip...

> >Ok, so if you kick X out of the TCB, then X still has the
> >VT.  It think it would be wise to require or strongly 
> >recommend that the TCB be reduced in such a way that X gets
> >kicked out.
> >  
> >
> As Ivan and I both stated.. this is what is intended, and we need
> to beef up all the collision cases with this notation that
> you must update all addresses etc.. more grist for the
> IG (as Ivan and I both stated)...
> 
> 
> In fact, thinking on this case a bit more.. it might be wise in this
> case to send a INIT and yourself "restart"... You know from the
> matching of addresses that you had a collision set
> of associations.. and thus you know things are
> amiss. But of course this comes back to the
> passive-agressive server case.. i.e. you contacted
> X in the first place, you obviously wanted something
> from X... so talking to C might not be what you want now...

	This is the problem... Most probably you want to be defensive with C, not with X... This scenario is the complete opposite to what one normally thinks it could be an attack...

> Before we worry about this case I think we need Mr. Bellovin's
> advise as to if this situation warrents attention... (as I have
> stated in other emails)... it is an interesting case.. though
> far-fetched... as Caitlin and Ivan have all said...
> 
> 
> >
> >>so if X did send in the
> >>a message it would be rejected... since X does not belong
> >>to the association.. no matter if the Vtag is correct or
> >>not...
> >>    
> >>
> >
> >Sending an ABORT with the VT is simple, X just spoofs C as the
> >source address in the ABORT and uses VT:X as the verification
> >tag.
> >  
> >
> Hmm. that may be, as long as ingress filtering is not in place.. which
> most all ISP's have now done.. it is much more difficult to spoof
> IP addresses these days...

	But, of course, it is still possible...

> >X can also send HEARTBEATs to A spoofed from C with the proper
> >VT.  A will dutifully send the HEARTBEAT-ACK with arbitrary
> >contents of the hb_info to C.  As X has the VT, some ADD-IP
> >attacks or PR-SCTP attacks might be possible.  Sending SACKs or

	I'm not that sure about this... Because of that new abort defined in the AddIP draft I talked about...

> >DATA to A spoofed from C with the proper VT could have some
> >interesting effects, because X also knows the A's initial TSN.
> >Sending DATA with a TSN skipping large blocks of TSN number to A
> >spoofed from C would canse A to send a pretty weird GAP report
> >to C that could hang many associations or have them fail
> >mysteriously.
> >
> 
> This all comes down to the passive-agressive server case... you seem
> hung on this case.. If  Steve Bellovin thinks this case
> needs attention there are some steps that can be taken.. since

	Exactly... I fail to see how somebody to who you connected, would like to attack you... Unless it is a "trusted" server, and somehow somebody manipulated it, but if somebody is able to do so, he will be able of worst things...

> you matched addresses when all of this occured you know
> there is a problem.. a number of solutions can be found:
> 
> 1) Abort both, and let the apps restart

	I don't think this is a good idea... Think that the probable attacker is C, not X...

> 2) Prune out the one (as we mention above) and then
>     put it into a restart mode.. aka send a INIT and start it
>    with a new VT..
> 3) And of course some proving of the addresses as well.
> 
> Of course all of this depends on being able to detect
> up front that it is occuring when the INIT from C arrives... which
> can only be done if you match addresses inside the INIT.

	Right...

> I will not discuss further any "passive/agressive" server case until
> after the IETF.. if Steve thinks it is not a worry then I see
> no reason to wring our hands and be concerned... on the
> other hand if not, we will probably go back through and
> flesh out the sketch we have of how to fix this..
> 
> The key of course will be early detection using the
> address lists...
> 
> 
> >
> >I think it would be best not to give X the VT of the
> >association.
> >
> >Would it cause problems to treat COOKIE-ECHOED the same as
> >ESTABLISHED?  I don't see much difference between the restart
> >actions (Action A) and collision action (Action B) for the
> >COOKIE-ECHOED state?  This would select a new IT and the VT
> >would not be surrendered in this fashion?  
> >
> I would need to think on this.. there are a couple of scenarios
> that would break I am thinking...
> 
> >Maybe that would
> >not work...  But some solution that secures the VT is necessary.
> >Could we say that if there are fewer addresses (we actually
> >kick an address out of the TCB) that we choose a new IT?
> >That would protect the VT in this case.
> >
> I think this is about right.. if this is a concern .. then we want
> the side that recognizes an address left the association to send out
> a new INIT with new VT... to the address set.. this will then
> make the association restart before it ever gets up... but
> thats ok.. we will also need to have an "audit/list" scenario
> where we HB all suspect addresses in the various Passive/Agressive
> server cases... but those we will need to wait to fully flesh
> out..

	I have took a look to scenarios where choosing a new IT when answering in the COOKIE ECHO state could be harmful... And so far it looked ok...

	If Steve says we should care about this, this might be one way of avoiding this agressive server situation...

	BR Iván Arias Rodríguez

> 
> R
> 
> -- 
> Randall R. Stewart
> ITD
> Cisco Systems Inc.
> [email-masked] 815-342-5222(cell) or 815-477-2127(office)
> 
> 
>

---

From: "Randall Stewart (cisco)" <[email-masked]>
Date: Wed, 12 Mar 2003 07:54:57 -0600

>	If Steve says we should care about this, this might be one way of avoiding this agressive server situation...
Ok, I looked into my book ... the drawings in there are nice to follow... :>
and yes I see no problem as yet.. but lets consider this a minute (and I am
feeling like drawing this am so here is the scenario

A                                                                       
     X /C?                                               D/C?
=================================================================
IT=Q

-----A->0:INIT(A,IT=Q)->X-------------------------------------->
Cookie-Wait state
<--------------A:INIT-ACK(X,C,IT=Y)<X----------------------
Cookie-echoed state
peer=X/C
------------------A->Y:COOKIE()->X--------------------------------->

<----------------------------------------------------<A--0:INIT(C,D,IT=P)<-D---------------------------------
<1> scenario 1
<Choose-New-Tag>
----------------------------------A->P:INIT-ACK(A,IT=Q')->D->--------------------------------------------->

<2> scenario 2
---------------------------------A->-ABORT(??error 
cause??)--D->------------------------------------>

Now currently, I think we would do <2> .. Ias you validly pointed out.. 
we just need
to expand it to include established :>) send an abort.. possibly with an 
Error Cause.. which
we may want to get rid of or at least have  "a MAY insert error cause" 
instead of the SHOULD
thats there...  and also note that this may give away information to an 
attacker.

Now lets say we choose <1>, since we have a different set of addresses 
we can't really
include tie-tags, otherwise we may be revealing the Tag of our forming 
assoc with X (which
is the same reason in the restart case you can't send if the address set 
is not the same).

So the only way to handle it is to update the TCB with the new choosen 
tag (Q').. but
this means that you have choosen X as the bad guy... and that association is
defunct... so when X sends

<--------------<-A-Q:COOKIE-ACK()--<-X---------

It will be rejected since the tag does not match and silently discarded. 
Now X will
eventually send a packet and retransmit until its association dies.

More so, there is a fundamental question here... whom do you trust?

The guy you contacted and want to talk to <OR> some guy that sends
you a INIT...

It could be that D does not have C but instead X has C. By doing anything
like this it just introduces a much easier attack... I can get you to
change the tag of a forming association by simply sending in
an INIT... and thus get you to talk to me instead of the real peer
you wish to talk to..  very bad...

And thinking about not looking at the addresses (as is currently
done in openss7's stack) you have a very bad situation. The RFC
requires you to silently discard packets with the wrong vtag.. and
NOT respond with an ABORT(). Without checking addresses this
is not possible..

You hash to V-Tag/local-port... and you will always return NULL
since you would not find an association that has the wrong V-Tag
... you get a null in this case when you lookup the association.
Which means you send an ABORT() back to the guy..

So If I wish to attack such a stack what I do is start sending HB's (a
rather innocent thing that could be from a previous assoc)....

Attacker:                                                               
                      Endpoint-A
Src-Does_not_matter:V-Tag-guess-1:HB----------------------------------->
                                                                        
           Lookup-Vtag() fails (we don't chk the
                                                                        
                                              address, and vtag is not 
matched).
                                    
 <---------------ABORT(No-TCB)-----------------
Eventually you get
Src-Does_not_matter:Luckey-guess:HB------------------------------------>
                                                                        
                            Lookup-Vtag() find assoc
                                      
<----------------HB-ACK---------------------------
Got Vtag.

Now if Endpoint-A responds only to known addresses aka sends the HB back to
its peers real address... no big deal.. do this enough and you now have 
an assurance
that you have guessed a V-tag that someone is using..

Now the attacker can inject any data it wants into the associations tag that
was guessed.. :-0

If you are looking up with address validation you get

Attacker:
Src-Does_matter:V-Tag-guess-1:HB------------------------------------------>
                                                                        
                      No matter what the V-Tag is no src known so
                        
 <-----------ABORT(No-TCB)------------------------------------

Here is the primary reason src addresses must be validated.. otherwise 
it not only weakens
security of the V-tag it makes it so that anyone can figure out a V-Tag 
of an assocaition...
its easy :-0

R

---

From: "Brian F. G. Bidulock" <[email-masked]>
Date: Wed, 12 Mar 2003 16:37:37 -0700

Randall,

On Wed, 12 Mar 2003, Randall Stewart (cisco) wrote:

-X- snip -X-
> Now the attacker can inject any data it wants into the associations tag that

Good point.  I will fix this.  If the hash comes back NULL, I will do a
sctp_lookup_tcb() which looks up on address pair and port pair and only
send an ABORT if that lookup fails.  The fix would apply to all such chunks:
(SHUTDOWN, SACK, HEARTBEAT, HEARTBEAT-ACK and DATA).  This change in
sctp_recv_ootb() will do the trick.

  Index: sctp.c
  ===================================================================
  RCS file: /home/common/cvsroot/linux/net/ipv4/sctp.c,v
  retrieving revision 1.9.2.36
  diff -r1.9.2.36 sctp.c
  11478c11478,11481
  < 		sctp_send_abort_ootb(iph->saddr, iph->daddr, sh);
  ---
  > #ifndef CONFIG_SCTP_SLOW_VERIFICATION
  > 		if (!sctp_lookup_tcb(sh->srce, sh->dest, iph->saddr, iph->daddr))
  > #endif
  > 			sctp_send_abort_ootb(iph->saddr, iph->daddr, sh);

The lookup is also then performed only if some such packet is actually sent,
and we have not already checked addresses in sctp_lookup_vtag because
CONFIG_SCTP_SLOW_VERIFICATION is not set.

Thanks for the security fix.

--brian

-- 
Brian F. G. Bidulock    ¦ The reasonable man adapts himself to the ¦
[email-masked]    ¦ world; the unreasonable one persists in  ¦
http://www.openss7.org/ ¦ trying  to adapt the  world  to himself. ¦
                        ¦ Therefore  all  progress  depends on the ¦
                        ¦ unreasonable man. -- George Bernard Shaw ¦

---

From: "Randall Stewart (cisco)" <[email-masked]>
Date: Thu, 13 Mar 2003 07:22:48 -0600

Brian:

Glad I could help..

As long as you are at it you might also want to look at:

1) Getting the V-tag security back up to what it should be aka a 1 in 
4billion guess
2) You might also want to take a look at fixing this security bug the
    rest of the way as well,  since you have not quite covered all cases 
and an
    attack still exists.
3) You might also want to conform to the RFC on recieving a INIT while
    in COOKIE-WAIT.. unless you pre-know all the addresses you will not
    respond properly.. unless of course this is a requirement of your 
stack... I have
    never yet understood which it is aka... a) required to know all 
addresses a-priori or
    b) you don't have to know all of your peers addresses.... if a) then 
you don'
    need to worry about 3)

But in any case fixing 1 and 2 would definetly increase security...

R

---

From: "Brian F. G. Bidulock" <[email-masked]>
Date: Thu, 13 Mar 2003 13:12:04 -0700

Randall,

> 1) Getting the V-tag security back up to what it should be aka a 1 in 

Set CONFIG_SCTP_SLOW_VERIFICATION.

>     rest of the way as well,  since you have not quite covered all cases 

Could you please point out where the attack still exists?

--brian

---

From: "Randall Stewart (cisco)" <[email-masked]>
Date: Thu, 13 Mar 2003 16:00:56 -0600

>Could you please point out where the attack still exists?
You should be able to easily spot it.. but even besides that.. I don't think
your fix really works...

Now instead of doing:

----------src-does-not-matter:VT:guess-1:HB--------->
<---------------ABORT(no-tcb)---------------------------

until I get a HB-ACK

I instead do

------src-does-not-matter:VT:guess-1:HB----------->
..<wait a RTT (not RTO) .. easily detected with a INIT >
-----src-does-not-matter:VT:guess-2:HB------------->

until

<--------------HB-ACK()

and assuming the guy is close, say 10ms, you could guess
100 vtags a second... so depending on the number of associations
you have up.. it should not take much more than an hour or two
to get a hit...  as you said, script kiddy's will love this :>

The key issue is you have to verify the source addresse as
well.. otherwise you have enabled a easy probing of your implementation
for V=Tags.. Besides of course the other one... which is a similar reverse
attack. Now if you only have one association up it might not be a big
concern... since then you only have a 1 in 4billion chance of getting in..

But a stack that checks the src address does not have this worry.. since
if the attacker were to somehow guess and spoof the src address on the
HB or any chunk that requires a response for that matter and get the
Vtag guessed as well.. the response does not go back to him...
as it does with implementations that do not check the src address at all...

The proper response to

HB
DATA
HB-ACK
SACK
SHUTDOWN
INIT (with no listner)
COOKIE-ECHO(with no listner)
ERRORS (that are  NOT a stale cookie error)

is always an ABORT... and the key here is that the src address
MUST be right or else the attack is enabled...

R

---

From: "Brian F. G. Bidulock" <[email-masked]>
Date: Thu, 13 Mar 2003 16:55:41 -0700

Randall,

> -----src-does-not-matter:VT:guess-2:HB------------->

The implementation will only ever HEARTBEAT-ACK to an address
that is part of the existing association, so the attacker will
never see it.  So the attacker gets nothing back in any case
and cannot use this approach to determine VT.

Could you please point out the other case?

--brian

---

From: "Randall Stewart (cisco)" <[email-masked]>
Date: Thu, 13 Mar 2003 18:16:11 -0600

>Could you please point out the other case?

Really:

STATIC int sctp_recv_heartbeat(sp, mp)
    sctp_t *sp;
    mblk_t *mp;
{
    struct sctp_heartbeat *m;
    struct sctpphdr *ph;
    size_t plen;

    assert( sp );
    assert( mp );

    m = (struct sctp_heartbeat *)mp->b_rptr;
    ph = (struct sctpphdr *)(m+1);
    plen = PADC(htons(m->ch.len)) - sizeof(*m);

    if ( plen >= sizeof(struct sctpphdr) ) {
    if ( ph->type == SCTP_PTYPE_HEARTBEAT_INFO ) {
    {
        caddr_t  hptr  = (caddr_t)ph;
        size_t   hlen  = min(plen, PADC(ntohs(ph->len)));
        sctp_send_heartbeat_ack(sp, hptr, hlen);
        return sctp_return_stop(mp);
    }

AND

int sctp_recv_msg(sp, mp)
    sctp_t *sp;
    mblk_t *mp;
{
    int err = -EMSGSIZE;

    if ( mp )
    {
        /* set the address for reply chunks */
        if ( sp->daddr ) {
            sp->caddr = sctp_find_daddr(sp, ((struct iphdr 
*)mp->b_datap->db_base)->saddr);
            normal( sp->caddr );
        }
        do {
            int dlen = mp->b_wptr - mp->b_rptr;
            struct sctpchdr *ch = (struct sctpchdr *)mp->b_rptr;

            unusual( dlen < 0 );
            if ( dlen > 0 && dlen >= sizeof(*ch) && dlen >= 
PADC(ntohs(ch->len)) )
            {
                switch ( (ch->type & SCTP_CTYPE_MASK) )
                {
                case SCTP_CTYPE_DATA:           err = sctp_recv_data    
        (sp, mp); break;
                case SCTP_CTYPE_INIT:           err = sctp_recv_init    
        (sp, mp); break;
                case SCTP_CTYPE_INIT_ACK:       err = 
sctp_recv_init_ack        (sp, mp); break;
                case SCTP_CTYPE_SACK:           err = sctp_recv_sack    
        (sp, mp); break;
                case SCTP_CTYPE_HEARTBEAT:       err = 
sctp_recv_heartbeat        (sp, mp); break;

I don't see anything that stops the heartbeat from being sent...

Of course, as I said, there is another obvious attack as well...

R

---

From: "Brian F. G. Bidulock" <[email-masked]>
Date: Thu, 13 Mar 2003 17:37:06 -0700

Randall,

> >Could you please point out the other case?

Yes really:

	STATIC void sctp_send_heartbeat_ack(sp, hptr, hlen)
		sctp_t *sp;
		caddr_t hptr;
		size_t hlen;
	{
		sctp_daddr_t *sd;

		assert(sp);
====>		if ((sd = sctp_route_response(sp))) {
			mblk_t *mp;
			struct sctp_heartbeat_ack *m;
			size_t clen = sizeof(*m) + hlen;
			size_t plen = PADC(clen);

			if ((mp = sctp_alloc_msg(sp, clen))) {
				m = (struct sctp_heartbeat_ack *) mp->b_wptr;
				m->ch.type = SCTP_CTYPE_HEARTBEAT_ACK;
				m->ch.flags = 0;
				m->ch.len = htons(clen);
				bcopy(hptr, (m + 1), hlen);
				mp->b_wptr += plen;
				sctp_send_msg(sp, sd, mp);
				freechunks(mp);
			}
		}
	}

	STATIC sctp_daddr_t *sctp_route_response(sp)
		sctp_t *sp;
	{
		sctp_daddr_t *sd;
		assert(sp);
====>		sd = sp->caddr;
====>		if (!sd || !sd->dst_cache || sd->retransmits)
			sd = sctp_route_normal(sp);
		normal(sd);
		return (sd);
	}

		if (mp) {
			/* set the address for reply chunks */
			if (sp->daddr) {
====>				sp->caddr = sctp_find_daddr(sp, ((struct iphdr *) mp->b_datap->db_base)->saddr);
				normal(sp->caddr);
			}
	.
	.
	.
	.

The HEARTBEAT-ACK will never be sent to an address other than a valid
destination address for the association.

The attacker will not receive the HEARTBEAT-ACK.

-X- snip -X-

> I don't see anything that stops the heartbeat from being sent...

Well, first it is the HEARTBEAT-ACK that you say is being sent to
the attacker in your example.  It is being sent, but will not be
sent to the attacker, so the attacker always gets nothing and can
not determine the VT this way.

> Of course, as I said, there is another obvious attack as well...

Please describe this other obvious attack.  I am interested to see it.

--brian

---

From: "Randall Stewart (cisco)" <[email-masked]>
Date: Fri, 14 Mar 2003 06:04:21 -0600

>Please describe this other obvious attack.  I am interested to see it.
Think about SHUTDOWN-ACK's.

Oh, one other little salient point.. The whole purpose of always getting
an ABORT back to HB's is to clean up state. So one other little
issue with your fix... consider this:

Host A                                                                   
  Host B
---------                                                               
    ------------
Host A initiates--->
   <--------------Valid association----------------------------->
                                                                        
      (Restart of Host-B)

--------------------HB---------------------------->

Now the normal and RFC compliant response is to send

 <-------------------ABORT(no-tcb)----------------------------

So that Host A can clean up its state and restart the
association (if the app so chooses)..

But with your little fix you would deem Host-A an attacker
and ignore the HB's...

Bottom line is you should always send an ABORT back to the
chunk types I put in one of my previous emails when you
either don't have a listner or no-tcb...

R

---

From: "Brian F. G. Bidulock" <[email-masked]>
Date: Fri, 14 Mar 2003 10:43:44 -0700

Randall,

> Think about SHUTDOWN-ACK's.

Yes, that was so obvious, it was already fixed:

	STATIC int sctp_rcv_ootb(struct sk_buff * skb)
	{
		struct iphdr *iph = skb->nh.iph;
		struct sctphdr *sh = skb->h.sh;
		struct sctpchdr *ch = (typeof(ch)) skb->data;
		int sat = inet_addr_type(iph->saddr);
		seldom();
		ensure(skb, return (-EFAULT));
		if (sat != RTN_UNICAST && sat != RTN_LOCAL) {
			/* RFC 2960 8.4(1) */
			kfree_skb(skb);
			return (0);
		}
		switch (ch->type) {
		case SCTP_CTYPE_COOKIE_ACK:		/* RFC 2960 8.4(7).	*/
		case SCTP_CTYPE_ERROR:			/* RFC 2960 8.4(7).	*/
		case SCTP_CTYPE_ABORT:			/* RFC 2960 8.4(2).	*/
		case SCTP_CTYPE_SHUTDOWN_COMPLETE:	/* RFC 2960 8.4(6).	*/
		case SCTP_CTYPE_INIT:			/* RFC 2960 8.4(3) and (8). */
		case SCTP_CTYPE_INIT_ACK:		/* RFC 2960 8.4(8).	*/
		case SCTP_CTYPE_COOKIE_ECHO:		/* RFC 2960 8.4(4) and (8). */
		default:
			seldom();
			break;
		case SCTP_CTYPE_SHUTDOWN:		/* RFC 2960 8.4(8).	*/
		case SCTP_CTYPE_SACK:			/* RFC 2960 8.4(8).	*/
		case SCTP_CTYPE_HEARTBEAT:		/* RFC 2960 8.4(8).	*/
		case SCTP_CTYPE_HEARTBEAT_ACK:		/* RFC 2960 8.4(8).	*/
		case SCTP_CTYPE_DATA:			/* RFC 2960 8.4(8).	*/
			seldom();
	#ifndef CONFIG_SCTP_SLOW_VERIFICATION
			if (!sctp_lookup_tcb(sh->srce, sh->dest, iph->saddr, iph->daddr))
	#endif
				sctp_send_abort_ootb(iph->saddr, iph->daddr, sh);
			break;
		case SCTP_CTYPE_SHUTDOWN_ACK:
			seldom();
	#ifndef CONFIG_SCTP_SLOW_VERIFICATION
			if (!sctp_lookup_tcb(sh->srce, sh->dest, iph->saddr, iph->daddr))
	#endif
				sctp_send_shutdown_complete_ootb(iph->saddr, iph->daddr, sh);
			break;
		}
		kfree_skb(skb);
		return (0);
	}

> issue with your fix... consider this:

The fix doesn't deem anybody an attacker.  It just searches
on port and address pair if a vtag match is not found.  If
no tcb exists the ABORT is sent.

The error was that ABORT was sent when vtag didn't match but
a tcb existed if CONFIG_SCTP_SLOW_VERIFICATION was not set.
The fix corrects that to only sending ABORT when the vtag
doesn't match and a tcb does not exist regardless of the
setting of CONFIG_SCTP_SLOW_VERIFICATION.

> either don't have a listner or no-tcb...

That's what the fix does.

Thanks again for pointing it out.

--brian

---

From: "Randall Stewart (cisco)" <[email-masked]>
Date: Fri, 14 Mar 2003 12:21:28 -0600

>Yes, that was so obvious, it was already fixed:

Great, I am glad you found it.. now let me get something straight because
I am still confused by your "fixes"

... You reach here (sctp_rcv_ootb()) out of the input due to the
fact that the VT did not match... and so you could not find a TCB..

>		case SCTP_CTYPE_DATA:			/* RFC 2960 8.4(8).	*/

assuming you hit the IF below... how will you ever match my
iph->saddr...

an association is up between

A---------------------------------------->Z

I send a HB(from:X) with Guess-Tag------------>Z

Of course Guess-Tag does not match.. so you hit this module.. your hunt 
for a TCB
will return NULL since you don't have an assocation with X right?  Which
means you send me an Abort... (which is actually correct)...

But if I guess the tag correctly.. you don't hit this code.. instead you
either send nothing back... or send a HB-ACK (I think you showed in your
routing calls that you sent back only to the cached address or some such.. I
don't have time to study it..)... But is this not providing me with a guess
attack from you?  Since if the tag does match I will get nothing back and
the peer (A) will get the HB-ACK...

Where-as if I do get an abort back  if I guess wrong..
The problem is more in the fact that you DO NOT recognize the difference
between a Guessed Tag/HB and a HB from the peer by checking the
addresses.. a correctly guessed V-Tag with bad source address should
also generate a ABORT...

>		case SCTP_CTYPE_SHUTDOWN_ACK:

And here it is the same thing... if I send you my bogus address 'X' you 
will never
find a TCB between X/port <--> Z/port... so you will send me a 
Shutdown-Complete
until I guess the tag.. and then you will silently discard the 
Shutdown-Ack since
you are not in the shutdown state...

The source of the problem is not checking the source addresses and handling
a non matching source address as an OOTB..

Note also an INIT or COOKIE-ECHO sent to a non-listening port (how you
would get here) should also get an ABORT sent back... and same with all but
one specific type of ERROR ...

R

---

From: "Brian F. G. Bidulock" <[email-masked]>
Date: Fri, 14 Mar 2003 23:57:09 -0700

Randall,

-X- snip -X-
> means you send me an Abort... (which is actually correct)...

Yes.

> addresses.. a correctly guessed V-Tag with bad source address should

Well no, because of the change from our previous discussions (Mar. 6).

The first change was to assure that the verification tag was unique in the
hashes.

	 extern uint32_t secure_tcp_sequence_number(uint32_t, uint32_t, uint16_t, uint16_t);
	 STATIC uint32_t sctp_get_vtag(uint32_t daddr, uint32_t saddr, uint16_t dport,
				      uint16_t sport)
	 {
		uint32_t ret;
	-	ret = secure_tcp_sequence_number(daddr, saddr, dport, sport);
	-	usual(ret);
	+	while (!(ret = secure_tcp_sequence_number(daddr, saddr, dport, sport)) ||
	+	       sctp_lookup_vtag(ret, sport, dport, saddr, daddr));
		return (ret);
	 }

This meant that sport was no longer necessary in vtag lookups and vtag could
be used alone to find the socket/stream.

	 #if !defined(CONFIG_SCTP_SLOW_VERIFICATION) || defined(CONFIG_SCTP_ADD_IP)
	 #define sctp_match_vtag(__sk, __saddr, __daddr, __v_tag, __sport, __dport) \
	-	( ((__v_tag) == SCTP_PROT(__sk)->v_tag) && \
	-	  ((__sport) == (__sk)->sport) )
	+	( ((__v_tag) == SCTP_PROT(__sk)->v_tag) )
	 #else
	 #define sctp_match_vtag(__sk, __saddr, __daddr, __v_tag, __sport, __dport) \
		( ((__v_tag) == SCTP_PROT(__sk)->v_tag) && \
		  ((__sport) == (__sk)->sport) && \
		  ((__dport) == (__sk)->dport) && \
	-	  (sctp_find_saddr((__sk),(__daddr))) && \
	-	  (sctp_find_daddr((__sk),(__saddr))) )
	+	  (sctp_find_saddr((__sk),(__daddr))) )
	 #endif

The second change was to test caddr (which is set to the reply address for all
packets anyway) in sctp_recv_msg as we discussed.

	 STATIC int sctp_recv_msg(struct sock *sk, struct sk_buff *skb)
	 {
		int err;
		struct sctp_opt *sp = SCTP_PROT(sk);
		struct sctpchdr *ch;
		ensure(sk, goto efault);
		ensure(skb, goto efault);
		/* set address for reply chunks */
	-	if (sp->daddr) {
	-		sp->caddr = sctp_find_daddr(sk, skb->nh.iph->saddr);
	-		normal(sp->caddr);
	+	if (!(sp->caddr = sctp_find_daddr(sk, skb->nh.iph->saddr))) {
	+		ch = (typeof(ch)) skb->data;
	+		switch (ch->type) {
	+		case SCTP_CTYPE_INIT:
	+		case SCTP_CTYPE_INIT_ACK:
	+		case SCTP_CTYPE_COOKIE_ECHO:
	+			break;
	+		default:
	+			SCTP_INC_STATS_BH(SctpOutOfBlues);
	+			return sctp_rcv_ootb(skb);
	+		}
		}
		do {

This change meant that source address never needs to be checked when looking
up the socket/stream and it was removed from sctp_match_vtag above.

One thing about Linux (and STREAMS) is that a backlog is queued against a
socket (stream) while the user has the socket/stream locked.  Because there
might be an INIT-ACK or COOKIE-ECHO in the backlog of messages waiting to be
processed, the state of the socket is not current until the backlog is
processed.  A match against source address would incorrect if an INIT-ACK or
COOKIE-ECHO (or ABORT) is in the backlog.

This is actually not a rare situation.  When the addresses involved are
loopback addresses (RTT of about 0) the user often still has the socket locked
on connect() when the INIT-ACK is received.

Therefore, source addresses cannot be considered until the message is actually
processed against the socket/stream that has been found by vtag.

Perhaps you can see now why I object to the wording "[w]hen searching for a
TCB" in IG 2.18.

> >		case SCTP_CTYPE_SHUTDOWN_ACK:

Actually, I removed the ifndef from above as well as the source address
is never checked in sctp_lookup_vtag now with the fix to sctp_recv_msg.

> a non matching source address as an OOTB..

Thanks for pointing it out, but I think I've got that one covered.  I think
the fix I suggested Mar 6 curred that.  You didn't see the whole fix (I just
showed returning -EPROTO to the caller).  See sctp_recv_msg above.  Anything
matching on vtag that has found a socket/stream which is from a source address
not belonging to the socket (except INIT, INIT-ACK and COOKIE-ECHO) will be
treated as OOTB there.  That fix made SLOW_VERIFICATION the equivalent of
RIDICULOUSLY_SLOW_VERIFICATION for vtag lookups.  It still has some use for
ptag lookups (because the uniqueness of peer tags for a given port pair cannot
be completely guaranteed).  The change to sctp_get_vtag provides strong
assurances that vtags are unique in the vtag hash (well their might be some
races, but the randomness of the vtag and the narrow time-window of the race
makes it a strong assurance).

So, when looking for a TCB all I need to use is vtag.  The reason for looking
up the source address in sctp_recv_msg was to determine where to send reply
chunks.  Now the source address is checked for validity just before processing
the packet against the TCB solely to thwart the type of attacks you have
presented.

> one specific type of ERROR ...

Well, there are no missing mandatory parameters, no invalid parameter values,
and no lack of local resources, so the sender gets static (and an invitation
to try again after T1-init).  That was intentional.  If there are missing
mandatory parameters, invalid parameter values or lack of local resources
(listen socket backlog exceeded) the ABORT with proper error value is sent in
compliance to RFC 2960 Section 5.1.

Thanks again for pointing out the ABORT problem.

--brian

---

From: "Randall Stewart (cisco)" <[email-masked]>
Date: Sat, 15 Mar 2003 15:33:48 -0600

>up the socket/stream and it was removed from sctp_match_vtag above.

Hmm.. this is better.. it does tighten security a bit more...

>Perhaps you can see now why I object to the wording "[w]hen searching for a
But in any event .. wording in the IG 2.18 or not.. you are still not 
compliant
to the RFC... you are supposed to use the same V-tag when the crossing
INIT's occur... I think we will adopt Ivan's suggested wording.. but note
that it requires you to have the "same" result as if you had used the 
addresses... aka
so you properly include the same V-Tag when you send a INIT-ACK to someone
who is colliding with you...

It is an unfortunate that you went with a design that can't meet the 
requirements.. I hope
that LK-SCTP did not do this too..  (I don't think so from my earlier 
conversations
with La Monte... ) that way at least one of the implementations is 
RFC2960 compliant...

>Thanks again for pointing out the ABORT problem.

But you don't send the abort in all cases when you are supposed to.. 
thats your choice
but its non-compliant...

There may be other attacks in this setup.. I think we have minimized 
most of them but
you still (as Ivan and Caitlin pointed out) have some situations where 
associations will
not work like they should (unless of course you pre-know all the 
addresses)... this
is just a limitation you have decided to place on your stack..

R