From da5d1d46d4c8aed148de530a61502e28dd1ff7f7 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Tue, 20 Feb 2018 18:14:38 +0100 Subject: [PATCH] refact: big refactoring and improvements of wifi.* modules --- modules/events_view.go | 31 ++++---- modules/wifi_recon.go | 162 +++++++++++++++++++++++++--------------- network/wifi.go | 86 +++++++++++++-------- network/wifi_ap.go | 88 ++++++++++++++++++++++ network/wifi_station.go | 14 ++-- session/session.go | 8 +- 6 files changed, 266 insertions(+), 123 deletions(-) create mode 100644 network/wifi_ap.go diff --git a/modules/events_view.go b/modules/events_view.go index ba6aa677..e24e7b9d 100644 --- a/modules/events_view.go +++ b/modules/events_view.go @@ -19,31 +19,30 @@ func (s EventsStream) viewLogEvent(e session.Event) { e.Data.(session.LogMessage).Message) } -func (s EventsStream) viewStationEvent(e session.Event) { - st := e.Data.(*network.Station) +func (s EventsStream) viewApEvent(e session.Event) { + ap := e.Data.(*network.AccessPoint) vend := "" - if st.Vendor != "" { - vend = fmt.Sprintf(" (%s)", st.Vendor) + if ap.Vendor != "" { + vend = fmt.Sprintf(" (%s)", ap.Vendor) } - if e.Tag == "wifi.station.new" { - fmt.Printf("[%s] WiFi station %s detected as %s%s.\n", + if e.Tag == "wifi.ap.new" { + fmt.Printf("[%s] WiFi access point %s detected as %s%s.\n", e.Time.Format(eventTimeFormat), - core.Bold(st.ESSID()), - core.Green(st.BSSID()), + core.Bold(ap.ESSID()), + core.Green(ap.BSSID()), vend) - } else if e.Tag == "wifi.station.lost" { - fmt.Printf("[%s] WiFi station %s (%s) lost.\n", + } else if e.Tag == "wifi.ap.lost" { + fmt.Printf("[%s] WiFi access point %s (%s) lost.\n", e.Time.Format(eventTimeFormat), - core.Red(st.ESSID()), - st.BSSID()) + core.Red(ap.ESSID()), + ap.BSSID()) } else { fmt.Printf("[%s] [%s] %s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), - st) + ap) } - } func (s EventsStream) viewEndpointEvent(e session.Event) { @@ -101,8 +100,8 @@ func (s *EventsStream) View(e session.Event, refresh bool) { s.viewLogEvent(e) } else if strings.HasPrefix(e.Tag, "endpoint.") { s.viewEndpointEvent(e) - } else if strings.HasPrefix(e.Tag, "wifi.station.") { - s.viewStationEvent(e) + } else if strings.HasPrefix(e.Tag, "wifi.ap.") { + s.viewApEvent(e) } else if strings.HasPrefix(e.Tag, "mod.") { s.viewModuleEvent(e) } else if strings.HasPrefix(e.Tag, "net.sniff.") { diff --git a/modules/wifi_recon.go b/modules/wifi_recon.go index 902e85a2..841bc20b 100644 --- a/modules/wifi_recon.go +++ b/modules/wifi_recon.go @@ -31,14 +31,16 @@ type WiFiRecon struct { handle *pcap.Handle channel int frequencies []int - apBSSID net.HardwareAddr + ap *network.AccessPoint + stickChan int } func NewWiFiRecon(s *session.Session) *WiFiRecon { w := &WiFiRecon{ SessionModule: session.NewSessionModule("wifi.recon", s), channel: 0, - apBSSID: nil, + stickChan: 0, + ap: nil, } w.AddHandler(session.NewModuleHandler("wifi.recon on", "", @@ -56,41 +58,33 @@ func NewWiFiRecon(s *session.Session) *WiFiRecon { 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 { - var err error - w.Session.WiFi.Clear() - w.apBSSID, err = net.ParseMAC(args[0]) - return err + 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 = mhz2chan(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.Session.WiFi.Clear() - w.apBSSID = nil + w.ap = nil + w.stickChan = 0 return nil })) - w.AddHandler(session.NewModuleHandler("wifi.deauth AP-BSSID TARGET-BSSID", `wifi\.deauth ([a-fA-F0-9\s:]+)`, - "Start a 802.11 deauth attack, while the AP-BSSID is mandatory, if no TARGET-BSSID is specified the deauth will be executed against every connected client.", + w.AddHandler(session.NewModuleHandler("wifi.deauth AP-BSSID TARGET-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 { - err := (error)(nil) - apMac := (net.HardwareAddr)(nil) - clMac := (net.HardwareAddr)(nil) - parts := strings.SplitN(args[0], " ", 2) - - if len(parts) == 2 { - if apMac, err = net.ParseMAC(parts[0]); err != nil { - return err - } else if clMac, err = net.ParseMAC(parts[1]); err != nil { - return err - } - } else { - if apMac, err = net.ParseMAC(parts[0]); err != nil { - return err - } + bssid, err := net.ParseMAC(args[0]) + if err != nil { + return err } - - return w.startDeauth(apMac, clMac) + return w.startDeauth(bssid) })) w.AddHandler(session.NewModuleHandler("wifi.show", "", @@ -115,7 +109,7 @@ func (w WiFiRecon) Description() string { } func (w WiFiRecon) Author() string { - return "Gianluca Braga " + return "Gianluca Braga && Simone Margaritelli >" } func (w *WiFiRecon) getRow(station *network.Station) []string { @@ -164,6 +158,11 @@ func (w *WiFiRecon) getRow(station *network.Station) []string { seen, } } else { + // this is ugly, but necessary in order to have this + // method handle both access point and clients + // transparently + ap, _ := w.Session.WiFi.Get(station.HwAddress) + return []string{ fmt.Sprintf("%d dBm", station.RSSI), bssid, @@ -171,6 +170,7 @@ func (w *WiFiRecon) getRow(station *network.Station) []string { station.Vendor, encryption, strconv.Itoa(mhz2chan(station.Frequency)), + strconv.Itoa(ap.NumClients()), sent, recvd, seen, @@ -200,11 +200,23 @@ func (w *WiFiRecon) showTable(header []string, rows [][]string) { } func (w *WiFiRecon) isApSelected() bool { - return w.apBSSID != nil + return w.ap != nil } func (w *WiFiRecon) Show(by string) error { - stations := w.Session.WiFi.List() + var stations []*network.Station + + apSelected := w.isApSelected() + if apSelected { + if ap, found := w.Session.WiFi.Get(w.ap.HwAddress); found == true { + stations = ap.Clients() + } else { + return fmt.Errorf("Could not find station %s", w.ap.HwAddress) + } + } else { + stations = w.Session.WiFi.Stations() + } + if by == "seen" { sort.Sort(ByWiFiSeenSorter(stations)) } else if by == "essid" { @@ -221,15 +233,15 @@ func (w *WiFiRecon) Show(by string) error { } nrows := len(rows) - columns := []string{"RSSI", "BSSID", "SSID", "Vendor", "Encryption", "Channel", "Sent", "Recvd", "Last Seen"} - if w.isApSelected() { + columns := []string{"RSSI", "BSSID", "SSID", "Vendor", "Encryption", "Channel", "Clients", "Sent", "Recvd", "Last Seen"} + if apSelected { // these are clients columns = []string{"RSSI", "MAC", "Vendor", "Channel", "Sent", "Received", "Last Seen"} if nrows == 0 { - fmt.Printf("\nNo authenticated clients detected for %s.\n", w.apBSSID.String()) + fmt.Printf("\nNo authenticated clients detected for %s.\n", w.ap.HwAddress) } else { - fmt.Printf("\n%s clients:\n", w.apBSSID.String()) + fmt.Printf("\n%s clients:\n", w.ap.HwAddress) } } @@ -305,7 +317,20 @@ func (w *WiFiRecon) sendDeauthPacket(ap net.HardwareAddr, client net.HardwareAdd } } -func (w *WiFiRecon) startDeauth(apMac net.HardwareAddr, clMac net.HardwareAddr) error { +func (w *WiFiRecon) onChannel(channel int, cb func()) { + prev := w.stickChan + w.stickChan = channel + + if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { + log.Warning("Error while hopping to channel %d: %s", channel, err) + } + + cb() + + w.stickChan = prev +} + +func (w *WiFiRecon) startDeauth(to net.HardwareAddr) error { // if not already running, temporarily enable the pcap handle // for packet injection if w.Running() == false { @@ -315,21 +340,34 @@ func (w *WiFiRecon) startDeauth(apMac net.HardwareAddr, clMac net.HardwareAddr) defer w.handle.Close() } - // deauth a specific client - if clMac != nil { - log.Info("Deauthing client %s from AP %s ...", clMac.String(), apMac.String()) - w.sendDeauthPacket(apMac, clMac) - } else { - log.Info("Deauthing clients from AP %s ...", apMac.String()) - // deauth every authenticated client - for _, station := range w.Session.WiFi.List() { - if station.IsAP == false { - w.sendDeauthPacket(apMac, station.HW) + bssid := to.String() + + // are we deauthing every client of a given access point? + if ap, found := w.Session.WiFi.Get(bssid); found == true { + clients := ap.Clients() + log.Info("Deauthing %d clients from AP %s ...", len(clients), ap.ESSID()) + w.onChannel(mhz2chan(ap.Frequency), func() { + for _, c := range clients { + w.sendDeauthPacket(ap.HW, c.HW) } + }) + + return nil + } + + // search for a client + aps := w.Session.WiFi.List() + for _, ap := range aps { + if c, found := ap.Get(bssid); found == true { + log.Info("Deauthing client %s from AP %s ...", c.HwAddress, ap.ESSID()) + w.onChannel(mhz2chan(ap.Frequency), func() { + w.sendDeauthPacket(ap.HW, c.HW) + }) + return nil } } - return nil + return fmt.Errorf("%s is an unknown BSSID.", bssid) } func (w *WiFiRecon) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) { @@ -337,17 +375,17 @@ func (w *WiFiRecon) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layer if ok, ssid := packets.Dot11ParseIDSSID(packet); ok == true { bssid := dot11.Address3.String() frequency := int(radiotap.ChannelFrequency) - w.Session.WiFi.AddIfNew(ssid, bssid, true, frequency, radiotap.DBMAntennaSignal) + w.Session.WiFi.AddIfNew(ssid, bssid, frequency, radiotap.DBMAntennaSignal) } } -func (w *WiFiRecon) discoverClients(radiotap *layers.RadioTap, dot11 *layers.Dot11, ap net.HardwareAddr, packet gopacket.Packet) { - // packet going to this specific BSSID? - if packets.Dot11IsDataFor(dot11, ap) == true { - src := dot11.Address2 - frequency := int(radiotap.ChannelFrequency) - w.Session.WiFi.AddIfNew("", src.String(), false, frequency, radiotap.DBMAntennaSignal) - } +func (w *WiFiRecon) discoverClients(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) { + w.Session.WiFi.EachAccessPoint(func(bssid string, ap *network.AccessPoint) { + // packet going to this specific BSSID? + if packets.Dot11IsDataFor(dot11, ap.HW) == true { + ap.AddClient(dot11.Address2.String(), int(radiotap.ChannelFrequency), radiotap.DBMAntennaSignal) + } + }) } func (w *WiFiRecon) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) { @@ -387,6 +425,12 @@ func (w *WiFiRecon) channelHopper() { for _, frequency := range w.frequencies { channel := mhz2chan(frequency) + // stick to the access point channel as long as it's selected + // or as long as we're deauthing on it + if w.stickChan != 0 { + channel = w.stickChan + } + if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { log.Warning("Error while hopping to channel %d: %s", channel, err) } @@ -438,15 +482,9 @@ func (w *WiFiRecon) Start() error { // perform initial dot11 parsing and layers validation if ok, radiotap, dot11 := packets.Dot11Parse(packet); ok == true { - // keep collecting traffic statistics w.updateStats(dot11, packet) - // no access point bssid selected, keep scanning for other aps - if w.isApSelected() == false { - w.discoverAccessPoints(radiotap, dot11, packet) - } else { - // discover stations connected to the selected access point bssid - w.discoverClients(radiotap, dot11, w.apBSSID, packet) - } + w.discoverAccessPoints(radiotap, dot11, packet) + w.discoverClients(radiotap, dot11, packet) } } }) diff --git a/network/wifi.go b/network/wifi.go index 92d00f18..e43debb0 100644 --- a/network/wifi.go +++ b/network/wifi.go @@ -6,50 +6,70 @@ import ( "time" ) -type StationNewCallback func(s *Station) -type StationLostCallback func(s *Station) +type APNewCallback func(ap *AccessPoint) +type APLostCallback func(ap *AccessPoint) type WiFi struct { sync.Mutex - stations map[string]*Station - iface *Endpoint - newCb StationNewCallback - lostCb StationLostCallback + aps map[string]*AccessPoint + iface *Endpoint + newCb APNewCallback + lostCb APLostCallback } type wifiJSON struct { - Stations []*Station `json:"stations"` + AccessPoints []*AccessPoint `json:"aps"` } -func NewWiFi(iface *Endpoint, newcb StationNewCallback, lostcb StationLostCallback) *WiFi { +func NewWiFi(iface *Endpoint, newcb APNewCallback, lostcb APLostCallback) *WiFi { return &WiFi{ - stations: make(map[string]*Station), - iface: iface, - newCb: newcb, - lostCb: lostcb, + aps: make(map[string]*AccessPoint), + iface: iface, + newCb: newcb, + lostCb: lostcb, } } func (w *WiFi) MarshalJSON() ([]byte, error) { doc := wifiJSON{ - Stations: make([]*Station, 0), + AccessPoints: make([]*AccessPoint, 0), } - for _, s := range w.stations { - doc.Stations = append(doc.Stations, s) + for _, ap := range w.aps { + doc.AccessPoints = append(doc.AccessPoints, ap) } return json.Marshal(doc) } -func (w *WiFi) List() (list []*Station) { +func (w *WiFi) EachAccessPoint(cb func(mac string, ap *AccessPoint)) { + w.Lock() + defer w.Unlock() + + for m, ap := range w.aps { + cb(m, ap) + } +} + +func (w *WiFi) Stations() (list []*Station) { w.Lock() defer w.Unlock() list = make([]*Station, 0) - for _, t := range w.stations { - list = append(list, t) + for _, ap := range w.aps { + list = append(list, ap.Station) + } + return +} + +func (w *WiFi) List() (list []*AccessPoint) { + w.Lock() + defer w.Unlock() + + list = make([]*AccessPoint, 0) + for _, ap := range w.aps { + list = append(list, ap) } return } @@ -58,45 +78,45 @@ func (w *WiFi) Remove(mac string) { w.Lock() defer w.Unlock() - if s, found := w.stations[mac]; found { - delete(w.stations, mac) + if ap, found := w.aps[mac]; found { + delete(w.aps, mac) if w.lostCb != nil { - w.lostCb(s) + w.lostCb(ap) } } } -func (w *WiFi) AddIfNew(ssid, mac string, isAp bool, frequency int, rssi int8) *Station { +func (w *WiFi) AddIfNew(ssid, mac string, frequency int, rssi int8) *AccessPoint { w.Lock() defer w.Unlock() mac = NormalizeMac(mac) - if station, found := w.stations[mac]; found { - station.LastSeen = time.Now() - station.RSSI = rssi - return station + if ap, found := w.aps[mac]; found { + ap.LastSeen = time.Now() + ap.RSSI = rssi + return ap } - newStation := NewStation(ssid, mac, isAp, frequency, rssi) - w.stations[mac] = newStation + newAp := NewAccessPoint(ssid, mac, frequency, rssi) + w.aps[mac] = newAp if w.newCb != nil { - w.newCb(newStation) + w.newCb(newAp) } return nil } -func (w *WiFi) Get(mac string) (*Station, bool) { +func (w *WiFi) Get(mac string) (*AccessPoint, bool) { w.Lock() defer w.Unlock() mac = NormalizeMac(mac) - station, found := w.stations[mac] - return station, found + ap, found := w.aps[mac] + return ap, found } func (w *WiFi) Clear() error { - w.stations = make(map[string]*Station) + w.aps = make(map[string]*AccessPoint) return nil } diff --git a/network/wifi_ap.go b/network/wifi_ap.go new file mode 100644 index 00000000..ba414a82 --- /dev/null +++ b/network/wifi_ap.go @@ -0,0 +1,88 @@ +package network + +import ( + "encoding/json" + "sync" + "time" +) + +type AccessPoint struct { + *Station + sync.Mutex + + clients map[string]*Station +} + +type apJSON struct { + *Station + Clients []*Station `json:"clients"` +} + +func NewAccessPoint(essid, bssid string, frequency int, rssi int8) *AccessPoint { + return &AccessPoint{ + Station: NewStation(essid, bssid, frequency, rssi), + clients: make(map[string]*Station), + } +} + +func (ap *AccessPoint) MarshalJSON() ([]byte, error) { + doc := apJSON{ + Station: ap.Station, + Clients: make([]*Station, 0), + } + + for _, c := range ap.clients { + doc.Clients = append(doc.Clients, c) + } + + return json.Marshal(doc) +} + +func (ap *AccessPoint) Get(bssid string) (*Station, bool) { + ap.Lock() + defer ap.Unlock() + + bssid = NormalizeMac(bssid) + if s, found := ap.clients[bssid]; found == true { + return s, true + } + return nil, false +} + +func (ap *AccessPoint) AddClient(bssid string, frequency int, rssi int8) *Station { + ap.Lock() + defer ap.Unlock() + + bssid = NormalizeMac(bssid) + + if s, found := ap.clients[bssid]; found == true { + // update + s.Frequency = frequency + s.RSSI = rssi + s.LastSeen = time.Now() + + return s + } + + s := NewStation("", bssid, frequency, rssi) + ap.clients[bssid] = s + + return s +} + +func (ap *AccessPoint) NumClients() int { + ap.Lock() + defer ap.Unlock() + return len(ap.clients) +} + +func (ap *AccessPoint) Clients() (list []*Station) { + ap.Lock() + defer ap.Unlock() + + list = make([]*Station, 0) + for _, c := range ap.clients { + list = append(list, c) + } + return +} diff --git a/network/wifi_station.go b/network/wifi_station.go index 9a755905..b0659da0 100644 --- a/network/wifi_station.go +++ b/network/wifi_station.go @@ -2,18 +2,16 @@ package network type Station struct { *Endpoint - IsAP bool - Frequency int - RSSI int8 - Sent uint64 - Received uint64 - Encryption string + Frequency int `json:"frequency"` + RSSI int8 `json:"rssi"` + Sent uint64 `json:"sent"` + Received uint64 `json:"received"` + Encryption string `json:"encryption"` } -func NewStation(essid, bssid string, isAp bool, frequency int, rssi int8) *Station { +func NewStation(essid, bssid string, frequency int, rssi int8) *Station { return &Station{ Endpoint: NewEndpointNoResolve(MonitorModeAddress, bssid, essid, 0), - IsAP: isAp, Frequency: frequency, RSSI: rssi, } diff --git a/session/session.go b/session/session.go index 3ba960a6..311212d6 100644 --- a/session/session.go +++ b/session/session.go @@ -378,10 +378,10 @@ func (s *Session) Start() error { s.Firewall = firewall.Make(s.Interface) - s.WiFi = network.NewWiFi(s.Interface, func(st *network.Station) { - s.Events.Add("wifi.station.new", st) - }, func(st *network.Station) { - s.Events.Add("wifi.station.lost", st) + s.WiFi = network.NewWiFi(s.Interface, func(ap *network.AccessPoint) { + s.Events.Add("wifi.ap.new", ap) + }, func(ap *network.AccessPoint) { + s.Events.Add("wifi.ap.lost", ap) }) s.Lan = network.NewLAN(s.Interface, s.Gateway, func(e *network.Endpoint) {