mirror of
https://github.com/slackhq/nebula.git
synced 2026-11-15 18:26:52 +01:00
IPv6FindUpperProtocol: fail closed when the extension header walk runs out of budget (#1883)
This commit is contained in:
1 parent
3fc627e08d
commit
cf3f315cd3
2 files changed
+17
-3
No files matched your search
+5
-3
@@ -351,8 +351,8 @@ func ipv6CreateRejectTCPPacket(packet []byte, out []byte, offset int) []byte {
|
||||
// 253/254, and real upper layer protocols like SCTP or GRE, is terminal. Walking those as extension headers
|
||||
// is a firewall bypass, so they fail closed. For a non-first fragment the returned protocol is the fragmented
|
||||
// protocol and offset points at the fragment header, there is no transport header to locate. Returns
|
||||
// ErrIPv6CouldNotFindPayload if packet is smaller than an ipv6 header or the chain is truncated before a
|
||||
// terminal protocol is reached.
|
||||
// ErrIPv6CouldNotFindPayload if packet is smaller than an ipv6 header, or the chain is truncated or longer
|
||||
// than the walk's budget before a terminal protocol is reached.
|
||||
func IPv6FindUpperProtocol(packet []byte) (nextHeader uint8, offset int, isFragment bool, anyFragment bool, err error) {
|
||||
const maxIPv6ExtHeaders = 8
|
||||
if len(packet) < ipv6.HeaderLen {
|
||||
@@ -398,7 +398,9 @@ func IPv6FindUpperProtocol(packet []byte) (nextHeader uint8, offset int, isFragm
|
||||
return nextHeader, offset, isFragment, anyFragment, nil
|
||||
}
|
||||
}
|
||||
return nextHeader, offset, isFragment, anyFragment, nil
|
||||
// Budget spent without reaching a terminal protocol. We don't know where the transport starts, so fail
|
||||
// closed instead of handing back a mid-chain offset a caller would read ports from.
|
||||
return nextHeader, offset, isFragment, anyFragment, ErrIPv6CouldNotFindPayload
|
||||
}
|
||||
|
||||
func CreateICMPEchoResponse(packet, out []byte) []byte {
|
||||
|
||||
@@ -529,6 +529,17 @@ func Test_IPv6FindUpperProtocol(t *testing.T) {
|
||||
firstFragToUDP := []byte{17, 0, 0, 1, 0, 0, 0, 1} // frag offset 0, M=1, next = UDP
|
||||
nonFirstFrag := []byte{17, 0, 0, 9, 0, 0, 0, 1} // frag offset non-zero, next = UDP
|
||||
transport := []byte{0, 80, 1, 187, 0, 0, 0, 0} // stand-in bytes, IPv6FindUpperProtocol never reads ports
|
||||
extToHbH := []byte{0, 0, 0, 0, 0, 0, 0, 0} // len 0 -> 8 bytes, next = Hop-by-Hop
|
||||
|
||||
// 8 walked headers spend the budget before the 9th slot's terminal is ever classified. This is the
|
||||
// report 4040546 shape: without failing closed the walk hands back offset 104, and a caller reads ports
|
||||
// from the attacker's bytes there.
|
||||
var budgetChain []byte
|
||||
for i := 0; i < 7; i++ {
|
||||
budgetChain = append(budgetChain, extToHbH...)
|
||||
}
|
||||
budgetChain = append(budgetChain, extToTCP...)
|
||||
budgetChain = append(budgetChain, transport...)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -552,6 +563,7 @@ func Test_IPv6FindUpperProtocol(t *testing.T) {
|
||||
{"truncated extension header", 0, nil, 0, ipv6.HeaderLen, false, false, ErrIPv6CouldNotFindPayload},
|
||||
// Destination Options with a declared length (255+1)*8 = 2048 that runs past the 48 byte buffer, next = SCTP
|
||||
{"extension length past buffer", 60, []byte{132, 255, 0, 0, 0, 0, 0, 0}, 132, ipv6.HeaderLen + 2048, false, false, ErrIPv6CouldNotFindPayload},
|
||||
{"budget exhausted fails closed", 0, budgetChain, 0, 0, false, false, ErrIPv6CouldNotFindPayload},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in new issue
Block a user