From 37afbb58a9dc29561b3999a33facd81b24ec7043 Mon Sep 17 00:00:00 2001 From: maride Date: Thu, 28 Nov 2019 17:16:22 +0100 Subject: [PATCH] Add support for DHCPv4 packets --- ethernet/dhcpv4/common.go | 30 +++++++++ ethernet/dhcpv4/dhcp.go | 109 ++++++++++++++++++++++++++++++++ ethernet/dhcpv4/dhcpResponse.go | 8 +++ ethernet/ethernet.go | 7 ++ 4 files changed, 154 insertions(+) create mode 100644 ethernet/dhcpv4/common.go create mode 100644 ethernet/dhcpv4/dhcp.go create mode 100644 ethernet/dhcpv4/dhcpResponse.go diff --git a/ethernet/dhcpv4/common.go b/ethernet/dhcpv4/common.go new file mode 100644 index 0000000..f50319d --- /dev/null +++ b/ethernet/dhcpv4/common.go @@ -0,0 +1,30 @@ +package dhcpv4 + +import "fmt" + +// Appends the appendee to the array if it does not contain appendee yet +func appendIfUnique(appendee string, array []string) []string { + // Iterate over all elements and check values + for _, elem := range array { + if elem == appendee { + // ... found. Stop here + return array + } + } + + // None found, append + return append(array, appendee) +} + +// Prints each element, along with a small ASCII tree +func printTree(strarr []string) { + // iterate over each element + for iter, elem := range strarr { + // check if we got the last element + if iter < len(strarr) - 1 { + fmt.Printf("|- %s\n", elem) + } else { + fmt.Printf("'- %s\n\n", elem) + } + } +} diff --git a/ethernet/dhcpv4/dhcp.go b/ethernet/dhcpv4/dhcp.go new file mode 100644 index 0000000..d7eee46 --- /dev/null +++ b/ethernet/dhcpv4/dhcp.go @@ -0,0 +1,109 @@ +package dhcpv4 + +import ( + "fmt" + "github.com/fatih/color" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "log" +) + +var ( + requestMAC []string + responses []dhcpResponse +) + +// Called on every DHCP (v4) packet +func HandleDHCPv4Packet(packet gopacket.Packet) error { + var dhcppacket layers.DHCPv4 + var ethernetpacket layers.Ethernet + + // Decode raw packet into DHCPv4 + decodeDHCPErr := dhcppacket.DecodeFromBytes(packet.Layer(layers.LayerTypeDHCPv4).LayerContents(), gopacket.NilDecodeFeedback) + if decodeDHCPErr != nil { + // Encountered an error during decoding, most likely a broken packet + return decodeDHCPErr + } + + // And decode raw packet into Ethernet + decodeEthernetErr := ethernetpacket.DecodeFromBytes(packet.Layer(layers.LayerTypeEthernet).LayerContents(), gopacket.NilDecodeFeedback) + if decodeEthernetErr != nil { + // Encountered an error during decoding, most likely a broken packet + return decodeEthernetErr + } + + // Examine packet further + if dhcppacket.Operation == layers.DHCPOpRequest { + // Request packet + appendIfUnique(dhcppacket.ClientHWAddr.String(), requestMAC) + } else { + // Response/Offer packet + addResponseEntry(dhcppacket.ClientIP.String(), dhcppacket.ClientHWAddr.String(), ethernetpacket.SrcMAC.String()) + } + + return nil +} + +// Print summary after all packets are processed +func PrintDHCPv4Summary() { + headline := color.New(color.FgRed, color.Bold) + headline.Println("DHCP Requests") + printRequestSummary() + headline.Println("DHCP Responses/Offers") + printResponseSummary() +} + +// Prints the summary of all DHCP request packets +func printRequestSummary() { + fmt.Printf("%d unique DHCP requests\n", len(requestMAC)) + printTree(requestMAC) +} + +// Prints the summary of all DHCP offer packets +func printResponseSummary() { + var tmpaddr []string + + // Iterate over all responses + for _, r := range responses { + tmpaddr = append(tmpaddr, fmt.Sprintf("%s offered %s IP address %s", r.serverMACAddr, r.destMACAddr, r.newIPAddr)) + } + + // Draw as tree + printTree(tmpaddr) +} + +// Adds a new response entry. If an IP address was already issued or a MAC asks multiple times for DNS, the case is examined further +func addResponseEntry(newIP string, destMAC string, serverMAC string) { + for _, r := range responses { + // Check for interesting cases + if r.destMACAddr == destMAC { + // The same client device received multiple IP addresses, let's examine further + if r.newIPAddr == newIP { + // the handed IP is the same - this is ok, just badly configured + if r.serverMACAddr == serverMAC { + // Same DHCP server answered. + log.Printf("MAC address %s received the same IP address multiple times via DHCP by the same server.", destMAC) + } else { + // Different DHCP servers answered, but with the same address - strange network, but ok... + log.Printf("MAC address %s received the same IP address multiple times via DHCP by different servers.", destMAC) + } + } else { + // far more interesting - one client received multiple addresses + if r.serverMACAddr == serverMAC { + // Same DHCP server answered. + log.Printf("MAC address %s received different IP addresses (%s, %s) multiple times via DHCP by the same server.", destMAC, r.newIPAddr, newIP) + } else { + // Different DHCP servers answered, with different addresses - possibly an attempt to build up MitM + log.Printf("MAC address %s received different IP addresses (%s, %s) multiple times via DHCP by different servers (%s, %s).", destMAC, r.newIPAddr, newIP, r.serverMACAddr, serverMAC) + } + } + } + } + + // Add a response entry - even if we found some "strange" behavior before. + responses = append(responses, dhcpResponse{ + destMACAddr: destMAC, + newIPAddr: newIP, + serverMACAddr: serverMAC, + }) +} \ No newline at end of file diff --git a/ethernet/dhcpv4/dhcpResponse.go b/ethernet/dhcpv4/dhcpResponse.go new file mode 100644 index 0000000..b4cc2e5 --- /dev/null +++ b/ethernet/dhcpv4/dhcpResponse.go @@ -0,0 +1,8 @@ +package dhcpv4 + +type dhcpResponse struct { + destMACAddr string + newIPAddr string + serverMACAddr string +} + diff --git a/ethernet/ethernet.go b/ethernet/ethernet.go index f7fcc22..afb9863 100644 --- a/ethernet/ethernet.go +++ b/ethernet/ethernet.go @@ -3,6 +3,7 @@ package ethernet import ( "git.darknebu.la/maride/pancap/ethernet/arp" "git.darknebu.la/maride/pancap/ethernet/dns" + "git.darknebu.la/maride/pancap/ethernet/dhcpv4" "github.com/google/gopacket" "github.com/google/gopacket/layers" "log" @@ -30,6 +31,11 @@ func Analyze(source *gopacket.PacketSource) error { // Handle ARP packet arp.ProcessARPPacket(packet) } + + if packet.Layer(layers.LayerTypeDHCPv4) != nil { + // Handle DHCP (v4) packet + dhcpv4.HandleDHCPv4Packet(packet) + } } // After processing all packets, print summary @@ -42,4 +48,5 @@ func Analyze(source *gopacket.PacketSource) error { func printSummary() { arp.PrintARPSummary() dns.PrintDNSSummary() + dhcpv4.PrintDHCPv4Summary() }