diff --git a/modules/net_probe.go b/modules/net_probe.go index a899fbb2..d668b992 100644 --- a/modules/net_probe.go +++ b/modules/net_probe.go @@ -60,7 +60,7 @@ func (p *Prober) sendProbe(from net.IP, from_hw net.HardwareAddr, ip net.IP) { wg.Add(1) go func(w *sync.WaitGroup) { - p.sendProbeUDP(from, from_hw, ip) + p.sendProbeNBNS(from, from_hw, ip) w.Done() }(&wg) @@ -110,6 +110,9 @@ func (p *Prober) Start() error { } else if p.Session.Skip(ip) { log.Debug("Skipping address %s from UDP probing.", ip) continue + } else if p.Session.Lan.GetByIp(ip.String()) != nil { + log.Debug("Skipping address %s from UDP probing (already in the arp cache).", ip) + continue } p.sendProbe(from, from_hw, ip) diff --git a/modules/net_probe_mdns.go b/modules/net_probe_mdns.go index cdccca36..14af0e2d 100644 --- a/modules/net_probe_mdns.go +++ b/modules/net_probe_mdns.go @@ -12,10 +12,9 @@ func (p *Prober) sendProbeMDNS(from net.IP, from_hw net.HardwareAddr) { if err != nil { log.Error("error while sending mdns probe: %v", err) return - } - - log.Debug("sending %d bytes of mdns probe query", len(raw)) - if err := p.Session.Queue.Send(raw); err != nil { + } else if err := p.Session.Queue.Send(raw); err != nil { log.Error("error sending mdns packet: %s", err) + } else { + log.Debug("sent %d bytes of MDNS probe", len(raw)) } } diff --git a/modules/net_probe_nbns.go b/modules/net_probe_nbns.go new file mode 100644 index 00000000..885353e7 --- /dev/null +++ b/modules/net_probe_nbns.go @@ -0,0 +1,25 @@ +package modules + +import ( + "fmt" + "net" + + "github.com/bettercap/bettercap/log" + "github.com/bettercap/bettercap/packets" +) + +func (p *Prober) sendProbeNBNS(from net.IP, from_hw net.HardwareAddr, ip net.IP) { + name := fmt.Sprintf("%s:%d", ip, packets.NBNSPort) + if addr, err := net.ResolveUDPAddr("udp", name); err != nil { + log.Debug("could not resolve %s.", name) + } else if con, err := net.DialUDP("udp", nil, addr); err != nil { + log.Debug("could not dial %s.", name) + } else { + defer con.Close() + if wrote, _ := con.Write(packets.NBNSRequest); wrote > 0 { + p.Session.Queue.TrackSent(uint64(wrote)) + } else { + p.Session.Queue.TrackError() + } + } +} diff --git a/modules/net_probe_udp.go b/modules/net_probe_udp.go deleted file mode 100644 index 6e76b289..00000000 --- a/modules/net_probe_udp.go +++ /dev/null @@ -1,52 +0,0 @@ -package modules - -import ( - "fmt" - "net" - - "github.com/bettercap/bettercap/log" -) - -// NBNS port -const NBNSPort = 137 - -// NBNS hostname resolution request buffer. -var NBNSRequest = []byte{ - 0x82, 0x28, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x20, 0x43, 0x4B, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x0, - 0x0, 0x21, 0x0, 0x1, -} - -func (p *Prober) sendProbeUDP(from net.IP, from_hw net.HardwareAddr, ip net.IP) { - name := fmt.Sprintf("%s:%d", ip, NBNSPort) - if addr, err := net.ResolveUDPAddr("udp", name); err != nil { - log.Debug("could not resolve %s.", name) - } else if con, err := net.DialUDP("udp", nil, addr); err != nil { - log.Debug("could not dial %s.", name) - } else { - log.Debug("udp connection to %s enstablished.", name) - - buffer := make([]byte, 0xff) - defer con.Close() - wrote, _ := con.Write(NBNSRequest) - - log.Info("wrote %d bytes", len(NBNSRequest)) - - read, _, _ := con.ReadFrom(buffer) - - log.Info("got %d bytes of buffer", len(buffer)) - - if wrote > 0 { - p.Session.Queue.TrackSent(uint64(wrote)) - } else { - p.Session.Queue.TrackError() - } - - if read > 0 { - p.Session.Queue.TrackPacket(uint64(read)) - } - } -} diff --git a/packets/mdns.go b/packets/mdns.go index a825f3f6..04798274 100644 --- a/packets/mdns.go +++ b/packets/mdns.go @@ -18,54 +18,41 @@ var ( ) func MDNSGetMeta(pkt gopacket.Packet) map[string]string { + meta := make(map[string]string) + if ludp := pkt.Layer(layers.LayerTypeUDP); ludp != nil { if udp := ludp.(*layers.UDP); udp != nil && udp.SrcPort == MDNSPort && udp.DstPort == MDNSPort { dns := layers.DNS{} if err := dns.DecodeFromBytes(udp.Payload, gopacket.NilDecodeFeedback); err == nil { answers := append(dns.Answers, dns.Additionals...) answers = append(answers, dns.Authorities...) + for _, answer := range answers { switch answer.Type { + case layers.DNSTypePTR: + case layers.DNSTypeA: + case layers.DNSTypeAAAA: + meta["mdns:hostname"] = string(answer.Name) + case layers.DNSTypeTXT: - meta := make(map[string]string) for _, raw := range answer.TXTs { if value := string(raw); strings.Contains(value, "=") { parts := strings.SplitN(value, "=", 2) meta[core.Trim(parts[0])] = core.Trim(parts[1]) } } - if len(meta) > 0 { - return meta - } } } } } } + + if len(meta) > 0 { + return meta + } return nil } -func MDNSGetHostname(pkt gopacket.Packet) string { - if ludp := pkt.Layer(layers.LayerTypeUDP); ludp != nil { - if udp := ludp.(*layers.UDP); udp != nil && udp.SrcPort == MDNSPort && udp.DstPort == MDNSPort { - dns := layers.DNS{} - if err := dns.DecodeFromBytes(udp.Payload, gopacket.NilDecodeFeedback); err == nil { - answers := append(dns.Answers, dns.Additionals...) - answers = append(answers, dns.Authorities...) - for _, answer := range answers { - switch answer.Type { - case layers.DNSTypePTR: - case layers.DNSTypeA: - case layers.DNSTypeAAAA: - return string(answer.Name) - } - } - } - } - } - return "" -} - func NewMDNSProbe(from net.IP, from_hw net.HardwareAddr) (error, []byte) { eth := layers.Ethernet{ SrcMAC: from_hw, diff --git a/packets/nbns.go b/packets/nbns.go new file mode 100644 index 00000000..677e83e3 --- /dev/null +++ b/packets/nbns.go @@ -0,0 +1,36 @@ +package packets + +import ( + "github.com/bettercap/bettercap/core" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +const ( + NBNSPort = 137 + NBNSMinRespSize = 73 +) + +var ( + // NBNS hostname resolution request buffer. + NBNSRequest = []byte{ + 0x82, 0x28, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x20, 0x43, 0x4B, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x0, + 0x0, 0x21, 0x0, 0x1, + } +) + +func NBNSGetMeta(pkt gopacket.Packet) map[string]string { + if ludp := pkt.Layer(layers.LayerTypeUDP); ludp != nil { + if udp := ludp.(*layers.UDP); udp != nil && udp.SrcPort == NBNSPort && len(udp.Payload) >= NBNSMinRespSize { + return map[string]string{ + "nbns:hostname": core.Trim(string(udp.Payload[57:72])), + } + } + } + return nil +} diff --git a/packets/queue.go b/packets/queue.go index 1a617ba9..6982c8b8 100644 --- a/packets/queue.go +++ b/packets/queue.go @@ -13,11 +13,10 @@ import ( ) type Activity struct { - IP net.IP - MAC net.HardwareAddr - Hostname string - Meta map[string]string - Source bool + IP net.IP + MAC net.HardwareAddr + Meta map[string]string + Source bool } type Traffic struct { @@ -114,14 +113,13 @@ func (q *Queue) trackProtocols(pkt gopacket.Packet) { } } -func (q *Queue) trackActivity(eth *layers.Ethernet, ip4 *layers.IPv4, address net.IP, hostname string, meta map[string]string, pktSize uint64, isSent bool) { +func (q *Queue) trackActivity(eth *layers.Ethernet, ip4 *layers.IPv4, address net.IP, meta map[string]string, pktSize uint64, isSent bool) { // push to activity channel q.Activities <- Activity{ - IP: address, - MAC: eth.SrcMAC, - Hostname: hostname, - Meta: meta, - Source: isSent, + IP: address, + MAC: eth.SrcMAC, + Meta: meta, + Source: isSent, } q.Lock() @@ -166,6 +164,36 @@ func (q *Queue) TrackError() { q.Stats.Errors++ } +func (q *Queue) getPacketMeta(pkt gopacket.Packet) map[string]string { + meta := make(map[string]string) + mdns := MDNSGetMeta(pkt) + nbns := NBNSGetMeta(pkt) + + if mdns != nil { + for k, v := range mdns { + meta[k] = v + } + } + + if nbns != nil { + for k, v := range nbns { + meta[k] = v + } + } + /* + // check if the packet is a useful mDNS query response + hostname := MDNSGetHostname(pkt) + // check if we can get some meta info from mDNS TXT records + meta := MDNSGetMeta(pkt) + + if hostname != "" { + meta["mdns:hostname"] = hostname + } + */ + + return meta +} + func (q *Queue) worker() { for pkt := range q.srcChannel { if !q.active { @@ -194,19 +222,16 @@ func (q *Queue) worker() { isFromMe := q.iface.IP.Equal(ip4.SrcIP) isFromLAN := q.iface.Net.Contains(ip4.SrcIP) if !isFromMe && isFromLAN { - // check if the packet is a useful mDNS query response - hostname := MDNSGetHostname(pkt) - // check if we can get some meta info from mDNS TXT records - meta := MDNSGetMeta(pkt) + meta := q.getPacketMeta(pkt) - q.trackActivity(eth, ip4, ip4.SrcIP, hostname, meta, pktSize, true) + q.trackActivity(eth, ip4, ip4.SrcIP, meta, pktSize, true) } // something going to someone on the LAN isToMe := q.iface.IP.Equal(ip4.DstIP) isToLAN := q.iface.Net.Contains(ip4.DstIP) if !isToMe && isToLAN { - q.trackActivity(eth, ip4, ip4.DstIP, "", nil, pktSize, false) + q.trackActivity(eth, ip4, ip4.DstIP, nil, pktSize, false) } } } diff --git a/session/session_setup.go b/session/session_setup.go index ab001d17..3a13ee32 100644 --- a/session/session_setup.go +++ b/session/session_setup.go @@ -94,14 +94,12 @@ func (s *Session) startNetMon() { existing, _ = s.Lan.Get(mac) } - if existing != nil { - if existing.Hostname == "" && event.Hostname != "" { - existing.Hostname = event.Hostname - } - if event.Meta != nil { - for k, v := range event.Meta { - existing.Meta.Set(k, v) + if existing != nil && event.Meta != nil { + for k, v := range event.Meta { + if strings.HasSuffix(k, ":hostname") && existing.Hostname == "" { + existing.Hostname = v } + existing.Meta.Set(k, v) } } }