diff --git a/modules/dhcp6_spoof.go b/modules/dhcp6_spoof.go index b1fbb358..489cd6ea 100644 --- a/modules/dhcp6_spoof.go +++ b/modules/dhcp6_spoof.go @@ -154,7 +154,7 @@ func (s *DHCP6Spoofer) dhcpAdvertise(pkt gopacket.Packet, solicit dhcp6.Packet, } var ip net.IP - if t, found := s.Session.Targets.Targets[target.String()]; found == true { + if t, found := s.Session.Lan.Hosts[target.String()]; found == true { ip = t.IP } else { log.Warning("Address %s not known, using random identity association address.", target.String()) @@ -312,7 +312,7 @@ func (s *DHCP6Spoofer) dhcpReply(toType string, pkt gopacket.Packet, req dhcp6.P addr = net.IP(raw[0]) } - if t, found := s.Session.Targets.Targets[target.String()]; found == true { + if t, found := s.Session.Lan.Hosts[target.String()]; found == true { log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), t) } else { log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), target) diff --git a/modules/dns_spoof.go b/modules/dns_spoof.go index 15d2a880..54cfd43a 100644 --- a/modules/dns_spoof.go +++ b/modules/dns_spoof.go @@ -111,7 +111,7 @@ func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp * redir := fmt.Sprintf("(->%s)", s.Address) who := target.String() - if t, found := s.Session.Targets.Targets[target.String()]; found == true { + if t, found := s.Session.Lan.Hosts[target.String()]; found == true { who = t.String() } diff --git a/modules/net_recon.go b/modules/net_recon.go index c6679110..26e15bca 100644 --- a/modules/net_recon.go +++ b/modules/net_recon.go @@ -71,19 +71,19 @@ func (d Discovery) Author() string { func (d *Discovery) runDiff(cache network.ArpTable) { // check for endpoints who disappeared var rem network.ArpTable = make(network.ArpTable) - for mac, t := range d.Session.Targets.Targets { + for mac, t := range d.Session.Lan.Hosts { if _, found := cache[mac]; found == false { rem[mac] = t.IpAddress } } for mac, ip := range rem { - d.Session.Targets.Remove(ip, mac) + d.Session.Lan.Remove(ip, mac) } // now check for new friends ^_^ for ip, mac := range cache { - d.Session.Targets.AddIfNew(ip, mac) + d.Session.Lan.AddIfNew(ip, mac) } } diff --git a/modules/net_recon_show.go b/modules/net_recon_show.go index a265aa81..014b8864 100644 --- a/modules/net_recon_show.go +++ b/modules/net_recon_show.go @@ -53,7 +53,7 @@ func (d *Discovery) getRow(e *network.Endpoint) []string { addr := e.IpAddress mac := e.HwAddress - if d.Session.Targets.WasMissed(e.HwAddress) == true { + if d.Session.Lan.WasMissed(e.HwAddress) == true { // if endpoint was not found in ARP at least once addr = core.Dim(addr) mac = core.Dim(mac) @@ -113,7 +113,7 @@ func (d *Discovery) showTable(header []string, rows [][]string) { } func (d *Discovery) Show(by string) error { - targets := d.Session.Targets.List() + targets := d.Session.Lan.List() if by == "seen" { sort.Sort(BySeenSorter(targets)) } else if by == "sent" { diff --git a/modules/net_sniff_views.go b/modules/net_sniff_views.go index 4321bfc0..7c471f9c 100644 --- a/modules/net_sniff_views.go +++ b/modules/net_sniff_views.go @@ -23,9 +23,9 @@ func vIP(ip net.IP) string { } address := ip.String() - host, found := session.I.Targets.Targets[address] + host := session.I.Lan.Get(address) - if found == true { + if host != nil { if host.Hostname != "" { return host.Hostname } diff --git a/network/lan.go b/network/lan.go new file mode 100644 index 00000000..92a06de0 --- /dev/null +++ b/network/lan.go @@ -0,0 +1,214 @@ +package network + +import ( + "bufio" + "fmt" + "io/ioutil" + "net" + "os" + "strings" + "sync" + + "github.com/evilsocket/bettercap-ng/core" +) + +const LANDefaultTTL = 10 +const LANAliasesFile = "~/bettercap.aliases" + +type EndpointNewCallback func(e *Endpoint) +type EndpointLostCallback func(e *Endpoint) + +type LAN struct { + sync.Mutex + + Interface *Endpoint + Gateway *Endpoint + Hosts map[string]*Endpoint + TTL map[string]uint + Aliases map[string]string + + newCb EndpointNewCallback + lostCb EndpointLostCallback + aliasesFileName string +} + +func NewLAN(iface, gateway *Endpoint, newcb EndpointNewCallback, lostcb EndpointLostCallback) *LAN { + lan := &LAN{ + Interface: iface, + Gateway: gateway, + Hosts: make(map[string]*Endpoint), + TTL: make(map[string]uint), + Aliases: make(map[string]string), + newCb: newcb, + lostCb: lostcb, + } + + lan.aliasesFileName, _ = core.ExpandPath(LANAliasesFile) + if core.Exists(lan.aliasesFileName) { + if err := lan.loadAliases(); err != nil { + fmt.Printf("%s\n", err) + } + } + + return lan +} + +func (lan *LAN) List() (list []*Endpoint) { + lan.Lock() + defer lan.Unlock() + + list = make([]*Endpoint, 0) + for _, t := range lan.Hosts { + list = append(list, t) + } + return +} + +func (lan *LAN) loadAliases() error { + file, err := os.Open(lan.aliasesFileName) + if err != nil { + return err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + parts := strings.SplitN(line, " ", 2) + mac := core.Trim(parts[0]) + alias := core.Trim(parts[1]) + lan.Aliases[mac] = alias + } + + return nil +} + +func (lan *LAN) saveAliases() { + data := "" + for mac, alias := range lan.Aliases { + data += fmt.Sprintf("%s %s\n", mac, alias) + } + ioutil.WriteFile(lan.aliasesFileName, []byte(data), 0644) +} + +func (lan *LAN) SetAliasFor(mac, alias string) bool { + lan.Lock() + defer lan.Unlock() + + if t, found := lan.Hosts[mac]; found == true { + if alias != "" { + lan.Aliases[mac] = alias + } else { + delete(lan.Aliases, mac) + } + + t.Alias = alias + lan.saveAliases() + return true + } + + return false +} + +func (lan *LAN) WasMissed(mac string) bool { + if mac == lan.Interface.HwAddress || mac == lan.Gateway.HwAddress { + return false + } + + lan.Lock() + defer lan.Unlock() + + if ttl, found := lan.TTL[mac]; found == true { + return ttl < LANDefaultTTL + } + return true +} + +func (lan *LAN) Remove(ip, mac string) { + lan.Lock() + defer lan.Unlock() + + if e, found := lan.Hosts[mac]; found { + lan.TTL[mac]-- + if lan.TTL[mac] == 0 { + delete(lan.Hosts, mac) + delete(lan.TTL, mac) + + lan.lostCb(e) + } + return + } +} + +func (lan *LAN) shouldIgnore(ip string) bool { + // skip our own address + if ip == lan.Interface.IpAddress { + return true + } + // skip the gateway + if ip == lan.Gateway.IpAddress { + return true + } + // skip broadcast addresses + if strings.HasSuffix(ip, ".255") { + return true + } + // skip everything which is not in our subnet (multicast noise) + addr := net.ParseIP(ip) + return lan.Interface.Net.Contains(addr) == false +} + +func (lan *LAN) Has(ip string) bool { + lan.Lock() + defer lan.Unlock() + + for _, e := range lan.Hosts { + if e.IpAddress == ip { + return true + } + } + + return false +} + +func (lan *LAN) Get(ip string) *Endpoint { + lan.Lock() + defer lan.Unlock() + + for _, e := range lan.Hosts { + if e.IpAddress == ip { + return e + } + } + + return nil +} + +func (lan *LAN) AddIfNew(ip, mac string) *Endpoint { + lan.Lock() + defer lan.Unlock() + + if lan.shouldIgnore(ip) { + return nil + } + + mac = NormalizeMac(mac) + if t, found := lan.Hosts[mac]; found { + if lan.TTL[mac] < LANDefaultTTL { + lan.TTL[mac]++ + } + return t + } + + e := NewEndpoint(ip, mac) + if alias, found := lan.Aliases[mac]; found { + e.Alias = alias + } + + lan.Hosts[mac] = e + lan.TTL[mac] = LANDefaultTTL + + lan.newCb(e) + + return nil +} diff --git a/session/session.go b/session/session.go index 6f34a621..20379e00 100644 --- a/session/session.go +++ b/session/session.go @@ -38,7 +38,7 @@ type Session struct { Gateway *network.Endpoint `json:"gateway"` Firewall firewall.FirewallManager `json:"-"` Env *Environment `json:"env"` - Targets *Targets `json:"targets"` + Lan *network.LAN `json:"lan"` WiFi *network.WiFi `json:"wifi"` Queue *packets.Queue `json:"packets"` Input *readline.Instance `json:"-"` @@ -295,7 +295,7 @@ func (s *Session) startNetMon() { addr := event.IP.String() mac := event.MAC.String() - existing := s.Targets.AddIfNew(addr, mac) + existing := s.Lan.AddIfNew(addr, mac) if existing != nil { existing.LastSeen = time.Now() } @@ -376,9 +376,13 @@ func (s *Session) Start() error { s.Gateway = s.Interface } - s.WiFi = network.NewWiFi(s.Interface) - s.Targets = NewTargets(s, s.Interface, s.Gateway) s.Firewall = firewall.Make(s.Interface) + s.WiFi = network.NewWiFi(s.Interface) + s.Lan = network.NewLAN(s.Interface, s.Gateway, func(e *network.Endpoint) { + s.Events.Add("endpoint.new", e) + }, func(e *network.Endpoint) { + s.Events.Add("endpoint.lost", e) + }) s.setupEnv() diff --git a/session/session_core_handlers.go b/session/session_core_handlers.go index 9796dc33..e08eb0c5 100644 --- a/session/session_core_handlers.go +++ b/session/session_core_handlers.go @@ -185,7 +185,7 @@ func (s *Session) aliasHandler(args []string, sess *Session) error { mac := args[0] alias := core.Trim(args[1]) - if s.Targets.SetAliasFor(mac, alias) == true { + if s.Lan.SetAliasFor(mac, alias) == true { return nil } else { return fmt.Errorf("Could not find endpoint %s", mac) @@ -298,7 +298,7 @@ func (s *Session) registerCoreHandlers() { readline.PcItem("alias", readline.PcItemDynamic(func(prefix string) []string { prefix = core.Trim(prefix[5:]) macs := []string{""} - for mac := range s.Targets.Targets { + for mac := range s.Lan.Hosts { if prefix == "" || strings.HasPrefix(mac, prefix) == true { macs = append(macs, mac) } diff --git a/session/targets.go b/session/targets.go deleted file mode 100644 index 19835054..00000000 --- a/session/targets.go +++ /dev/null @@ -1,204 +0,0 @@ -package session - -import ( - "bufio" - "fmt" - "io/ioutil" - "net" - "os" - "strings" - "sync" - - "github.com/evilsocket/bettercap-ng/core" - "github.com/evilsocket/bettercap-ng/network" -) - -const TargetsDefaultTTL = 10 -const TargetsAliasesFile = "~/bettercap.aliases" - -type Targets struct { - sync.Mutex - - Session *Session `json:"-"` - Interface *network.Endpoint - Gateway *network.Endpoint - Targets map[string]*network.Endpoint - TTL map[string]uint - Aliases map[string]string - - aliasesFileName string -} - -func NewTargets(s *Session, iface, gateway *network.Endpoint) *Targets { - t := &Targets{ - Session: s, - Interface: iface, - Gateway: gateway, - Targets: make(map[string]*network.Endpoint), - TTL: make(map[string]uint), - Aliases: make(map[string]string), - } - - t.aliasesFileName, _ = core.ExpandPath(TargetsAliasesFile) - if core.Exists(t.aliasesFileName) { - if err := t.loadAliases(); err != nil { - s.Events.Log(core.ERROR, "%s", err) - } - } - - return t -} - -func (tp *Targets) List() (list []*network.Endpoint) { - tp.Lock() - defer tp.Unlock() - - list = make([]*network.Endpoint, 0) - for _, t := range tp.Targets { - list = append(list, t) - } - return -} - -func (tp *Targets) loadAliases() error { - tp.Session.Events.Log(core.INFO, "Loading aliases from %s ...", tp.aliasesFileName) - file, err := os.Open(tp.aliasesFileName) - if err != nil { - return err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - parts := strings.SplitN(line, " ", 2) - mac := core.Trim(parts[0]) - alias := core.Trim(parts[1]) - tp.Session.Events.Log(core.DEBUG, " aliases[%s] = '%s'", mac, alias) - tp.Aliases[mac] = alias - } - - return nil -} - -func (tp *Targets) saveAliases() { - data := "" - for mac, alias := range tp.Aliases { - data += fmt.Sprintf("%s %s\n", mac, alias) - } - ioutil.WriteFile(tp.aliasesFileName, []byte(data), 0644) -} - -func (tp *Targets) SetAliasFor(mac, alias string) bool { - tp.Lock() - defer tp.Unlock() - - if t, found := tp.Targets[mac]; found == true { - if alias != "" { - tp.Aliases[mac] = alias - } else { - delete(tp.Aliases, mac) - } - - t.Alias = alias - tp.saveAliases() - return true - } - - return false -} - -func (tp *Targets) WasMissed(mac string) bool { - if mac == tp.Session.Interface.HwAddress || mac == tp.Session.Gateway.HwAddress { - return false - } - - tp.Lock() - defer tp.Unlock() - - if ttl, found := tp.TTL[mac]; found == true { - return ttl < TargetsDefaultTTL - } - return true -} - -func (tp *Targets) Remove(ip, mac string) { - tp.Lock() - defer tp.Unlock() - - if e, found := tp.Targets[mac]; found { - tp.TTL[mac]-- - if tp.TTL[mac] == 0 { - tp.Session.Events.Add("endpoint.lost", e) - delete(tp.Targets, mac) - delete(tp.TTL, mac) - } - return - } -} - -func (tp *Targets) shouldIgnore(ip string) bool { - // skip our own address - if ip == tp.Interface.IpAddress { - return true - } - // skip the gateway - if ip == tp.Gateway.IpAddress { - return true - } - // skip broadcast addresses - if strings.HasSuffix(ip, ".255") { - return true - } - // skip everything which is not in our subnet (multicast noise) - addr := net.ParseIP(ip) - return tp.Session.Interface.Net.Contains(addr) == false -} - -func (tp *Targets) Has(ip string) bool { - tp.Lock() - defer tp.Unlock() - - for _, e := range tp.Targets { - if e.IpAddress == ip { - return true - } - } - - return false -} - -func (tp *Targets) AddIfNew(ip, mac string) *network.Endpoint { - tp.Lock() - defer tp.Unlock() - - if tp.shouldIgnore(ip) { - return nil - } - - mac = network.NormalizeMac(mac) - if t, found := tp.Targets[mac]; found { - if tp.TTL[mac] < TargetsDefaultTTL { - tp.TTL[mac]++ - } - return t - } - - e := network.NewEndpoint(ip, mac) - /* - e.ResolvedCallback = func(e *network.Endpoint) { - tp.Session.Events.Add("endpoint.resolved", e) - } - */ - - if alias, found := tp.Aliases[mac]; found { - e.Alias = alias - } - - tp.Targets[mac] = e - tp.TTL[mac] = TargetsDefaultTTL - - tp.Session.Events.Add("endpoint.new", e) - - return nil -}