mirror of
https://github.com/bettercap/bettercap
synced 2025-07-07 13:32:07 -07:00
new: net.probe on uses both NBNS and MDNS queries to fetch endpoints metadata and hostnames
This commit is contained in:
parent
36999813c4
commit
e2cc4574c4
8 changed files with 127 additions and 106 deletions
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
25
modules/net_probe_nbns.go
Normal file
25
modules/net_probe_nbns.go
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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,
|
||||
|
|
36
packets/nbns.go
Normal file
36
packets/nbns.go
Normal file
|
@ -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
|
||||
}
|
|
@ -15,7 +15,6 @@ import (
|
|||
type Activity struct {
|
||||
IP net.IP
|
||||
MAC net.HardwareAddr
|
||||
Hostname string
|
||||
Meta map[string]string
|
||||
Source bool
|
||||
}
|
||||
|
@ -114,12 +113,11 @@ 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,
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
if existing != nil && event.Meta != nil {
|
||||
for k, v := range event.Meta {
|
||||
existing.Meta.Set(k, v)
|
||||
if strings.HasSuffix(k, ":hostname") && existing.Hostname == "" {
|
||||
existing.Hostname = v
|
||||
}
|
||||
existing.Meta.Set(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue