diff --git a/modules/wifi.go b/modules/wifi.go new file mode 100644 index 00000000..7eefcc40 --- /dev/null +++ b/modules/wifi.go @@ -0,0 +1,351 @@ +package modules + +import ( + "fmt" + "net" + "strconv" + "strings" + "sync" + "time" + + "github.com/bettercap/bettercap/core" + "github.com/bettercap/bettercap/log" + "github.com/bettercap/bettercap/network" + "github.com/bettercap/bettercap/packets" + "github.com/bettercap/bettercap/session" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" +) + +type WiFiModule struct { + session.SessionModule + + handle *pcap.Handle + source string + channel int + hopPeriod time.Duration + frequencies []int + ap *network.AccessPoint + stickChan int + skipBroken bool + pktSourceChan chan gopacket.Packet + pktSourceChanClosed bool + apRunning bool + apConfig packets.Dot11ApConfig + writes *sync.WaitGroup + reads *sync.WaitGroup +} + +func NewWiFiModule(s *session.Session) *WiFiModule { + w := &WiFiModule{ + SessionModule: session.NewSessionModule("wifi", s), + channel: 0, + stickChan: 0, + hopPeriod: 250 * time.Millisecond, + ap: nil, + skipBroken: true, + apRunning: false, + writes: &sync.WaitGroup{}, + reads: &sync.WaitGroup{}, + } + + w.AddHandler(session.NewModuleHandler("wifi.recon on", "", + "Start 802.11 wireless base stations discovery and channel hopping.", + func(args []string) error { + return w.Start() + })) + + w.AddHandler(session.NewModuleHandler("wifi.recon off", "", + "Stop 802.11 wireless base stations discovery and channel hopping.", + func(args []string) error { + return w.Stop() + })) + + w.AddHandler(session.NewModuleHandler("wifi.recon MAC", "wifi.recon ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))", + "Set 802.11 base station address to filter for.", + func(args []string) error { + bssid, err := net.ParseMAC(args[0]) + if err != nil { + return err + } else if ap, found := w.Session.WiFi.Get(bssid.String()); found == true { + w.ap = ap + w.stickChan = network.Dot11Freq2Chan(ap.Frequency) + return nil + } + return fmt.Errorf("Could not find station with BSSID %s", args[0]) + })) + + w.AddHandler(session.NewModuleHandler("wifi.recon clear", "", + "Remove the 802.11 base station filter.", + func(args []string) error { + w.ap = nil + w.stickChan = 0 + var err error + if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { + return err + } + return nil + })) + + w.AddHandler(session.NewModuleHandler("wifi.deauth BSSID", `wifi\.deauth ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))`, + "Start a 802.11 deauth attack, if an access point BSSID is provided, every client will be deauthenticated, otherwise only the selected client.", + func(args []string) error { + bssid, err := net.ParseMAC(args[0]) + if err != nil { + return err + } + return w.startDeauth(bssid) + })) + + w.AddHandler(session.NewModuleHandler("wifi.ap", "", + "Inject fake management beacons in order to create a rogue access point.", + func(args []string) error { + if err := w.parseApConfig(); err != nil { + return err + } else { + return w.startAp() + } + })) + + w.AddParam(session.NewStringParameter("wifi.ap.ssid", + "FreeWiFi", + "", + "SSID of the fake access point.")) + + w.AddParam(session.NewStringParameter("wifi.ap.bssid", + session.ParamRandomMAC, + "[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}", + "BSSID of the fake access point.")) + + w.AddParam(session.NewIntParameter("wifi.ap.channel", + "1", + "Channel of the fake access point.")) + + w.AddParam(session.NewBoolParameter("wifi.ap.encryption", + "true", + "If true, the fake access point will use WPA2, otherwise it'll result as an open AP.")) + + w.AddHandler(session.NewModuleHandler("wifi.show", "", + "Show current wireless stations list (default sorting by essid).", + func(args []string) error { + return w.Show("rssi") + })) + + w.AddHandler(session.NewModuleHandler("wifi.recon.channel", `wifi\.recon\.channel[\s]+([0-9]+(?:[, ]+[0-9]+)*|clear)`, + "WiFi channels (comma separated) or 'clear' for channel hopping.", + func(args []string) error { + newfrequencies := w.frequencies[:0] + + if len(args) > 0 && args[0] != "clear" { + channels := strings.Split(args[0], ",") + for _, c := range channels { + trimmed := strings.Trim(c, " ") + channel, err := strconv.Atoi(trimmed) + if err != nil { + return err + } + newfrequencies = append(newfrequencies, network.Dot11Chan2Freq(channel)) + } + } else { + // No channels setted, retrieve frequencies supported by the card + if frequencies, err := network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { + return err + } else { + newfrequencies = frequencies + } + } + + w.frequencies = newfrequencies + + return nil + })) + + w.AddParam(session.NewStringParameter("wifi.source.file", + "", + "", + "If set, the wifi module will read from this pcap file instead of the hardware interface.")) + + w.AddParam(session.NewIntParameter("wifi.hop.period", + "250", + "If channel hopping is enabled (empty wifi.recon.channel), this is the time in milliseconds the algorithm will hop on every channel (it'll be doubled if both 2.4 and 5.0 bands are available).")) + + w.AddParam(session.NewBoolParameter("wifi.skip-broken", + "true", + "If true, dot11 packets with an invalid checksum will be skipped.")) + + return w +} + +func (w WiFiModule) Name() string { + return "wifi" +} + +func (w WiFiModule) Description() string { + return "A module to monitor and perform wireless attacks on 802.11." +} + +func (w WiFiModule) Author() string { + return "Gianluca Braga && Simone Margaritelli >" +} + +func (w *WiFiModule) Configure() error { + var hopPeriod int + var err error + + if err, w.source = w.StringParam("wifi.source.file"); err != nil { + return err + } + + if w.source != "" { + if w.handle, err = pcap.OpenOffline(w.source); err != nil { + return err + } + } else { + ihandle, err := pcap.NewInactiveHandle(w.Session.Interface.Name()) + if err != nil { + return err + } + defer ihandle.CleanUp() + + if err = ihandle.SetRFMon(true); err != nil { + return fmt.Errorf("Error while setting interface %s in monitor mode: %s", core.Bold(w.Session.Interface.Name()), err) + } else if err = ihandle.SetSnapLen(65536); err != nil { + return err + } else if err = ihandle.SetTimeout(pcap.BlockForever); err != nil { + return err + } else if w.handle, err = ihandle.Activate(); err != nil { + return err + } + } + + if err, w.skipBroken = w.BoolParam("wifi.skip-broken"); err != nil { + return err + } else if err, hopPeriod = w.IntParam("wifi.hop.period"); err != nil { + return err + } + + w.hopPeriod = time.Duration(hopPeriod) * time.Millisecond + + if w.source == "" { + // No channels setted, retrieve frequencies supported by the card + if len(w.frequencies) == 0 { + if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { + return err + } + + // we need to start somewhere, this is just to check if + // this OS supports switching channel programmatically. + if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil { + return err + } + log.Info("WiFi recon active with channel hopping.") + } + } + + return nil +} + +func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) { + // collect stats from data frames + if dot11.Type.MainType() == layers.Dot11TypeData { + bytes := uint64(len(packet.Data())) + + dst := dot11.Address1.String() + if station, found := w.Session.WiFi.Get(dst); found == true { + station.Received += bytes + } + + src := dot11.Address2.String() + if station, found := w.Session.WiFi.Get(src); found == true { + station.Sent += bytes + } + } + + if ok, enc, cipher, auth := packets.Dot11ParseEncryption(packet, dot11); ok == true { + bssid := dot11.Address3.String() + if station, found := w.Session.WiFi.Get(bssid); found == true { + station.Encryption = enc + station.Cipher = cipher + station.Authentication = auth + } + } +} + +func (w *WiFiModule) trackPacket(pkt gopacket.Packet) { + pktSize := uint64(len(pkt.Data())) + + w.Session.Queue.Stats.Lock() + + w.Session.Queue.Stats.PktReceived++ + w.Session.Queue.Stats.Received += pktSize + + w.Session.Queue.Stats.Unlock() +} + +func (w *WiFiModule) Start() error { + if err := w.Configure(); err != nil { + return err + } + + w.SetRunning(true, func() { + // start channel hopper if needed + if w.channel == 0 && w.source == "" { + go w.channelHopper() + } + + // start the pruner + go w.stationPruner() + + w.reads.Add(1) + defer w.reads.Done() + + src := gopacket.NewPacketSource(w.handle, w.handle.LinkType()) + w.pktSourceChan = src.Packets() + for packet := range w.pktSourceChan { + if w.Running() == false { + break + } + + if packet == nil { + continue + } + + w.trackPacket(packet) + + // perform initial dot11 parsing and layers validation + if ok, radiotap, dot11 := packets.Dot11Parse(packet); ok == true { + // check FCS checksum + if w.skipBroken && dot11.ChecksumValid() == false { + log.Debug("Skipping dot11 packet with invalid checksum.") + continue + } + + w.discoverProbes(radiotap, dot11, packet) + w.discoverAccessPoints(radiotap, dot11, packet) + w.discoverClients(radiotap, dot11, packet) + w.updateStats(dot11, packet) + } + } + w.pktSourceChanClosed = true + }) + + return nil +} + +func (w *WiFiModule) Stop() error { + return w.SetRunning(false, func() { + // wait any pending write operation + w.writes.Wait() + // signal the main for loop we want to exit + if w.pktSourceChanClosed == false { + w.pktSourceChan <- nil + } + // close the pcap handle to make the main for exit + w.handle.Close() + // close the pcap handle to make the main for exit + // wait for the loop to exit. + w.reads.Wait() + }) +} diff --git a/modules/wifi_ap.go b/modules/wifi_ap.go new file mode 100644 index 00000000..bf40db7d --- /dev/null +++ b/modules/wifi_ap.go @@ -0,0 +1,74 @@ +package modules + +import ( + "errors" + "net" + "time" + + "github.com/bettercap/bettercap/core" + "github.com/bettercap/bettercap/log" + "github.com/bettercap/bettercap/network" + "github.com/bettercap/bettercap/packets" + "github.com/bettercap/bettercap/session" +) + +var errNoRecon = errors.New("Module wifi.ap requires module wifi.recon to be activated.") + +func (w *WiFiModule) parseApConfig() (err error) { + var bssid string + + if err, w.apConfig.SSID = w.StringParam("wifi.ap.ssid"); err != nil { + return + } else if err, bssid = w.StringParam("wifi.ap.bssid"); err != nil { + return + } else if w.apConfig.BSSID, err = net.ParseMAC(network.NormalizeMac(bssid)); err != nil { + return + } else if err, w.apConfig.Channel = w.IntParam("wifi.ap.channel"); err != nil { + return + } else if err, w.apConfig.Encryption = w.BoolParam("wifi.ap.encryption"); err != nil { + return + } + + return +} + +func (w *WiFiModule) startAp() error { + // we need channel hopping and packet injection for this + if w.Running() == false { + return errNoRecon + } else if w.apRunning { + return session.ErrAlreadyStarted + } + + go func() { + w.apRunning = true + defer func() { + w.apRunning = false + }() + + enc := core.Yellow("WPA2") + if w.apConfig.Encryption == false { + enc = core.Green("Open") + } + log.Info("Sending beacons as SSID %s (%s) on channel %d (%s).", + core.Bold(w.apConfig.SSID), + w.apConfig.BSSID.String(), + w.apConfig.Channel, + enc) + + for seqn := uint16(0); w.Running(); seqn++ { + w.writes.Add(1) + defer w.writes.Done() + + if err, pkt := packets.NewDot11Beacon(w.apConfig, seqn); err != nil { + log.Error("Could not create beacon packet: %s", err) + } else { + w.injectPacket(pkt) + } + + time.Sleep(100 * time.Millisecond) + } + }() + + return nil +} diff --git a/modules/wifi_beacon_flood.go b/modules/wifi_beacon_flood.go deleted file mode 100644 index 1f29f2d1..00000000 --- a/modules/wifi_beacon_flood.go +++ /dev/null @@ -1,129 +0,0 @@ -package modules - -import ( - "crypto/rand" - "fmt" - "net" - "time" - - "github.com/bettercap/bettercap/log" - "github.com/bettercap/bettercap/network" - "github.com/bettercap/bettercap/packets" - - "github.com/google/gopacket" - "github.com/google/gopacket/layers" -) - -var ( - openFlags = 1057 - wpaFlags = 1041 - //1-54 Mbit - supportedRates = []byte{0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03, 0x01} - wpaRSN = []byte{ - 0x01, 0x00, // RSN Version 1 - 0x00, 0x0f, 0xac, 0x02, // Group Cipher Suite : 00-0f-ac TKIP - 0x02, 0x00, // 2 Pairwise Cipher Suites (next two lines) - 0x00, 0x0f, 0xac, 0x04, // AES Cipher / CCMP - 0x00, 0x0f, 0xac, 0x02, // TKIP Cipher - 0x01, 0x00, // 1 Authentication Key Managment Suite (line below) - 0x00, 0x0f, 0xac, 0x02, // Pre-Shared Key - 0x00, 0x00, - } -) - -type Dot11BeaconConfig struct { - SSID string - BSSID net.HardwareAddr - Channel int - Encryption bool -} - -func NewDot11Beacon(conf Dot11BeaconConfig) (error, []byte) { - flags := openFlags - if conf.Encryption == true { - flags = wpaFlags - } - - stack := []gopacket.SerializableLayer{ - &layers.RadioTap{}, - &layers.Dot11{ - Address1: network.BroadcastHw, - Address2: conf.BSSID, - Address3: conf.BSSID, - Type: layers.Dot11TypeMgmtBeacon, - }, - &layers.Dot11MgmtBeacon{ - Flags: uint16(flags), - Interval: 100, - }, - &layers.Dot11InformationElement{ - ID: layers.Dot11InformationElementIDSSID, - Length: uint8(len(conf.SSID) & 0xff), - Info: []byte(conf.SSID), - }, - &layers.Dot11InformationElement{ - ID: layers.Dot11InformationElementIDRates, - Length: uint8(len(supportedRates) & 0xff), - Info: supportedRates, - }, - &layers.Dot11InformationElement{ - ID: layers.Dot11InformationElementIDDSSet, - Length: 1, - Info: []byte{byte(conf.Channel & 0xff)}, - }, - } - - if conf.Encryption == true { - stack = append(stack, &layers.Dot11InformationElement{ - ID: layers.Dot11InformationElementIDRSNInfo, - Length: uint8(len(wpaRSN) & 0xff), - Info: wpaRSN, - }) - } - - return packets.Serialize(stack...) -} - -func (w *WiFiModule) sendBeaconPacket(counter int) { - w.writes.Add(1) - defer w.writes.Done() - - hw := make([]byte, 6) - rand.Read(hw) - - n := counter % len(w.frequencies) - - conf := Dot11BeaconConfig{ - SSID: fmt.Sprintf("Prova_%d", n), - BSSID: w.Session.Interface.HW, - Channel: network.Dot11Freq2Chan(w.frequencies[n]), - Encryption: true, - } - - if err, pkt := NewDot11Beacon(conf); err != nil { - log.Error("Could not create beacon packet: %s", err) - } else { - w.injectPacket(pkt) - } - - time.Sleep(100 * time.Millisecond) -} - -func (w *WiFiModule) startBeaconFlood() error { - // if not already running, temporarily enable the pcap handle - // for packet injection - if w.Running() == false { - if err := w.Configure(); err != nil { - return err - } - } - - go func() { - defer w.handle.Close() - for counter := 0; w.Running(); counter++ { - w.sendBeaconPacket(counter) - } - }() - - return nil -} diff --git a/modules/wifi_hopping.go b/modules/wifi_hopping.go index 6e0fddff..8fcb099e 100644 --- a/modules/wifi_hopping.go +++ b/modules/wifi_hopping.go @@ -27,6 +27,7 @@ func (w *WiFiModule) channelHopper() { defer w.reads.Done() log.Info("Channel hopper started.") + for w.Running() == true { delay := w.hopPeriod // if we have both 2.4 and 5ghz capabilities, we have @@ -45,6 +46,8 @@ func (w *WiFiModule) channelHopper() { channel = w.stickChan } + log.Debug("Hopping on channel %d", channel) + if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { log.Warning("Error while hopping to channel %d: %s", channel, err) } diff --git a/modules/wifi_recon.go b/modules/wifi_recon.go index 3e9cc7a2..b27c86e5 100644 --- a/modules/wifi_recon.go +++ b/modules/wifi_recon.go @@ -1,22 +1,16 @@ package modules import ( - "fmt" + "bytes" "net" - "strconv" - "strings" - "sync" "time" - "github.com/bettercap/bettercap/core" "github.com/bettercap/bettercap/log" "github.com/bettercap/bettercap/network" "github.com/bettercap/bettercap/packets" - "github.com/bettercap/bettercap/session" "github.com/google/gopacket" "github.com/google/gopacket/layers" - "github.com/google/gopacket/pcap" ) var maxStationTTL = 5 * time.Minute @@ -29,213 +23,33 @@ type WiFiProbe struct { RSSI int8 } -type WiFiModule struct { - session.SessionModule +func (w *WiFiModule) stationPruner() { + w.reads.Add(1) + defer w.reads.Done() - handle *pcap.Handle - source string - channel int - hopPeriod time.Duration - frequencies []int - ap *network.AccessPoint - stickChan int - skipBroken bool - pktSourceChan chan gopacket.Packet - pktSourceChanClosed bool - writes *sync.WaitGroup - reads *sync.WaitGroup -} - -func NewWiFiModule(s *session.Session) *WiFiModule { - w := &WiFiModule{ - SessionModule: session.NewSessionModule("wifi.recon", s), - channel: 0, - stickChan: 0, - hopPeriod: 250 * time.Millisecond, - ap: nil, - skipBroken: true, - writes: &sync.WaitGroup{}, - reads: &sync.WaitGroup{}, - } - - w.AddHandler(session.NewModuleHandler("wifi.recon on", "", - "Start 802.11 wireless base stations discovery.", - func(args []string) error { - return w.Start() - })) - - w.AddHandler(session.NewModuleHandler("wifi.recon off", "", - "Stop 802.11 wireless base stations discovery.", - func(args []string) error { - return w.Stop() - })) - - w.AddHandler(session.NewModuleHandler("wifi.recon MAC", "wifi.recon ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))", - "Set 802.11 base station address to filter for.", - func(args []string) error { - bssid, err := net.ParseMAC(args[0]) - if err != nil { - return err - } else if ap, found := w.Session.WiFi.Get(bssid.String()); found == true { - w.ap = ap - w.stickChan = network.Dot11Freq2Chan(ap.Frequency) - return nil + log.Debug("WiFi stations pruner started.") + for w.Running() == true { + for _, s := range w.Session.WiFi.List() { + sinceLastSeen := time.Since(s.LastSeen) + if sinceLastSeen > maxStationTTL { + log.Debug("Station %s not seen in %s, removing.", s.BSSID(), sinceLastSeen) + w.Session.WiFi.Remove(s.BSSID()) } - return fmt.Errorf("Could not find station with BSSID %s", args[0]) - })) - - w.AddHandler(session.NewModuleHandler("wifi.recon clear", "", - "Remove the 802.11 base station filter.", - func(args []string) error { - w.ap = nil - w.stickChan = 0 - var err error - if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { - return err - } - return nil - })) - - w.AddHandler(session.NewModuleHandler("wifi.deauth BSSID", `wifi\.deauth ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))`, - "Start a 802.11 deauth attack, if an access point BSSID is provided, every client will be deauthenticated, otherwise only the selected client.", - func(args []string) error { - bssid, err := net.ParseMAC(args[0]) - if err != nil { - return err - } - return w.startDeauth(bssid) - })) - - w.AddHandler(session.NewModuleHandler("wifi.beacon.flood", "", - "todo", - func(args []string) error { - return w.startBeaconFlood() - })) - - w.AddHandler(session.NewModuleHandler("wifi.show", "", - "Show current wireless stations list (default sorting by essid).", - func(args []string) error { - return w.Show("rssi") - })) - - w.AddHandler(session.NewModuleHandler("wifi.recon.channel", `wifi\.recon\.channel[\s]+([0-9]+(?:[, ]+[0-9]+)*|clear)`, - "WiFi channels (comma separated) or 'clear' for channel hopping.", - func(args []string) error { - newfrequencies := w.frequencies[:0] - - if len(args) > 0 && args[0] != "clear" { - channels := strings.Split(args[0], ",") - for _, c := range channels { - trimmed := strings.Trim(c, " ") - channel, err := strconv.Atoi(trimmed) - if err != nil { - return err - } - newfrequencies = append(newfrequencies, network.Dot11Chan2Freq(channel)) - } - } else { - // No channels setted, retrieve frequencies supported by the card - if frequencies, err := network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { - return err - } else { - newfrequencies = frequencies - } - } - - w.frequencies = newfrequencies - - return nil - })) - - w.AddParam(session.NewStringParameter("wifi.source.file", - "", - "", - "If set, the wifi module will read from this pcap file instead of the hardware interface.")) - - w.AddParam(session.NewIntParameter("wifi.hop.period", - "250", - "If channel hopping is enabled (empty wifi.recon.channel), this is the time in milliseconds the algorithm will hop on every channel (it'll be doubled if both 2.4 and 5.0 bands are available).")) - - w.AddParam(session.NewBoolParameter("wifi.skip-broken", - "true", - "If true, dot11 packets with an invalid checksum will be skipped.")) - - return w -} - -func (w WiFiModule) Name() string { - return "wifi.recon" -} - -func (w WiFiModule) Description() string { - return "A module to monitor and perform wireless attacks on 802.11." -} - -func (w WiFiModule) Author() string { - return "Gianluca Braga && Simone Margaritelli >" -} - -func (w *WiFiModule) Configure() error { - var hopPeriod int - var err error - - if err, w.source = w.StringParam("wifi.source.file"); err != nil { - return err - } - - if w.source != "" { - if w.handle, err = pcap.OpenOffline(w.source); err != nil { - return err - } - } else { - ihandle, err := pcap.NewInactiveHandle(w.Session.Interface.Name()) - if err != nil { - return err - } - defer ihandle.CleanUp() - - if err = ihandle.SetRFMon(true); err != nil { - return fmt.Errorf("Error while setting interface %s in monitor mode: %s", core.Bold(w.Session.Interface.Name()), err) - } else if err = ihandle.SetSnapLen(65536); err != nil { - return err - } else if err = ihandle.SetTimeout(pcap.BlockForever); err != nil { - return err - } else if w.handle, err = ihandle.Activate(); err != nil { - return err } + time.Sleep(1 * time.Second) } - - if err, w.skipBroken = w.BoolParam("wifi.skip-broken"); err != nil { - return err - } else if err, hopPeriod = w.IntParam("wifi.hop.period"); err != nil { - return err - } - - w.hopPeriod = time.Duration(hopPeriod) * time.Millisecond - - if w.source == "" { - // No channels setted, retrieve frequencies supported by the card - if len(w.frequencies) == 0 { - if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { - return err - } - - // we need to start somewhere, this is just to check if - // this OS supports switching channel programmatically. - if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil { - return err - } - log.Info("WiFi recon active with channel hopping.") - } - } - - return nil } func (w *WiFiModule) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) { // search for Dot11InformationElementIDSSID if ok, ssid := packets.Dot11ParseIDSSID(packet); ok == true { from := dot11.Address3 + + // skip stuff we're sending + if w.apRunning && bytes.Compare(from, w.apConfig.BSSID) == 0 { + return + } + if network.IsZeroMac(from) == false && network.IsBroadcastMac(from) == false { var frequency int bssid := from.String() @@ -297,118 +111,3 @@ func (w *WiFiModule) discoverClients(radiotap *layers.RadioTap, dot11 *layers.Do } }) } - -func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) { - // collect stats from data frames - if dot11.Type.MainType() == layers.Dot11TypeData { - bytes := uint64(len(packet.Data())) - - dst := dot11.Address1.String() - if station, found := w.Session.WiFi.Get(dst); found == true { - station.Received += bytes - } - - src := dot11.Address2.String() - if station, found := w.Session.WiFi.Get(src); found == true { - station.Sent += bytes - } - } - - if ok, enc, cipher, auth := packets.Dot11ParseEncryption(packet, dot11); ok == true { - bssid := dot11.Address3.String() - if station, found := w.Session.WiFi.Get(bssid); found == true { - station.Encryption = enc - station.Cipher = cipher - station.Authentication = auth - } - } -} - -func (w *WiFiModule) stationPruner() { - w.reads.Add(1) - defer w.reads.Done() - - log.Debug("WiFi stations pruner started.") - for w.Running() == true { - for _, s := range w.Session.WiFi.List() { - sinceLastSeen := time.Since(s.LastSeen) - if sinceLastSeen > maxStationTTL { - log.Debug("Station %s not seen in %s, removing.", s.BSSID(), sinceLastSeen) - w.Session.WiFi.Remove(s.BSSID()) - } - } - time.Sleep(1 * time.Second) - } -} - -func (w *WiFiModule) trackPacket(pkt gopacket.Packet) { - pktSize := uint64(len(pkt.Data())) - - w.Session.Queue.Stats.Lock() - - w.Session.Queue.Stats.PktReceived++ - w.Session.Queue.Stats.Received += pktSize - - w.Session.Queue.Stats.Unlock() -} - -func (w *WiFiModule) Start() error { - if err := w.Configure(); err != nil { - return err - } - - w.SetRunning(true, func() { - // start the pruner - go w.stationPruner() - - w.reads.Add(1) - defer w.reads.Done() - - src := gopacket.NewPacketSource(w.handle, w.handle.LinkType()) - w.pktSourceChan = src.Packets() - for packet := range w.pktSourceChan { - if w.Running() == false { - break - } - - if packet == nil { - continue - } - - w.trackPacket(packet) - - // perform initial dot11 parsing and layers validation - if ok, radiotap, dot11 := packets.Dot11Parse(packet); ok == true { - // check FCS checksum - if w.skipBroken && dot11.ChecksumValid() == false { - log.Debug("Skipping dot11 packet with invalid checksum.") - continue - } - - w.discoverProbes(radiotap, dot11, packet) - w.discoverAccessPoints(radiotap, dot11, packet) - w.discoverClients(radiotap, dot11, packet) - w.updateStats(dot11, packet) - } - } - w.pktSourceChanClosed = true - }) - - return nil -} - -func (w *WiFiModule) Stop() error { - return w.SetRunning(false, func() { - // wait any pending write operation - w.writes.Wait() - // signal the main for loop we want to exit - if w.pktSourceChanClosed == false { - w.pktSourceChan <- nil - } - // close the pcap handle to make the main for exit - w.handle.Close() - // close the pcap handle to make the main for exit - // wait for the loop to exit. - w.reads.Wait() - }) -} diff --git a/packets/dot11.go b/packets/dot11.go index ae27126b..14ebec09 100644 --- a/packets/dot11.go +++ b/packets/dot11.go @@ -4,11 +4,82 @@ import ( "bytes" "net" + "github.com/bettercap/bettercap/network" + "github.com/google/gopacket" "github.com/google/gopacket/layers" ) -var wpaSignatureBytes = []byte{0, 0x50, 0xf2, 1} +var ( + openFlags = 1057 + wpaFlags = 1041 + //1-54 Mbit + supportedRates = []byte{0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03, 0x01} + wpaRSN = []byte{ + 0x01, 0x00, // RSN Version 1 + 0x00, 0x0f, 0xac, 0x02, // Group Cipher Suite : 00-0f-ac TKIP + 0x02, 0x00, // 2 Pairwise Cipher Suites (next two lines) + 0x00, 0x0f, 0xac, 0x04, // AES Cipher / CCMP + 0x00, 0x0f, 0xac, 0x02, // TKIP Cipher + 0x01, 0x00, // 1 Authentication Key Managment Suite (line below) + 0x00, 0x0f, 0xac, 0x02, // Pre-Shared Key + 0x00, 0x00, + } + wpaSignatureBytes = []byte{0, 0x50, 0xf2, 1} +) + +type Dot11ApConfig struct { + SSID string + BSSID net.HardwareAddr + Channel int + Encryption bool +} + +func Dot11Info(id layers.Dot11InformationElementID, info []byte) *layers.Dot11InformationElement { + return &layers.Dot11InformationElement{ + ID: id, + Length: uint8(len(info) & 0xff), + Info: info, + } +} + +func NewDot11Beacon(conf Dot11ApConfig, seq uint16) (error, []byte) { + flags := openFlags + if conf.Encryption == true { + flags = wpaFlags + } + + stack := []gopacket.SerializableLayer{ + &layers.RadioTap{ + DBMAntennaSignal: int8(-10), + ChannelFrequency: layers.RadioTapChannelFrequency(network.Dot11Chan2Freq(conf.Channel)), + }, + &layers.Dot11{ + Address1: network.BroadcastHw, + Address2: conf.BSSID, + Address3: conf.BSSID, + Type: layers.Dot11TypeMgmtBeacon, + SequenceNumber: seq, + }, + &layers.Dot11MgmtBeacon{ + Flags: uint16(flags), + Interval: 100, + }, + Dot11Info(layers.Dot11InformationElementIDSSID, []byte(conf.SSID)), + Dot11Info(layers.Dot11InformationElementIDRates, supportedRates), + Dot11Info(layers.Dot11InformationElementIDDSSet, []byte{byte(conf.Channel & 0xff)}), + } + + if conf.Encryption == true { + stack = append(stack, &layers.Dot11InformationElement{ + ID: layers.Dot11InformationElementIDRSNInfo, + Length: uint8(len(wpaRSN) & 0xff), + Info: wpaRSN, + }) + } + + return Serialize(stack...) +} func NewDot11Deauth(a1 net.HardwareAddr, a2 net.HardwareAddr, a3 net.HardwareAddr, seq uint16) (error, []byte) { return Serialize( diff --git a/session/events.go b/session/events.go index 9fe075ab..9d64e7fa 100644 --- a/session/events.go +++ b/session/events.go @@ -85,8 +85,6 @@ func (p *EventPool) Add(tag string, data interface{}) { for _, l := range p.listeners { select { case l <- e: - default: - fmt.Fprintf(os.Stderr, "Message not sent!\n") } } }