package wifi import ( "bytes" "fmt" "net" "sort" "github.com/bettercap/bettercap/v2/network" "github.com/bettercap/bettercap/v2/packets" ) func (mod *WiFiModule) sendAssocPacket(ap *network.AccessPoint) { if err, pkt := packets.NewDot11Auth(mod.iface.HW, ap.HW, 1); err != nil { mod.Error("cloud not create auth packet: %s", err) } else { mod.injectPacket(pkt) } if err, pkt := packets.NewDot11AssociationRequest(mod.iface.HW, ap.HW, ap.ESSID(), 1); err != nil { mod.Error("cloud not create association request packet: %s", err) } else { mod.injectPacket(pkt) } } func (mod *WiFiModule) skipAssoc(to net.HardwareAddr) bool { for _, mac := range mod.assocSkip { if bytes.Equal(to, mac) { return true } } return false } func (mod *WiFiModule) isAssocSilent() bool { if err, is := mod.BoolParam("wifi.assoc.silent"); err != nil { mod.Warning("%v", err) } else { mod.assocSilent = is } return mod.assocSilent } func (mod *WiFiModule) doAssocOpen() bool { if err, is := mod.BoolParam("wifi.assoc.open"); err != nil { mod.Warning("%v", err) } else { mod.assocOpen = is } return mod.assocOpen } func (mod *WiFiModule) doAssocAcquired() bool { if err, is := mod.BoolParam("wifi.assoc.acquired"); err != nil { mod.Warning("%v", err) } else { mod.assocAcquired = is } return mod.assocAcquired } func (mod *WiFiModule) startAssoc(to net.HardwareAddr) error { // parse skip list if err, assocSkip := mod.StringParam("wifi.assoc.skip"); err != nil { return err } else if macs, err := network.ParseMACs(assocSkip); err != nil { return err } else { mod.assocSkip = macs } // 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() } toAssoc := make([]*network.AccessPoint, 0) isBcast := network.IsBroadcastMac(to) for _, ap := range mod.Session.WiFi.List() { if isBcast || bytes.Equal(ap.HW, to) { if !mod.skipAssoc(ap.HW) { toAssoc = append(toAssoc, ap) } else { mod.Debug("skipping ap:%v because skip list %v", ap, mod.assocSkip) } } } if len(toAssoc) == 0 { if isBcast { return nil } return fmt.Errorf("%s is an unknown BSSID or it is in the association skip list.", to.String()) } mod.writes.Add(1) go func() { defer mod.writes.Done() // since we need to change the wifi adapter channel for each // association request, let's sort by channel so we do the minimum // amount of hops possible sort.Slice(toAssoc, func(i, j int) bool { return toAssoc[i].Channel < toAssoc[j].Channel }) // send the association request frames for _, ap := range toAssoc { if mod.Running() { logger := mod.Info if mod.isAssocSilent() { logger = mod.Debug } if ap.IsOpen() && !mod.doAssocOpen() { mod.Debug("skipping association for open network %s (wifi.assoc.open is false)", ap.ESSID()) } else if ap.HasKeyMaterial() && !mod.doAssocAcquired() { mod.Debug("skipping association for AP %s (key material already acquired)", ap.ESSID()) } else { logger("sending association request to AP %s (channel:%d encryption:%s)", ap.ESSID(), ap.Channel, ap.Encryption) mod.onChannel(ap.Channel, func() { mod.sendAssocPacket(ap) }) } } } }() return nil }