diff --git a/core/swag.go b/core/swag.go index dca94d09..e1ecbc4c 100644 --- a/core/swag.go +++ b/core/swag.go @@ -74,6 +74,10 @@ func Green(s string) string { return W(GREEN, s) } +func Blue(s string) string { + return W(BLUE, s) +} + func Yellow(s string) string { return W(YELLOW, s) } diff --git a/modules/net_recon_show.go b/modules/net_recon_show.go index beff2c1f..a51d3c42 100644 --- a/modules/net_recon_show.go +++ b/modules/net_recon_show.go @@ -15,8 +15,9 @@ import ( ) var ( - aliveTimeInterval = time.Duration(10) * time.Second - presentTimeInterval = time.Duration(1) * time.Minute + aliveTimeInterval = time.Duration(10) * time.Second + presentTimeInterval = time.Duration(1) * time.Minute + justJoinedTimeInterval = time.Duration(10) * time.Second ) type ProtoPair struct { @@ -45,113 +46,107 @@ func rankByProtoHits(protos map[string]uint64) (ProtoPairList, uint64) { return pl, max } +func (d *Discovery) getRow(e *net.Endpoint) []string { + sinceStarted := time.Since(d.Session.StartedAt) + sinceFirstSeen := time.Since(e.FirstSeen) + + addr := e.IpAddress + mac := e.HwAddress + if d.Session.Targets.WasMissed(e.HwAddress) == true { + // if endpoint was not found in ARP at least once + addr = core.Dim(addr) + mac = core.Dim(mac) + } else if sinceStarted > justJoinedTimeInterval && sinceFirstSeen <= justJoinedTimeInterval { + // if endpoint was first seen in the last 10 seconds + addr = core.Bold(addr) + mac = core.Bold(mac) + } + + name := "" + if e == d.Session.Interface { + name = e.Name() + } else if e == d.Session.Gateway { + name = "gateway" + } else if e.Hostname != "" { + name = core.Yellow(e.Hostname) + } else if e.Alias != "" { + name = core.Green(e.Alias) + } + + var traffic *packets.Traffic + var found bool + if traffic, found = d.Session.Queue.Traffic[e.IpAddress]; found == false { + traffic = &packets.Traffic{} + } + + seen := e.LastSeen.Format("15:04:05") + sinceLastSeen := time.Since(e.LastSeen) + if sinceStarted > aliveTimeInterval && sinceLastSeen <= aliveTimeInterval { + // if endpoint seen in the last 10 seconds + seen = core.Bold(seen) + } else if sinceLastSeen <= presentTimeInterval { + // if endpoint seen in the last 60 seconds + } else { + // not seen in a while + seen = core.Dim(seen) + } + + return []string{ + addr, + mac, + name, + e.Vendor, + humanize.Bytes(traffic.Sent), + humanize.Bytes(traffic.Received), + seen, + } +} + +func (d *Discovery) showTable(header []string, rows [][]string) { + fmt.Println() + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader(header) + table.SetColWidth(80) + table.AppendBulk(rows) + table.Render() +} + func (d *Discovery) Show(by string) error { - d.Session.Targets.Lock() d.Session.Queue.Lock() - defer d.Session.Targets.Unlock() defer d.Session.Queue.Unlock() - iface := d.Session.Interface - gw := d.Session.Gateway - - data := [][]string{ - []string{core.Green("interface"), core.Bold(iface.Name()), iface.IpAddress, iface.HwAddress, core.Dim(iface.Vendor)}, - []string{core.Green("gateway"), core.Bold(gw.Hostname), gw.IpAddress, gw.HwAddress, core.Dim(gw.Vendor)}, - } - - fmt.Println() - - table := tablewriter.NewWriter(os.Stdout) - - table.SetColWidth(80) - table.AppendBulk(data) - table.Render() - - fmt.Println() - - nTargets := len(d.Session.Targets.Targets) - if nTargets == 0 { - fmt.Println(core.Dim("No endpoints discovered so far.\n")) + targets := d.Session.Targets.List() + if by == "seen" { + sort.Sort(BySeenSorter(targets)) + } else if by == "sent" { + sort.Sort(BySentSorter(targets)) + } else if by == "rcvd" { + sort.Sort(ByRcvdSorter(targets)) } else { - targets := make([]*net.Endpoint, 0, nTargets) - for _, t := range d.Session.Targets.Targets { - targets = append(targets, t) - } - - if by == "seen" { - sort.Sort(BySeenSorter(targets)) - } else if by == "sent" { - sort.Sort(BySentSorter(targets)) - } else if by == "rcvd" { - sort.Sort(ByRcvdSorter(targets)) - } else { - sort.Sort(ByAddressSorter(targets)) - } - - data = make([][]string, nTargets) - for i, t := range targets { - var traffic *packets.Traffic - var found bool - - if traffic, found = d.Session.Queue.Traffic[t.IpAddress]; found == false { - traffic = &packets.Traffic{} - } - - seen := t.LastSeen.Format("15:04:05") - sinceLastSeen := time.Since(t.LastSeen) - if sinceLastSeen <= aliveTimeInterval { - seen = core.Bold(seen) - } else if sinceLastSeen <= presentTimeInterval { - - } else { - seen = core.Dim(seen) - } - - name := core.Yellow(t.Hostname) - if t.Alias != "" { - name = core.Green(t.Alias) - } - - data[i] = []string{ - t.IpAddress, - t.HwAddress, - name, - t.Vendor, - humanize.Bytes(traffic.Sent), - humanize.Bytes(traffic.Received), - seen, - } - } - - table = tablewriter.NewWriter(os.Stdout) - - table.SetHeader([]string{"IP", "MAC", "Name", "Vendor", "Sent", "Recvd", "Last Seen"}) - table.SetColWidth(80) - table.AppendBulk(data) - table.Render() - - fmt.Println() + sort.Sort(ByAddressSorter(targets)) } - row := []string{ + targets = append([]*net.Endpoint{d.Session.Interface, d.Session.Gateway}, targets...) + rows := make([][]string, 0) + for i, t := range targets { + rows = append(rows, d.getRow(t)) + if i == 1 { + rows = append(rows, []string{"", "", "", "", "", "", ""}) + } + } + + d.showTable([]string{"IP", "MAC", "Name", "Vendor", "Sent", "Recvd", "Last Seen"}, rows) + + rows = [][]string{{ humanize.Bytes(d.Session.Queue.Sent), humanize.Bytes(d.Session.Queue.Received), fmt.Sprintf("%d", d.Session.Queue.PktReceived), fmt.Sprintf("%d", d.Session.Queue.Errors), - } + }} - table = tablewriter.NewWriter(os.Stdout) - - table.SetHeader([]string{"Sent", "Sniffed", "# Packets", "Errors"}) - table.SetColWidth(80) - table.Append(row) - table.Render() - - fmt.Println() - - table = tablewriter.NewWriter(os.Stdout) - table.SetColWidth(80) + d.showTable([]string{"Sent", "Sniffed", "# Packets", "Errors"}, rows) + rows = make([][]string, 0) protos, maxPackets := rankByProtoHits(d.Session.Queue.Protos) maxBarWidth := 70 @@ -162,12 +157,10 @@ func (d *Discovery) Show(by string) error { bar += "▇" } - table.Append([]string{p.Protocol, fmt.Sprintf("%s %d", bar, p.Hits)}) + rows = append(rows, []string{p.Protocol, fmt.Sprintf("%s %d", bar, p.Hits)}) } - table.SetHeader([]string{"Proto", "# Packets"}) - - table.Render() + d.showTable([]string{"Proto", "# Packets"}, rows) d.Session.Refresh() diff --git a/modules/net_recon_sort.go b/modules/net_recon_sort.go index 66e9ffc8..716e9ea1 100644 --- a/modules/net_recon_sort.go +++ b/modules/net_recon_sort.go @@ -8,9 +8,15 @@ import ( type ByAddressSorter []*net.Endpoint -func (a ByAddressSorter) Len() int { return len(a) } -func (a ByAddressSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByAddressSorter) Less(i, j int) bool { return a[i].IpAddressUint32 < a[j].IpAddressUint32 } +func (a ByAddressSorter) Len() int { return len(a) } +func (a ByAddressSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByAddressSorter) Less(i, j int) bool { + if a[i].IpAddressUint32 == a[j].IpAddressUint32 { + return a[i].HwAddress < a[j].HwAddress + } else { + return a[i].IpAddressUint32 < a[j].IpAddressUint32 + } +} type BySeenSorter []*net.Endpoint diff --git a/session/session.go b/session/session.go index 99b918dc..d435af43 100644 --- a/session/session.go +++ b/session/session.go @@ -38,6 +38,7 @@ type Session struct { Targets *Targets `json:"targets"` Queue *packets.Queue `json:"packets"` Input *readline.Instance `json:"-"` + StartedAt time.Time `json:"started_at"` Active bool `json:"active"` Prompt Prompt `json:"-"` @@ -264,6 +265,7 @@ func (s *Session) Start() error { os.Exit(0) }() + s.StartedAt = time.Now() s.Active = true // keep reading network events in order to add / update endpoints diff --git a/session/targets.go b/session/targets.go index a1718f69..bf4ad7b7 100644 --- a/session/targets.go +++ b/session/targets.go @@ -12,6 +12,7 @@ import ( "github.com/evilsocket/bettercap-ng/net" ) +const TargetsDefaultTTL = 2 const TargetsAliasesFile = "~/bettercap.aliases" type Targets struct { @@ -47,6 +48,17 @@ func NewTargets(s *Session, iface, gateway *net.Endpoint) *Targets { return t } +func (tp *Targets) List() (list []*net.Endpoint) { + tp.Lock() + defer tp.Unlock() + + list = make([]*net.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) @@ -90,6 +102,20 @@ func (tp *Targets) SetAliasFor(mac, alias string) bool { 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() @@ -145,7 +171,7 @@ func (tp *Targets) AddIfNotExist(ip, mac string) *net.Endpoint { } tp.Targets[mac] = e - tp.TTL[mac] = 2 + tp.TTL[mac] = TargetsDefaultTTL tp.Session.Events.Add("target.new", e)