keep track of knock sequences which are already used
This commit is contained in:
		@@ -11,21 +11,30 @@ import (
 | 
			
		||||
	"github.com/google/gopacket/pcap"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// type client is used to keept track of a client attempting to perform an authentic knock sequence
 | 
			
		||||
// Client is used to keep track of a client attempting to perform an authentic knock sequence
 | 
			
		||||
type Client struct {
 | 
			
		||||
	Progress int       // index of current progress in sequence.   Value of 1 means first port has been matched
 | 
			
		||||
	Sequence [4]uint16 // stores the knock sequence the current client is attempting.  It's set and tracked here to prevent race conditions during a knock sequence being received and key rotations
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KnockSequence is used keep track of an ordered knock sequence and whether it's been marked for use (to prevent replay attacks)
 | 
			
		||||
type KnockSequence struct {
 | 
			
		||||
	Used         bool
 | 
			
		||||
	PortSequence [4]uint16
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	clients       map[string]*Client // Contains a map of clients
 | 
			
		||||
	portSequences [][4]uint16
 | 
			
		||||
	sharedSecret  string // base32 encoded shared secret used for totp
 | 
			
		||||
	clients        map[string]*Client // Contains a map of clients
 | 
			
		||||
	knockSequences []KnockSequence
 | 
			
		||||
	sharedSecret   string // base32 encoded shared secret used for totp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// packetServer is the main function when operating in server mode
 | 
			
		||||
// it sets up the pcap on the capture deivce and starts a goroutine
 | 
			
		||||
// to rotate the knock sequence
 | 
			
		||||
func packetServer(captureDevice string) {
 | 
			
		||||
	clients = make(map[string]*Client, 0) // key is flow, value is the current progress through the sequence. i.e. value of 1 means that the first port in the sequence was successful
 | 
			
		||||
	portSequences = [][4]uint16{}         // Slice of accepted port sequences, there have to be several to account for clock skew between client and server
 | 
			
		||||
	knockSequences = []KnockSequence{}    // Slice of accepted port sequences, there have to be several to account for clock skew between client and server
 | 
			
		||||
 | 
			
		||||
	handle, err := pcap.OpenLive(captureDevice, 1600, true, pcap.BlockForever)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -45,9 +54,14 @@ func handlePacket(packet gopacket.Packet) {
 | 
			
		||||
	srcip := packet.NetworkLayer().NetworkFlow().Src().String()
 | 
			
		||||
	client, ok := clients[srcip]
 | 
			
		||||
	if !ok { // create the client, identify which authentic knock sequence is matched
 | 
			
		||||
		for _, sequence := range portSequences {
 | 
			
		||||
			if port == sequence[0] {
 | 
			
		||||
				clients[srcip] = &Client{Progress: 1, Sequence: sequence}
 | 
			
		||||
		for i, knockSequence := range knockSequences {
 | 
			
		||||
			if knockSequence.Used { // skip over sequences that are already used to prevent replay attack
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if port == knockSequence.PortSequence[0] {
 | 
			
		||||
				clients[srcip] = &Client{Progress: 1, Sequence: knockSequence.PortSequence}
 | 
			
		||||
				knockSequences[i].Used = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
@@ -74,18 +88,19 @@ func handlePacket(packet gopacket.Packet) {
 | 
			
		||||
func rotateSequence(handle *pcap.Handle) {
 | 
			
		||||
	for {
 | 
			
		||||
 | 
			
		||||
		if len(portSequences) < 3 {
 | 
			
		||||
			t := time.Now().Add(time.Second * -30)
 | 
			
		||||
			for i := 0; i < 3; i++ {
 | 
			
		||||
				portSequence, err := otphyp.GeneratePorts(sharedSecret, t.Add((time.Second * 30 * time.Duration(i))))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Fatalf("failed to generate port knock sequence: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				portSequences = append(portSequences, portSequence)
 | 
			
		||||
		// Generate new knock sequences with time skew support
 | 
			
		||||
		t := time.Now().Add(time.Second * -30)
 | 
			
		||||
		for i := len(knockSequences); i < 3; i++ {
 | 
			
		||||
			portSequence, err := otphyp.GeneratePorts(sharedSecret, t.Add((time.Second * 30 * time.Duration(i))))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Fatalf("failed to generate port knock sequence: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			knockSequence := KnockSequence{PortSequence: portSequence}
 | 
			
		||||
			knockSequences = append(knockSequences, knockSequence)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println("New sequences:", knockSequences)
 | 
			
		||||
 | 
			
		||||
		fmt.Println("New sequences:", portSequences)
 | 
			
		||||
		// Set BPF filter
 | 
			
		||||
		err := setPacketFilter(handle)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("failed to change packet filter: %v", err)
 | 
			
		||||
@@ -94,16 +109,16 @@ func rotateSequence(handle *pcap.Handle) {
 | 
			
		||||
		// Sleep until next 30 second offset
 | 
			
		||||
		time.Sleep(time.Until(time.Now().Truncate(time.Second * 30).Add(time.Second * 30)))
 | 
			
		||||
 | 
			
		||||
		// TBD: pop first value and only generate latest (time.Now().Add(time.Second*30)) value instead of re-initializing completely
 | 
			
		||||
		portSequences = [][4]uint16{}
 | 
			
		||||
		// pop first value, next iteration pushes new value
 | 
			
		||||
		knockSequences = knockSequences[1:]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given a pcap handle and list of authentic port knock sequences, configures a BPF filter
 | 
			
		||||
func setPacketFilter(handle *pcap.Handle) error {
 | 
			
		||||
	filter := "udp && ("
 | 
			
		||||
	for i, portSequence := range portSequences {
 | 
			
		||||
		for j, port := range portSequence {
 | 
			
		||||
	for i, knockSequence := range knockSequences {
 | 
			
		||||
		for j, port := range knockSequence.PortSequence {
 | 
			
		||||
			if i == 0 && j == 0 {
 | 
			
		||||
				filter += fmt.Sprint("port ", port)
 | 
			
		||||
			} else {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user