// 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()) }
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 }
// 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 }
// 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() }
// 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 }
// 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 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 }
// 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) }
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 } }
// 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) } } }