Compare commits

..

No commits in common. "bd7fff97b3251ec1ad6fa6e253f564fdcd40dcb1" and "b95f764fc99b0dc3c128f0ef2c5feb450b61b5af" have entirely different histories.

7 changed files with 24 additions and 50 deletions

View File

@ -1,5 +1,5 @@
module deadbeef.codes/steven/hyp module deadbeef.codes/steven/hyp-client
go 1.22.0 go 1.22.0
require deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407135923-27c2f284299f require deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee

View File

@ -1,4 +1,2 @@
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee h1:r9xdoIGPhc/tXzyOj+lCDb+ReYa/zkysqK3ZRCqpyIU= deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee h1:r9xdoIGPhc/tXzyOj+lCDb+ReYa/zkysqK3ZRCqpyIU=
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee/go.mod h1:NANpGD/K+nDkW+bkomchwaXMrsXWza58+8AR7Sau3fs= deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee/go.mod h1:NANpGD/K+nDkW+bkomchwaXMrsXWza58+8AR7Sau3fs=
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407135923-27c2f284299f h1:M+QxtB9hQ/+9MmvPtqOvXSL+LGU/f54VlPZHsM4Knao=
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407135923-27c2f284299f/go.mod h1:NANpGD/K+nDkW+bkomchwaXMrsXWza58+8AR7Sau3fs=

View File

@ -45,16 +45,7 @@ func main() {
} }
func usage() { func usage() {
fmt.Print(`hyp <server> fmt.Println(os.Args[0], "usage")
fmt.Println("Supply an ordered list of ports to knock")
Example Usage: fmt.Println(os.Args[0], "server")
# Transmit an authentic knock sequence to a server
hyp 10.69.4.20
# You can use a DNS name too
hyp hyp.stevenpolley.net
`)
os.Exit(1)
} }

View File

@ -3,7 +3,7 @@ module deadbeef.codes/steven/hypd
go 1.22.0 go 1.22.0
require ( require (
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407135923-27c2f284299f deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
) )

View File

@ -1,7 +1,5 @@
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee h1:r9xdoIGPhc/tXzyOj+lCDb+ReYa/zkysqK3ZRCqpyIU= deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee h1:r9xdoIGPhc/tXzyOj+lCDb+ReYa/zkysqK3ZRCqpyIU=
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee/go.mod h1:NANpGD/K+nDkW+bkomchwaXMrsXWza58+8AR7Sau3fs= deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407035202-7ccdf4d89fee/go.mod h1:NANpGD/K+nDkW+bkomchwaXMrsXWza58+8AR7Sau3fs=
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407135923-27c2f284299f h1:M+QxtB9hQ/+9MmvPtqOvXSL+LGU/f54VlPZHsM4Knao=
deadbeef.codes/steven/hyp/otphyp v0.0.0-20240407135923-27c2f284299f/go.mod h1:NANpGD/K+nDkW+bkomchwaXMrsXWza58+8AR7Sau3fs=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

View File

