diff --git a/modules/wifi_recon.go b/modules/wifi_recon.go index 3972be8b..2f0e6d2b 100644 --- a/modules/wifi_recon.go +++ b/modules/wifi_recon.go @@ -11,6 +11,7 @@ import ( "github.com/evilsocket/bettercap-ng/core" "github.com/evilsocket/bettercap-ng/log" + "github.com/evilsocket/bettercap-ng/network" "github.com/evilsocket/bettercap-ng/packets" "github.com/evilsocket/bettercap-ng/session" @@ -28,6 +29,7 @@ type WiFiRecon struct { wifi *WiFi stats *WiFiStats handle *pcap.Handle + channel int client net.HardwareAddr accessPoint net.HardwareAddr } @@ -36,6 +38,7 @@ func NewWiFiRecon(s *session.Session) *WiFiRecon { w := &WiFiRecon{ SessionModule: session.NewSessionModule("wifi.recon", s), stats: NewWiFiStats(), + channel: 0, client: make([]byte, 0), accessPoint: make([]byte, 0), } @@ -97,9 +100,13 @@ func NewWiFiRecon(s *session.Session) *WiFiRecon { w.AddHandler(session.NewModuleHandler("wifi.show", "", "Show current wireless stations list (default sorting by essid).", func(args []string) error { - return w.Show("essid") + return w.Show("channel") })) + w.AddParam(session.NewIntParameter("wifi.recon.channel", + "", + "WiFi channel or empty for channel hopping.")) + return w } @@ -177,6 +184,17 @@ func mhz2chan(freq int) int { return 0 } +type ByChannelSorter []*WiFiStation + +func (a ByChannelSorter) Len() int { return len(a) } +func (a ByChannelSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByChannelSorter) Less(i, j int) bool { + if a[i].Channel == a[j].Channel { + return a[i].HwAddress < a[j].HwAddress + } + return a[i].Channel < a[j].Channel +} + type ByEssidSorter []*WiFiStation func (a ByEssidSorter) Len() int { return len(a) } @@ -221,8 +239,10 @@ func (w *WiFiRecon) Show(by string) error { stations := w.wifi.List() if by == "seen" { sort.Sort(BywifiSeenSorter(stations)) - } else { + } else if by == "essid" { sort.Sort(ByEssidSorter(stations)) + } else { + sort.Sort(ByChannelSorter(stations)) } rows := make([][]string, 0) @@ -259,6 +279,20 @@ func (w *WiFiRecon) Configure() error { return err } else if w.handle, err = ihandle.Activate(); err != nil { return err + } else if err, w.channel = w.IntParam("wifi.recon.channel"); err == nil { + if err = network.SetInterfaceChannel(w.Session.Interface.Name(), w.channel); err != nil { + return err + } + log.Info("WiFi recon active on channel %d.", w.channel) + } else { + w.channel = 0 + // we need to start somewhere, this is just to check if + // this OS support switching channel programmatically. + if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil { + return err + } + log.Info("WiFi recon active with channel hopping.") + } w.wifi = NewWiFi(w.Session, w.Session.Interface) @@ -368,6 +402,25 @@ func (w *WiFiRecon) Start() error { } w.SetRunning(true, func() { + // start channel hopper if needed + if w.channel == 0 { + go func() { + log.Info("Channel hopper started.") + for w.Running() == true { + for channel := 1; channel < 15; channel++ { + if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { + log.Warning("Error while hopping to channel %d: %s", channel, err) + } + // this is the default for airodump-ng, good for them, good for us. + time.Sleep(250 * time.Millisecond) + if w.Running() == false { + return + } + } + } + }() + } + defer w.handle.Close() src := gopacket.NewPacketSource(w.handle, w.handle.LinkType()) for packet := range src.Packets() { diff --git a/network/net_darwin.go b/network/net_darwin.go index 2f5089b1..a3d22152 100644 --- a/network/net_darwin.go +++ b/network/net_darwin.go @@ -3,8 +3,12 @@ package network import ( "net" "regexp" + + "github.com/evilsocket/bettercap-ng/core" ) +const airPortPath = "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport" + var IPv4RouteParser = regexp.MustCompile("^([a-z]+)+\\s+(\\d+\\.+\\d+.\\d.+\\d)+\\s+([a-zA-z]+)+\\s+(\\d+)+\\s+(\\d+)+\\s+([a-zA-Z]+\\d+)$") var IPv4RouteTokens = 7 var IPv4RouteCmd = "netstat" @@ -26,3 +30,13 @@ func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) ( func getInterfaceName(iface net.Interface) string { return iface.Name } + +func SetInterfaceChannel(iface string, channel int) error { + out, err := core.Exec(airPortPath, []string{iface, "--channel", fmt.Sprintf("%d", channel)}) + if err != nil { + return err + } else if out != "" { + return fmt.Errorf("Unexpected output while setting interface %s to channel %d: %s", iface, channel, out) + } + return nil +} diff --git a/network/net_linux.go b/network/net_linux.go index 0291587e..b6207930 100644 --- a/network/net_linux.go +++ b/network/net_linux.go @@ -1,8 +1,11 @@ package network import ( + "fmt" "net" "regexp" + + "github.com/evilsocket/bettercap-ng/core" ) // only matches gateway lines @@ -26,3 +29,13 @@ func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) ( func getInterfaceName(iface net.Interface) string { return iface.Name } + +func SetInterfaceChannel(iface string, channel int) error { + out, err := core.Exec("iwconfig", []string{iface, "channel", fmt.Sprintf("%d", channel)}) + if err != nil { + return err + } else if out != "" { + return fmt.Errorf("Unexpected output while setting interface %s to channel %d: %s", iface, channel, out) + } + return nil +} diff --git a/network/net_windows.go b/network/net_windows.go index c19b1e33..dfb8ec15 100644 --- a/network/net_windows.go +++ b/network/net_windows.go @@ -1,6 +1,7 @@ package network import ( + "fmt" "net" "regexp" "strings" @@ -57,3 +58,7 @@ func getInterfaceName(iface net.Interface) string { return iface.Name } + +func SetInterfaceChannel(iface string, channel int) error { + return fmt.Errorf("Windows does not support WiFi channel hopping.") +}