func (c *Connection) detectCensorInjection(p *types.PacketManifest) {
	var attackType string
	if p.TCP.FIN || p.TCP.RST {
		// ignore "closing" retransmissions
		return
	}
	if len(p.Payload) == 0 {
		return
	}
	if c.closingRST {
		attackType = "censor-injection-RST_"
	} else if c.closingFIN {
		attackType = "censor-injection-FIN_"
	} else {
		attackType = "censor-injection-coalesce_"
	}
	if c.closingFlow != nil {
		if p.Flow.Equal(c.closingFlow) && types.Sequence(p.TCP.Seq).Difference(c.closingSeq) == 0 {
			attackType += "closing-sequence-overlap"
		} else {
			return
		}
	} else {
		return
	}
	event := types.Event{
		Type:          attackType,
		PacketCount:   c.packetCount,
		Time:          time.Now(),
		Flow:          p.Flow,
		StartSequence: types.Sequence(p.TCP.Seq),
	}
	c.AttackLogger.Log(&event)
	c.attackDetected = true
}
// stateConnectionEstablished is called by our TCP FSM runtime and
// changes our state to TCP_DATA_TRANSFER if we receive a valid final
// handshake ACK packet.
func (c *Connection) stateConnectionEstablished(p *types.PacketManifest) {
	if !c.attackDetected {
		if c.DetectHijack {
			c.detectHijack(p, p.Flow)
			if c.attackDetected {
				return
			}
		}
	}
	if !p.Flow.Equal(c.clientFlow) {
		log.Print("handshake anomaly")
		return
	}
	if !p.TCP.ACK || p.TCP.SYN {
		log.Print("handshake anomaly")
		return
	}
	if types.Sequence(p.TCP.Seq).Difference(c.clientNextSeq) != 0 {
		log.Print("handshake anomaly")
		return
	}
	if types.Sequence(p.TCP.Ack).Difference(c.serverNextSeq) != 0 {
		log.Print("handshake anomaly")
		return
	}
	c.state = TCP_DATA_TRANSFER
	log.Printf("connected %s\n", c.clientFlow.String())
}
// stateFinWait2 handles packets for the FIN-WAIT-2 state
func (c *Connection) stateFinWait2(p *types.PacketManifest, flow *types.TcpIpFlow, nextSeqPtr *types.Sequence, nextAckPtr *types.Sequence, statePtr *uint8) {
	c.detectCensorInjection(p)
	diff := nextSeqPtr.Difference(types.Sequence(p.TCP.Seq))
	if diff < 0 {
		// overlap
		if len(p.Payload) > 0 {
			c.detectInjection(p, p.Flow)
		}
	} else if diff > 0 {
		// future out of order
		log.Print("FIN-WAIT-2: out of order packet received.\n")
		log.Printf("got TCP.Seq %d expected %d\n", p.TCP.Seq, *nextSeqPtr)
		c.Close()
	} else if diff == 0 {
		// contiguous
		if p.TCP.ACK && p.TCP.FIN {
			if types.Sequence(p.TCP.Ack).Difference(*nextAckPtr) != 0 {
				log.Print("FIN-WAIT-2: out of order ACK packet received.\n")
				c.Close()
				return
			}
			*nextSeqPtr += 1
			*statePtr = TCP_TIME_WAIT
		} else {
			if len(p.Payload) > 0 {
				c.detectInjection(p, p.Flow)
			} else {
				log.Print("FIN-WAIT-2: wtf non-FIN/ACK with payload len 0")
			}
		}
	}
}
// stateLastAck represents the TCP FSM's LAST-ACK state
func (c *Connection) stateLastAck(p *types.PacketManifest, flow *types.TcpIpFlow, nextSeqPtr *types.Sequence, nextAckPtr *types.Sequence, statePtr *uint8) {
	if types.Sequence(p.TCP.Seq).Difference(*nextSeqPtr) == 0 {
		if p.TCP.ACK && (!p.TCP.FIN && !p.TCP.SYN) {
			if types.Sequence(p.TCP.Ack).Difference(*nextAckPtr) != 0 {
				log.Printf("LAST-ACK: out of order ACK packet received. seq %d != nextAck %d\n", p.TCP.Ack, *nextAckPtr)
			}
		} else {
			log.Print("LAST-ACK: protocol anamoly\n")
		}
	} else {
		log.Print("LAST-ACK: out of order packet received\n")
		log.Printf("LAST-ACK: out of order packet received; got %d expected %d\n", p.TCP.Seq, *nextSeqPtr)
	}
	c.state = TCP_CLOSED
}
func (o *OrderedCoalesce) insert(packetManifest *types.PacketManifest, nextSeq types.Sequence) (types.Sequence, bool) {
	isEnd := false
	if o.first != nil && o.first.Seq == nextSeq {
		panic("wtf")
	}
	// XXX for now we ignore zero size packets
	if len(packetManifest.Payload) == 0 {
		return nextSeq, false
	}
	if o.pageCount < 0 {
		panic("OrderedCoalesce.insert pageCount less than zero")
	}
	// XXX todo: handle out of order FIN and RST packets
	p, p2, pcount := o.pagesFromTcp(packetManifest)
	prev, current := o.traverse(types.Sequence(packetManifest.TCP.Seq))
	o.pushBetween(prev, current, p, p2)
	o.pageCount += pcount
	if (o.MaxBufferedPagesPerFlow > 0 && o.pageCount >= o.MaxBufferedPagesPerFlow) ||
		(o.MaxBufferedPagesTotal > 0 && o.PageCache.used >= o.MaxBufferedPagesTotal) {
		if o.pageCount < 0 {
			panic("OrderedCoalesce.insert pageCount less than zero")
		}
		nextSeq, isEnd = o.flushUntilThreshold(nextSeq)
	}
	return nextSeq, isEnd
}
// stateConnectionRequest gets called by our TCP finite state machine runtime
// and moves us into the TCP_CONNECTION_ESTABLISHED state if we receive
// a SYN/ACK packet.
func (c *Connection) stateConnectionRequest(p *types.PacketManifest) {
	if !p.Flow.Equal(c.serverFlow) {
		log.Print("handshake anomaly")
		return
	}
	if !(p.TCP.SYN && p.TCP.ACK) {
		log.Print("handshake anomaly")
		return
	}
	if c.clientNextSeq.Difference(types.Sequence(p.TCP.Ack)) != 0 {
		log.Print("handshake anomaly")
		return
	}
	c.state = TCP_CONNECTION_ESTABLISHED
	c.serverNextSeq = types.Sequence(p.TCP.Seq).Add(len(p.Payload) + 1) // XXX see above comment about TCP extentions
	c.firstSynAckSeq = p.TCP.Seq
}
// stateUnknown gets called by our TCP finite state machine runtime
// and moves us into the TCP_CONNECTION_REQUEST state if we receive
// a SYN packet... otherwise TCP_DATA_TRANSFER state.
func (c *Connection) stateUnknown(p *types.PacketManifest) {
	if p.TCP.SYN && !p.TCP.ACK {
		c.state = TCP_CONNECTION_REQUEST
		c.clientFlow = p.Flow
		c.serverFlow = p.Flow.Reverse()

		// Note that TCP SYN and SYN/ACK packets may contain payload data if
		// a TCP extension is used...
		// If so then the sequence number needs to track this payload.
		// For more information see: https://tools.ietf.org/id/draft-agl-tcpm-sadata-00.html
		c.clientNextSeq = types.Sequence(p.TCP.Seq).Add(len(p.Payload) + 1)
		c.hijackNextAck = c.clientNextSeq

	} else {
		// else process a connection after handshake
		c.state = TCP_DATA_TRANSFER
		c.clientFlow = p.Flow
		c.serverFlow = p.Flow.Reverse()

		// skip handshake hijack detection completely
		c.skipHijackDetectionCount = 0
		c.clientNextSeq = types.Sequence(p.TCP.Seq).Add(len(p.Payload) + 1)

		if p.TCP.FIN || p.TCP.RST {
			c.state = TCP_CLOSED
			c.closingFlow = p.Flow
			c.closingSeq = types.Sequence(p.TCP.Seq)
			return
		} else {
			if len(p.Payload) > 0 {
				isEnd := false
				c.clientNextSeq, isEnd = c.ServerCoalesce.insert(p, c.clientNextSeq)
				if isEnd {
					c.state = TCP_CLOSED
					c.closingFlow = p.Flow
					c.closingSeq = types.Sequence(p.TCP.Seq)
					return
				}
			}
		}
	}
}
func TestGetRingSlicePanic3(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Error("failed to panic")
			t.Fail()
		}
	}()

	head := types.NewRing(3)
	head.Reassembly = &types.Reassembly{
		Seq:   types.Sequence(2),
		Bytes: []byte{1, 2, 3, 4, 5},
	}

	tail := types.NewRing(3)
	tail.Reassembly = &types.Reassembly{
		Seq:   types.Sequence(2),
		Bytes: []byte{1, 2, 3, 4, 5},
	}
	_ = getRingSlice(head, tail, 0, 6)
}
// getOverlapRings returns the head and tail ring elements corresponding to the first and last
// overlapping ring segments... that overlap with the given packet (types.PacketManifest).
// Furthermore geOverlapRings also will make sure none of these ring elements will have a Reassembly.Skip value
// other than 0 (zero).
func getOverlapRings(p *types.PacketManifest, flow *types.TcpIpFlow, ringPtr *types.Ring) (*types.Ring, *types.Ring) {
	var head, tail *types.Ring
	start := types.Sequence(p.TCP.Seq)
	end := start.Add(len(p.Payload) - 1)
	head = getHeadFromRing(ringPtr, start, end)
	if head == nil {
		return nil, nil
	}
	tail = getTailFromRing(head, end)
	if tail == nil {
		return head, head
	}
	return head, tail
}
Exemple #10
0
func (i *TCPStreamInjector) SpraySequenceRangePackets(start uint32, count int) error {
	var err error

	currentSeq := types.Sequence(start)
	stopSeq := currentSeq.Add(count)

	for ; currentSeq.Difference(stopSeq) != 0; currentSeq = currentSeq.Add(1) {
		i.tcp.Seq = uint32(currentSeq)
		err = i.Write()
		if err != nil {
			return err
		}
	}
	return nil
}
Exemple #11
0
// stateCloseWait represents the TCP FSM's CLOSE-WAIT state
func (c *Connection) stateCloseWait(p *types.PacketManifest) {
	var nextSeqPtr *types.Sequence
	if p.Flow.Equal(c.clientFlow) {
		nextSeqPtr = &c.clientNextSeq
	} else {
		nextSeqPtr = &c.serverNextSeq
	}
	diff := types.Sequence(p.TCP.Seq).Difference(*nextSeqPtr)
	// stream overlap case
	if diff > 0 {
		if len(p.Payload) > 0 {
			c.detectInjection(p, p.Flow)
		} else {
			c.detectCensorInjection(p)
		}
	}
}
func TestOrderedCoalesceUsedPages(t *testing.T) {
	maxBufferedPagesTotal := 1024
	maxBufferedPagesPerFlow := 1024
	streamRing := types.NewRing(40)
	PageCache := newPageCache()

	ipFlow, _ := gopacket.FlowFromEndpoints(layers.NewIPEndpoint(net.IPv4(1, 2, 3, 4)), layers.NewIPEndpoint(net.IPv4(2, 3, 4, 5)))
	tcpFlow, _ := gopacket.FlowFromEndpoints(layers.NewTCPPortEndpoint(layers.TCPPort(1)), layers.NewTCPPortEndpoint(layers.TCPPort(2)))
	flow := types.NewTcpIpFlowFromFlows(ipFlow, tcpFlow)

	var nextSeq types.Sequence = types.Sequence(1)

	coalesce := NewOrderedCoalesce(nil, flow, PageCache, streamRing, maxBufferedPagesTotal, maxBufferedPagesPerFlow, false)

	ip := layers.IPv4{
		SrcIP:    net.IP{1, 2, 3, 4},
		DstIP:    net.IP{2, 3, 4, 5},
		Version:  4,
		TTL:      64,
		Protocol: layers.IPProtocolTCP,
	}
	tcp := layers.TCP{
		Seq:     3,
		SYN:     false,
		SrcPort: 1,
		DstPort: 2,
	}
	p := types.PacketManifest{
		Timestamp: time.Now(),
		Flow:      flow,
		IP:        ip,
		TCP:       tcp,
		Payload:   []byte{1, 2, 3, 4, 5, 6, 7},
	}

	coalesce.insert(&p, nextSeq)

	if coalesce.PageCache.used != 1 {
		t.Errorf("coalesce.pager.Used() not equal to 1\n")
		t.Fail()
	}

	coalesce.Close()
}
Exemple #13
0
// detectHijack checks for duplicate SYN/ACK indicating handshake hijake
// and submits a report if an attack was observed
func (c *Connection) detectHijack(p *types.PacketManifest, flow *types.TcpIpFlow) {
	// check for duplicate SYN/ACK indicating handshake hijake
	if !flow.Equal(c.serverFlow) {
		return
	}
	if p.TCP.ACK && p.TCP.SYN {
		if types.Sequence(p.TCP.Ack).Difference(c.hijackNextAck) == 0 {
			if p.TCP.Seq != c.firstSynAckSeq {
				log.Print("handshake hijack detected\n")
				c.AttackLogger.Log(&types.Event{
					Time:        time.Now(),
					Type:        "handshake-hijack",
					PacketCount: c.packetCount,
					Flow:        flow,
					HijackSeq:   p.TCP.Seq,
					HijackAck:   p.TCP.Ack})
				c.attackDetected = true
			} else {
				log.Print("SYN/ACK retransmission\n")
			}
		}
	}
}
// pagesFromTcp creates a page (or set of pages) from a TCP packet.  Note that
// it should NEVER receive a SYN packet, as it doesn't handle sequences
// correctly.
//
// It returns the first and last page in its doubly-linked list of new pages.
func (o *OrderedCoalesce) pagesFromTcp(p *types.PacketManifest) (*page, *page, int) {
	first := o.PageCache.next(p.Timestamp)
	count := 1
	current := first
	seq, bytes := types.Sequence(p.TCP.Seq), p.Payload
	for {
		length := min(len(bytes), pageBytes)
		current.Bytes = current.buf[:length]
		copy(current.Bytes, bytes)
		current.Seq = seq
		bytes = bytes[length:]
		if len(bytes) == 0 {
			break
		}
		seq = seq.Add(length)
		current.next = o.PageCache.next(p.Timestamp)
		count++
		current.next.prev = current
		current = current.next
	}
	current.End = p.TCP.RST || p.TCP.FIN
	return first, current, count
}
Exemple #15
0
// stateFinWait1 handles packets for the FIN-WAIT-1 state
//func (c *Connection) stateFinWait1(p *types.PacketManifest) {
func (c *Connection) stateFinWait1(p *types.PacketManifest, flow *types.TcpIpFlow, nextSeqPtr *types.Sequence, nextAckPtr *types.Sequence, statePtr, otherStatePtr *uint8) {
	c.detectCensorInjection(p)
	diff := nextSeqPtr.Difference(types.Sequence(p.TCP.Seq))
	if diff < 0 {
		// overlap
		if len(p.Payload) > 0 {
			c.detectInjection(p, p.Flow)
		}
		return
	} else if diff > 0 {
		// future out of order
		log.Print("FIN-WAIT-1: ignoring out of order packet")
		return
	} else if p.TCP.ACK {
		*nextAckPtr += 1
		if p.TCP.FIN {
			*statePtr = TCP_CLOSING
			*otherStatePtr = TCP_LAST_ACK
			*nextSeqPtr = types.Sequence(p.TCP.Seq).Add(len(p.Payload) + 1)
			if types.Sequence(p.TCP.Ack).Difference(*nextAckPtr) != 0 {
				log.Printf("FIN-WAIT-1: unexpected ACK: got %d expected %d TCP.Seq %d\n", p.TCP.Ack, *nextAckPtr, p.TCP.Seq)
				c.closingFlow = p.Flow
				c.closingSeq = types.Sequence(p.TCP.Seq)
				return
			}
		} else {
			*statePtr = TCP_FIN_WAIT2
			*nextSeqPtr = types.Sequence(p.TCP.Seq).Add(len(p.Payload))
		}
	} else {
		log.Print("FIN-WAIT-1: non-ACK packet received.\n")
		c.closingFlow = p.Flow
		c.closingSeq = types.Sequence(p.TCP.Seq)
		return
	}
}
func TestGetRingSlice(t *testing.T) {
	options := ConnectionOptions{
		MaxBufferedPagesTotal:         0,
		MaxBufferedPagesPerConnection: 0,
		MaxRingPackets:                40,
		PageCache:                     nil,
		LogDir:                        "fake-log-dir",
	}

	f := &DefaultConnFactory{}
	conn := f.Build(options).(*Connection)
	for j := 5; j < 40; j += 5 {
		reassembly := types.Reassembly{
			Seq:   types.Sequence(j),
			Bytes: []byte{1, 2, 3, 4, 5},
		}

		conn.ClientStreamRing.Reassembly = &reassembly
		conn.ClientStreamRing = conn.ClientStreamRing.Next()
	}
	var startSeq uint32 = 5
	p := types.PacketManifest{
		IP: layers.IPv4{
			SrcIP:    net.IP{1, 2, 3, 4},
			DstIP:    net.IP{2, 3, 4, 5},
			Version:  4,
			TTL:      64,
			Protocol: layers.IPProtocolTCP,
		},
		TCP: layers.TCP{
			Seq:     startSeq,
			SrcPort: 1,
			DstPort: 2,
		},
		Payload: []byte{1, 2, 3, 4, 5, 6, 7},
	}

	ipFlow, _ := gopacket.FlowFromEndpoints(layers.NewIPEndpoint(net.IPv4(1, 2, 3, 4)), layers.NewIPEndpoint(net.IPv4(2, 3, 4, 5)))
	tcpFlow, _ := gopacket.FlowFromEndpoints(layers.NewTCPPortEndpoint(layers.TCPPort(1)), layers.NewTCPPortEndpoint(layers.TCPPort(2)))
	serverFlow := types.NewTcpIpFlowFromFlows(ipFlow, tcpFlow)
	clientFlow := serverFlow.Reverse()
	conn.serverFlow = serverFlow
	conn.clientFlow = clientFlow

	head, tail := getOverlapRings(&p, serverFlow, conn.ClientStreamRing)

	if head == nil {
		t.Fatal()
	}
	if tail == nil {
		t.Fatal()
	}

	ringSlice := getRingSlice(head, tail, 0, 1)
	if !bytes.Equal(ringSlice, []byte{1, 2, 3, 4, 5, 1}) {
		t.Error("byte comparison failed")
		t.Fail()
	}

	ringSlice = getRingSlice(head, tail, 0, 3)
	if !bytes.Equal(ringSlice, []byte{1, 2, 3, 4, 5, 1, 2, 3}) {
		t.Error("byte comparison failed")
		t.Fail()
	}

	ringSlice = getRingSlice(head, tail, 0, 1)
	if !bytes.Equal(ringSlice, []byte{1, 2, 3, 4, 5, 1}) {
		t.Error("byte comparison failed")
		t.Fail()
	}

	ringSlice = getRingSlice(head, tail, 1, 0)
	if !bytes.Equal(ringSlice, []byte{2, 3, 4, 5}) {
		t.Error("byte comparison failed")
		t.Fail()
	}

	ringSlice = getRingSlice(head, tail, 1, 1)
	if !bytes.Equal(ringSlice, []byte{2, 3, 4, 5, 1}) {
		t.Error("byte comparison failed")
		t.Fail()
	}

	ringSlice = getRingSlice(head, tail, 2, 0)
	if !bytes.Equal(ringSlice, []byte{3, 4, 5}) {
		t.Error("byte comparison failed")
		t.Fail()
	}
	ringSlice = getRingSlice(head, tail, 2, 3)
	if !bytes.Equal(ringSlice, []byte{3, 4, 5, 1, 2, 3}) {
		t.Error("byte comparison failed")
		t.Fail()
	}

	startSeq = 1
	p = types.PacketManifest{
		IP: layers.IPv4{
			SrcIP:    net.IP{1, 2, 3, 4},
			DstIP:    net.IP{2, 3, 4, 5},
			Version:  4,
			TTL:      64,
			Protocol: layers.IPProtocolTCP,
		},
		TCP: layers.TCP{
			Seq:     startSeq,
			SrcPort: 1,
			DstPort: 2,
		},
		Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
	}

	head, tail = getOverlapRings(&p, serverFlow, conn.ClientStreamRing)

	log.Printf("sequence of head %d", head.Reassembly.Seq)
	log.Printf("and tail %d", tail.Reassembly.Seq)

	ringSlice = getRingSlice(head, tail, 0, 2)

	if !bytes.Equal(ringSlice, []byte{1, 2, 3, 4, 5, 1, 2}) {
		t.Errorf("ringSlice is %x\n", ringSlice)
		t.Fail()
	}

	ringSlice = getRingSlice(head, tail, 2, 4)
	if !bytes.Equal(ringSlice, []byte{3, 4, 5, 1, 2, 3, 4}) {
		t.Errorf("ringSlice is %x\n", ringSlice) //XXX
		t.Fail()
	}
}
func injectionInStreamRing(p *types.PacketManifest, flow *types.TcpIpFlow, ringPtr *types.Ring, eventType string, packetCount uint64) *types.Event {
	start := types.Sequence(p.TCP.Seq)
	end := start.Add(len(p.Payload) - 1)
	head, tail := getOverlapRings(p, flow, ringPtr)

	if head == nil || tail == nil {
		return nil
	}

	overlapBytes, startOffset, endOffset := getOverlapBytes(head, tail, start, end)

	if overlapBytes == nil {
		return nil
	}
	if len(overlapBytes) > len(p.Payload) {
		log.Printf("impossible: overlapBytes length greater than payload length at packet # %d", packetCount)
		return nil
	}
	if startOffset >= endOffset {
		log.Print("impossible: startOffset >= endOffset")
		return nil
	}
	if endOffset > len(p.Payload) {
		log.Print("impossible: endOffset greater than payload length")
		return nil
	}

	log.Printf("len overlapBytes %d startOffset %d endOffset %d\n", len(overlapBytes), startOffset, endOffset)

	if len(overlapBytes) != len(p.Payload[startOffset:endOffset]) {
		log.Printf("impossible: %d != %d len overlapBytes is not equal to payload slice", len(overlapBytes), len(p.Payload[startOffset:endOffset]))
		return nil
	}

	if !bytes.Equal(overlapBytes, p.Payload[startOffset:endOffset]) {
		log.Printf("injection attack detected at packet # %d with TCP.Seq %d\n", packetCount, p.TCP.Seq)
		log.Printf("len overlapBytes %d len Payload slice %d\n", len(overlapBytes), len(p.Payload[startOffset:endOffset]))
		log.Print("overlapBytes:")
		log.Print(hex.Dump(overlapBytes))
		log.Print("packet payload slice:")
		log.Print(hex.Dump(p.Payload[startOffset:endOffset]))

		e := &types.Event{
			Type:          eventType,
			PacketCount:   packetCount,
			Time:          time.Now(),
			Flow:          flow,
			Payload:       p.Payload,
			Overlap:       overlapBytes,
			StartSequence: start,
			EndSequence:   end,
			OverlapStart:  startOffset,
			OverlapEnd:    endOffset,
		}
		copy(e.Overlap, overlapBytes)

		return e
	} else {
		return nil
	}
}
func TestGetOverlapBytes(t *testing.T) {
	overlapBytesTests := []struct {
		in   reassemblyInput
		want TestOverlapBytesWant
	}{
		{ //0
			reassemblyInput{3, []byte{2, 3, 4}}, TestOverlapBytesWant{
				bytes:       []byte{6},
				startOffset: 2,
				endOffset:   3,
			},
		},
		{ //1
			reassemblyInput{4, []byte{2, 3, 4}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7},
				startOffset: 1,
				endOffset:   3,
			},
		},
		{ //2
			reassemblyInput{5, []byte{2, 3, 4}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8},
				startOffset: 0,
				endOffset:   3,
			},
		},
		{ //3
			reassemblyInput{6, []byte{1, 2, 3}}, TestOverlapBytesWant{
				bytes:       []byte{7, 8, 9},
				startOffset: 0,
				endOffset:   3,
			},
		},
		{ //4
			reassemblyInput{4, []byte{91, 92, 93, 94, 95, 96, 97}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11},
				startOffset: 1,
				endOffset:   7,
			},
		},
		{
			reassemblyInput{4, []byte{91, 92, 93, 94, 95, 96, 97, 98}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12},
				startOffset: 1,
				endOffset:   8,
			},
		},
		{
			reassemblyInput{4, []byte{91, 92, 93, 94, 95, 96, 97, 98, 99}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12, 13},
				startOffset: 1,
				endOffset:   9,
			},
		},
		{
			reassemblyInput{3, []byte{1, 2, 3, 4, 5, 6, 7}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10},
				startOffset: 2,
				endOffset:   7,
			},
		},
		{
			reassemblyInput{34, []byte{1, 2, 3, 4, 5, 6, 7}}, TestOverlapBytesWant{
				bytes:       []byte{35, 36, 37, 38, 39, 40},
				startOffset: 0,
				endOffset:   6,
			},
		},
		{
			reassemblyInput{34, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, TestOverlapBytesWant{
				bytes:       []byte{35, 36, 37, 38, 39, 40},
				startOffset: 0,
				endOffset:   6,
			},
		},
		{
			reassemblyInput{5, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
				startOffset: 0,
				endOffset:   35,
			},
		},

		{
			reassemblyInput{5, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
				startOffset: 0,
				endOffset:   35,
			},
		},

		{
			reassemblyInput{5, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
				startOffset: 0,
				endOffset:   35,
			},
		},

		{
			reassemblyInput{4, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
				startOffset: 1,
				endOffset:   36,
			},
		},

		{
			reassemblyInput{3, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
				startOffset: 2,
				endOffset:   37,
			},
		},

		{
			reassemblyInput{4, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, TestOverlapBytesWant{
				bytes:       []byte{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
				startOffset: 1,
				endOffset:   36,
			},
		},
	}

	options := ConnectionOptions{
		MaxBufferedPagesTotal:         0,
		MaxBufferedPagesPerConnection: 0,
		MaxRingPackets:                40,
		PageCache:                     nil,
		LogDir:                        "fake-log-dir",
	}

	f := &DefaultConnFactory{}
	conn := f.Build(options).(*Connection)

	for j := 5; j < 40; j += 5 {
		reassembly := types.Reassembly{
			Seq:   types.Sequence(j),
			Bytes: []byte{byte(j + 1), byte(j + 2), byte(j + 3), byte(j + 4), byte(j + 5)},
		}
		conn.ClientStreamRing.Reassembly = &reassembly
		conn.ClientStreamRing = conn.ClientStreamRing.Next()
	}
	for i := 0; i < len(overlapBytesTests); i++ {
		var startSeq uint32 = overlapBytesTests[i].in.Seq
		start := types.Sequence(startSeq)
		end := start.Add(len(overlapBytesTests[i].in.Payload) - 1)
		p := types.PacketManifest{
			IP: layers.IPv4{
				SrcIP:    net.IP{1, 2, 3, 4},
				DstIP:    net.IP{2, 3, 4, 5},
				Version:  4,
				TTL:      64,
				Protocol: layers.IPProtocolTCP,
			},
			TCP: layers.TCP{
				Seq:     startSeq,
				SrcPort: 1,
				DstPort: 2,
			},
			Payload: overlapBytesTests[i].in.Payload,
		}

		ipFlow, _ := gopacket.FlowFromEndpoints(layers.NewIPEndpoint(net.IPv4(1, 2, 3, 4)), layers.NewIPEndpoint(net.IPv4(2, 3, 4, 5)))
		tcpFlow, _ := gopacket.FlowFromEndpoints(layers.NewTCPPortEndpoint(layers.TCPPort(1)), layers.NewTCPPortEndpoint(layers.TCPPort(2)))
		serverFlow := types.NewTcpIpFlowFromFlows(ipFlow, tcpFlow)
		clientFlow := serverFlow.Reverse()
		conn.serverFlow = serverFlow
		conn.clientFlow = clientFlow

		head, tail := getOverlapRings(&p, serverFlow, conn.ClientStreamRing)
		if head == nil || tail == nil {
			t.Errorf("%d getOverlapRings returned a nil\n", i)
			t.Fail()
			continue
		}

		log.Printf("test #%d", i)
		overlapBytes, startOffset, endOffset := getOverlapBytes(head, tail, start, end)

		if startOffset != overlapBytesTests[i].want.startOffset {
			t.Errorf("test %d startOffset %d does not match want.startOffset %d\n", i, startOffset, overlapBytesTests[i].want.startOffset)
			t.Fail()
		}
		if endOffset != overlapBytesTests[i].want.endOffset {
			t.Errorf("test %d endOffset %d does not match want.endOffset %d\n", i, endOffset, overlapBytesTests[i].want.endOffset)
			t.Fail()
		}
		if len(overlapBytes) != len(overlapBytesTests[i].want.bytes) {
			t.Errorf("test %d overlapBytes len %d not equal to want.bytes len %d\n", i, len(overlapBytes), len(overlapBytesTests[i].want.bytes))
			t.Fail()
		}
		if !bytes.Equal(overlapBytes, overlapBytesTests[i].want.bytes) {
			t.Errorf("test %d overlapBytes %x not equal to want.bytes %x\n", i, overlapBytes, overlapBytesTests[i].want.bytes)
			t.Fail()
		}
	}
}
func TestInjectionDetector(t *testing.T) {
	log.Print("TestInjectionDetector")
	attackLogger := NewDummyAttackLogger()
	options := ConnectionOptions{
		MaxBufferedPagesTotal:         0,
		MaxBufferedPagesPerConnection: 0,
		MaxRingPackets:                40,
		PageCache:                     nil,
		LogDir:                        "fake-log-dir",
		AttackLogger:                  attackLogger,
	}

	f := &DefaultConnFactory{}
	conn := f.Build(options).(*Connection)
	reassembly := types.Reassembly{
		Seq:   types.Sequence(5),
		Bytes: []byte{1, 2, 3, 4, 5},
	}
	conn.ClientStreamRing.Reassembly = &reassembly
	conn.ClientStreamRing = conn.ClientStreamRing.Next()

	p := types.PacketManifest{
		IP: layers.IPv4{
			SrcIP:    net.IP{1, 2, 3, 4},
			DstIP:    net.IP{2, 3, 4, 5},
			Version:  4,
			TTL:      64,
			Protocol: layers.IPProtocolTCP,
		},
		TCP: layers.TCP{
			Seq:     7,
			SrcPort: 1,
			DstPort: 2,
		},
		Payload: []byte{1, 2, 3, 4, 5, 6, 7},
	}

	ipFlow, _ := gopacket.FlowFromEndpoints(layers.NewIPEndpoint(net.IPv4(1, 2, 3, 4)), layers.NewIPEndpoint(net.IPv4(2, 3, 4, 5)))
	tcpFlow, _ := gopacket.FlowFromEndpoints(layers.NewTCPPortEndpoint(layers.TCPPort(1)), layers.NewTCPPortEndpoint(layers.TCPPort(2)))
	serverFlow := types.NewTcpIpFlowFromFlows(ipFlow, tcpFlow)
	conn.serverFlow = serverFlow
	clientFlow := serverFlow.Reverse()
	conn.clientFlow = clientFlow
	conn.detectInjection(&p, serverFlow)

	if attackLogger.Count != 1 {
		t.Errorf("detectInjection failed; count == %d\n", attackLogger.Count)
		t.Fail()
	}

	// next test case
	p.TCP = layers.TCP{
		Seq:     7,
		SrcPort: 1,
		DstPort: 2,
	}
	p.Payload = []byte{3, 4, 5}
	conn.detectInjection(&p, serverFlow)
	if attackLogger.Count == 0 {
		t.Error("failed to detect injection\n")
		t.Fail()
	}

	// next test case
	attackLogger.Count = 0
	p.TCP = layers.TCP{
		Seq:     1,
		SrcPort: 1,
		DstPort: 2,
	}
	p.Payload = []byte{1, 2, 3, 4, 5, 6}
	conn.detectInjection(&p, serverFlow)
	if attackLogger.Count == 0 {
		t.Error("failed to detect injection\n")
		t.Fail()
	}

	// next test case
	attackLogger.Count = 0
	p.TCP = layers.TCP{
		Seq:     1,
		SrcPort: 1,
		DstPort: 2,
	}
	p.Payload = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17}
	conn.detectInjection(&p, serverFlow)
	if attackLogger.Count != 1 {
		t.Error("injection detection failure\n")
		t.Fail()
	}
}
func TestGetOverlapRings(t *testing.T) {
	overlapTests := []struct {
		in   reassemblyInput
		want []*types.Reassembly
	}{
		{
			reassemblyInput{7, []byte{1, 2}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 5,
				},
			},
		},
		{
			reassemblyInput{7, []byte{6, 7}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 5,
				},
			},
		},
		{
			reassemblyInput{5, []byte{1, 2, 3, 4, 5}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 5,
				},
			},
		},
		{
			reassemblyInput{5, []byte{1, 2, 3, 4, 5, 6}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 10,
				},
			},
		},
		{
			reassemblyInput{6, []byte{1, 2, 3, 4, 5}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 10,
				},
			},
		},
		{
			reassemblyInput{7, []byte{1, 2, 3, 4, 5}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 10,
				},
			},
		},
		{
			reassemblyInput{32, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, []*types.Reassembly{
				{
					Seq: 30,
				},
				{
					Seq: 35,
				},
			},
		},
		{
			reassemblyInput{0, []byte{1, 2, 3}}, []*types.Reassembly{
				nil,
				nil,
			},
		},
		{
			reassemblyInput{0, []byte{1, 2, 3, 4, 5, 6, 7, 8}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 5,
				},
			},
		},
		{
			reassemblyInput{0, []byte{1, 2, 3, 4, 5, 6}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 5,
				},
			},
		},
		{
			reassemblyInput{0, []byte{1, 2, 3}}, []*types.Reassembly{
				nil,
				nil,
			},
		},
		{
			reassemblyInput{0, []byte{1, 2, 3, 4, 5}}, []*types.Reassembly{
				nil,
				nil,
			},
		},
		{
			reassemblyInput{0, []byte{1, 2, 3, 4, 5, 6}}, []*types.Reassembly{
				{
					Seq: 5,
				},
				{
					Seq: 5,
				},
			},
		},
		{
			reassemblyInput{40, []byte{1}}, []*types.Reassembly{
				nil,
				nil,
			},
		},
		{
			reassemblyInput{42, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, []*types.Reassembly{
				nil,
				nil,
			},
		},
		{
			reassemblyInput{38, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, []*types.Reassembly{
				{
					Seq: 35,
				},
				{
					Seq: 35,
				},
			},
		},
	}

	ip := layers.IPv4{
		SrcIP:    net.IP{1, 2, 3, 4},
		DstIP:    net.IP{2, 3, 4, 5},
		Version:  4,
		TTL:      64,
		Protocol: layers.IPProtocolTCP,
	}

	options := ConnectionOptions{
		MaxBufferedPagesTotal:         0,
		MaxBufferedPagesPerConnection: 0,
		MaxRingPackets:                40,
		PageCache:                     nil,
		LogDir:                        "fake-log-dir",
	}

	f := &DefaultConnFactory{}
	conn := f.Build(options).(*Connection)

	for j := 5; j < 40; j += 5 {
		reassembly := types.Reassembly{
			Skip:  0,
			Seq:   types.Sequence(j),
			Bytes: []byte{1, 2, 3, 4, 5},
		}
		conn.ClientStreamRing.Reassembly = &reassembly
		conn.ClientStreamRing = conn.ClientStreamRing.Next()
	}

	for i := 0; i < len(overlapTests); i++ {
		log.Printf("test # %d", i)
		tcp := layers.TCP{
			Seq:     overlapTests[i].in.Seq,
			SYN:     false,
			SrcPort: 1,
			DstPort: 2,
		}
		p := types.PacketManifest{
			IP:      ip,
			TCP:     tcp,
			Payload: overlapTests[i].in.Payload,
		}

		ipFlow, _ := gopacket.FlowFromEndpoints(layers.NewIPEndpoint(net.IPv4(1, 2, 3, 4)), layers.NewIPEndpoint(net.IPv4(2, 3, 4, 5)))
		tcpFlow, _ := gopacket.FlowFromEndpoints(layers.NewTCPPortEndpoint(layers.TCPPort(1)), layers.NewTCPPortEndpoint(layers.TCPPort(2)))
		serverFlow := types.NewTcpIpFlowFromFlows(ipFlow, tcpFlow)
		clientFlow := serverFlow.Reverse()
		conn.serverFlow = serverFlow
		conn.clientFlow = clientFlow

		head, tail := getOverlapRings(&p, serverFlow, conn.ClientStreamRing)

		log.Printf("head %v tail %v", head, tail)

		log.Printf("want %v", overlapTests[i].want[0])

		if overlapTests[i].want[0] == nil {
			log.Print("want nil results")

			if head != nil {
				t.Error("getOverlapRings did not return a nil ring segment head\n")
				t.Fail()
			}
			if tail != nil {
				t.Error("getOverlapRings did not return a nil ring segment tail\n")
				t.Fail()
			}
		} else {
			if overlapTests[i].want[0] != nil {
				if head == nil || tail == nil {
					t.Error("head or tail is nil\n")
					t.Fail()
				}
			}
			if overlapTests[i].want[0] != nil {
				if head.Reassembly.Seq.Difference(overlapTests[i].want[0].Seq) != 0 {
					t.Errorf("test %d: reassembly.Seq %d != want.Seq %d\n", i, head.Reassembly.Seq, overlapTests[i].want[0].Seq)
					t.Fail()
				}
			}
			if overlapTests[i].want[1] != nil {
				if tail.Reassembly.Seq.Difference(overlapTests[i].want[1].Seq) != 0 {
					t.Errorf("test num %d in.Seq %d != want.Seq %d\n", i, head.Reassembly.Seq, overlapTests[i].want[1].Seq)
					t.Fail()
				}
			}
		}
	}
	return
}
Exemple #21
0
// stateDataTransfer is called by our TCP FSM and processes packets
// once we are in the TCP_DATA_TRANSFER state
func (c *Connection) stateDataTransfer(p *types.PacketManifest) {
	var closerState, remoteState *uint8
	var diff int
	isEnd := false

	if c.clientNextSeq == types.InvalidSequence && p.Flow.Equal(c.clientFlow) {
		c.clientNextSeq, isEnd = c.ServerCoalesce.insert(p, c.clientNextSeq)
		if isEnd {
			c.state = TCP_CLOSED
			c.closingFlow = p.Flow
			c.closingSeq = types.Sequence(p.TCP.Seq)
		}
		return
	} else if c.serverNextSeq == types.InvalidSequence && p.Flow.Equal(c.serverFlow) {
		c.serverNextSeq, isEnd = c.ClientCoalesce.insert(p, c.serverNextSeq)
		if isEnd {
			c.state = TCP_CLOSED
			c.closingFlow = p.Flow
			c.closingSeq = types.Sequence(p.TCP.Seq)
		}
		return
	}
	if c.packetCount < c.skipHijackDetectionCount {
		if c.DetectHijack {
			c.detectHijack(p, p.Flow)
		}
	}
	if p.Flow.Equal(c.clientFlow) {
		diff = c.clientNextSeq.Difference(types.Sequence(p.TCP.Seq))
		closerState = &c.clientState
		remoteState = &c.serverState
	} else if p.Flow.Equal(c.serverFlow) {
		diff = c.serverNextSeq.Difference(types.Sequence(p.TCP.Seq))
		closerState = &c.serverState
		remoteState = &c.clientState
	} else {
		panic("wtf")
	}

	// stream overlap case
	if diff < 0 {
		// ignore zero size packets
		if len(p.Payload) > 0 {
			if c.DetectInjection {
				c.detectInjection(p, p.Flow)
			}
		} else {
			// deal with strange packets here...
			// possibly RST or FIN
		}
	} else if diff == 0 { // contiguous
		if len(p.Payload) > 0 {
			reassembly := types.Reassembly{
				Seq:   types.Sequence(p.TCP.Seq),
				Bytes: []byte(p.Payload),
				Seen:  p.Timestamp,
			}
			if p.Flow.Equal(c.clientFlow) {
				c.ServerStreamRing.Reassembly = &reassembly
				c.ServerStreamRing = c.ServerStreamRing.Next()
				c.clientNextSeq = types.Sequence(p.TCP.Seq).Add(len(p.Payload))
				c.clientNextSeq, isEnd = c.ServerCoalesce.addContiguous(c.clientNextSeq)
				if isEnd {
					c.state = TCP_CLOSED
					return
				}
			} else {
				c.ClientStreamRing.Reassembly = &reassembly
				c.ClientStreamRing = c.ClientStreamRing.Next()
				c.serverNextSeq = types.Sequence(p.TCP.Seq).Add(len(p.Payload))
				c.serverNextSeq, isEnd = c.ClientCoalesce.addContiguous(c.serverNextSeq)
				if isEnd {
					c.state = TCP_CLOSED
					return
				}
			}
		}
		if p.TCP.RST {
			log.Print("got RST!\n")
			c.closingRST = true
			c.state = TCP_CLOSED
			c.closingFlow = p.Flow
			c.closingSeq = types.Sequence(p.TCP.Seq)
			return
		}
		if p.TCP.FIN {
			c.closingFIN = true
			c.closingFlow = p.Flow
			c.state = TCP_CONNECTION_CLOSING
			*closerState = TCP_FIN_WAIT1
			*remoteState = TCP_CLOSE_WAIT
			return
		}
	} else if diff > 0 { // future-out-of-order packet case
		if p.Flow.Equal(c.clientFlow) {
			c.clientNextSeq, isEnd = c.ServerCoalesce.insert(p, c.clientNextSeq)
		} else {
			c.serverNextSeq, isEnd = c.ClientCoalesce.insert(p, c.serverNextSeq)
		}
		if isEnd {
			c.state = TCP_CLOSED
			c.closingFlow = p.Flow
			c.closingSeq = types.Sequence(p.TCP.Seq)
		}
	}
}
Exemple #22
0
// stateTimeWait represents the TCP FSM's CLOSE-WAIT state
func (c *Connection) stateTimeWait(p *types.PacketManifest) {
	log.Print("TIME-WAIT: invalid protocol state\n")
	c.closingFlow = p.Flow
	c.closingSeq = types.Sequence(p.TCP.Seq)
}