@ -41,35 +41,28 @@ func main() {
usage() usage()
} }
packetServer(os.Args[2]) packetServer(os.Args[2])
default:
usage()
} }
} }
func usage() { func usage() {
fmt.Print(`hypd <command> fmt.Print(`hyp <command>
Commands: Commands:
generatesecret - creates a pre shared key file named hyp.secret which can be distributed to a trusted client generatesecret - creates a pre shared key file named hyp.secret which can be distributed to a trusted client
server <device> - runs the hypd server watching for an authentic knock sequence server <device> - TBD
Example Usage: Example Usage:
# Generate a secret, to be shared with a trusted client # Linux
hypd generatesecret hyp server "/dev/eth0"
# Linux - ip link # Windows - get-netadapter | where {$_.Name -eq Ethernet} | Select-Object -Property DeviceName
hypd server eth0 hyp server "\\Device\\NPF_{A066F7DE-CC2D-4E4B-97C4-BF0EC4C03649}"
# Windows - get-netadapter | where {$_.Name -eq Ethernet} | Select-Object -Property DeviceName
hypd server "\\Device\\NPF_{A066F7DE-CC2D-4E4B-97C4-BF0EC4C03649}"
`) `)
os.Exit(1) os.Exit(1)
} }
// TBD: Implement - this is a temporary routine to demonstrate an application // TBD: Implement
func handleSuccess(srcip string) { func handleSuccess(srcip string) {
fmt.Println("Success for ", srcip) fmt.Println("Success for ", srcip)

View File

@ -19,34 +19,30 @@ type Client struct {
// KnockSequence is used keep track of an ordered knock sequence and whether it's been marked for use (to prevent replay attacks) // 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 { type KnockSequence struct {
Used bool // If true, that means this knock sequence has already been used once. It may still be within the valid time window, but it can't be used again Used bool
PortSequence [4]uint16 // Each knock sequence is four ports long PortSequence [4]uint16
} }
var ( var (
clients map[string]*Client // Contains a map of clients clients map[string]*Client // Contains a map of clients
knockSequences []KnockSequence // We have 3 valid knock sequences at any time to account for clock skew knockSequences []KnockSequence
sharedSecret string // base32 encoded shared secret used for totp sharedSecret string // base32 encoded shared secret used for totp
) )
// packetServer is the main function when operating in server mode // packetServer is the main function when operating in server mode
// it sets up the pcap on the capture device and starts a goroutine // it sets up the pcap on the capture deivce and starts a goroutine
// to rotate the knock sequence // to rotate the knock sequence
func packetServer(captureDevice string) { func packetServer(captureDevice string) {
clients = make(map[string]*Client, 0) // key is source IP address, value is the current progress through the sequence. i.e. value of 1 means that the first port in the sequence was successful 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
knockSequences = []KnockSequence{} // 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
// Open pcap handle on device
handle, err := pcap.OpenLive(captureDevice, 1600, true, pcap.BlockForever) handle, err := pcap.OpenLive(captureDevice, 1600, true, pcap.BlockForever)
if err != nil { if err != nil {
log.Fatalf("failed to open adapter") log.Fatalf("failed to open adapter")
} }
packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
// Setup a goroutine to periodically rotate the authentic knock sequence
go rotateSequence(handle) go rotateSequence(handle)
// Read from the pcap handle until we exit
for packet := range packetSource.Packets() { for packet := range packetSource.Packets() {
handlePacket(packet) // Do something with a packet here. handlePacket(packet) // Do something with a packet here.
} }
@ -56,15 +52,14 @@ func packetServer(captureDevice string) {
func handlePacket(packet gopacket.Packet) { func handlePacket(packet gopacket.Packet) {
port := binary.BigEndian.Uint16(packet.TransportLayer().TransportFlow().Dst().Raw()) port := binary.BigEndian.Uint16(packet.TransportLayer().TransportFlow().Dst().Raw())
srcip := packet.NetworkLayer().NetworkFlow().Src().String() srcip := packet.NetworkLayer().NetworkFlow().Src().String()
client, ok := clients[srcip] client, ok := clients[srcip]
if !ok { // client doesn't exist yet if !ok { // create the client, identify which authentic knock sequence is matched
for i, knockSequence := range knockSequences { // identify which of the 3 authentic knock sequences is matched for i, knockSequence := range knockSequences {
if knockSequence.Used { // skip over sequences that are already used to prevent replay attack if knockSequence.Used { // skip over sequences that are already used to prevent replay attack
continue continue
} }
if port == knockSequence.PortSequence[0] { if port == knockSequence.PortSequence[0] {
// Create the client and mark the knock sequence as used
clients[srcip] = &Client{Progress: 1, Sequence: knockSequence.PortSequence} clients[srcip] = &Client{Progress: 1, Sequence: knockSequence.PortSequence}
knockSequences[i].Used = true knockSequences[i].Used = true
} }
@ -74,7 +69,6 @@ func handlePacket(packet gopacket.Packet) {
// if it's wrong, reset progress // if it's wrong, reset progress
// TBD: vulnerable to sweep attack - this won't be triggered if a wrong packet doesn't match BPF filter // TBD: vulnerable to sweep attack - this won't be triggered if a wrong packet doesn't match BPF filter
// TBD: make the sweep attack fix on by default, but configurable to be off to allow for limited BPF filter for extremely low overhead as compromise.
if port != client.Sequence[client.Progress] { if port != client.Sequence[client.Progress] {
delete(clients, srcip) delete(clients, srcip)
fmt.Printf("port '%d' is in sequence, but came at unexpected order - resetting progress", port) fmt.Printf("port '%d' is in sequence, but came at unexpected order - resetting progress", port)
@ -85,7 +79,7 @@ func handlePacket(packet gopacket.Packet) {
client.Progress++ client.Progress++
if client.Progress >= len(client.Sequence) { if client.Progress >= len(client.Sequence) {
delete(clients, srcip) delete(clients, srcip)
handleSuccess(srcip) // The magic function, the knock is completed handleSuccess(srcip)
return return
} }
} }