From 50bf22af9cac7c11f22db9e7e1d5e5895c911d45 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Sun, 27 Jan 2019 14:52:59 +0100 Subject: [PATCH] new: new WPS parsing capabilities and wifi.show.wps command --- modules/wifi.go | 33 ++++++++--- modules/wifi_show.go | 77 +++++++++++++++++++++++--- network/wifi_station.go | 19 ++++--- packets/dot11_wps.go | 118 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 23 deletions(-) create mode 100644 packets/dot11_wps.go diff --git a/modules/wifi.go b/modules/wifi.go index 20f87868..aab45e6f 100644 --- a/modules/wifi.go +++ b/modules/wifi.go @@ -141,6 +141,12 @@ func NewWiFiModule(s *session.Session) *WiFiModule { "true", "If true, the fake access point will use WPA2, otherwise it'll result as an open AP.")) + w.AddHandler(session.NewModuleHandler("wifi.show.wps BSSID", "wifi.show.wps ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))", + "Show WPS information about a given station.", + func(args []string) error { + return w.ShowWPS(args[0]) + })) + w.AddHandler(session.NewModuleHandler("wifi.show", "", "Show current wireless stations list (default sorting by essid).", func(args []string) error { @@ -266,6 +272,23 @@ func (w *WiFiModule) Configure() error { return nil } +func (w *WiFiModule) updateInfo(dot11 *layers.Dot11, packet gopacket.Packet) { + if ok, enc, cipher, auth := packets.Dot11ParseEncryption(packet, dot11); ok { + bssid := dot11.Address3.String() + if station, found := w.Session.WiFi.Get(bssid); found { + station.Encryption = enc + station.Cipher = cipher + station.Authentication = auth + } + } + + if ok, bssid, info := packets.Dot11ParseWPS(packet, dot11); ok { + if station, found := w.Session.WiFi.Get(bssid.String()); found { + station.WPS = info + } + } +} + func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) { // collect stats from data frames if dot11.Type.MainType() == layers.Dot11TypeData { @@ -281,15 +304,6 @@ func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) { station.Sent += bytes } } - - if ok, enc, cipher, auth := packets.Dot11ParseEncryption(packet, dot11); ok { - bssid := dot11.Address3.String() - if station, found := w.Session.WiFi.Get(bssid); found { - station.Encryption = enc - station.Cipher = cipher - station.Authentication = auth - } - } } func (w *WiFiModule) Start() error { @@ -331,6 +345,7 @@ func (w *WiFiModule) Start() error { w.discoverProbes(radiotap, dot11, packet) w.discoverAccessPoints(radiotap, dot11, packet) w.discoverClients(radiotap, dot11, packet) + w.updateInfo(dot11, packet) w.updateStats(dot11, packet) } } diff --git a/modules/wifi_show.go b/modules/wifi_show.go index db1180b0..9132abac 100644 --- a/modules/wifi_show.go +++ b/modules/wifi_show.go @@ -78,7 +78,6 @@ func (w *WiFiModule) getRow(station *network.Station) ([]string, bool) { return []string{ fmt.Sprintf("%d dBm", station.RSSI), bssid, - /* station.Vendor, */ strconv.Itoa(station.Channel()), sent, recvd, @@ -95,12 +94,29 @@ func (w *WiFiModule) getRow(station *network.Station) ([]string, bool) { } } + wps := "" + if station.HasWPS() { + if ver, found := station.WPS["Version"]; found { + wps = ver + } else { + wps = "✔" + } + + if state, found := station.WPS["State"]; found { + if state == "Not Configured" { + wps += " (not configured)" + } + } + + wps = tui.Dim(tui.Yellow(wps)) + } + return []string{ fmt.Sprintf("%d dBm", station.RSSI), bssid, ssid, - /* station.Vendor, */ encryption, + wps, strconv.Itoa(station.Channel()), clients, sent, @@ -199,13 +215,17 @@ func (w *WiFiModule) colDecorate(colNames []string, name string, dir string) { } } -func (w *WiFiModule) colNames(nrows int) []string { +func (w *WiFiModule) colNames(nrows int, withWPS bool) []string { columns := []string(nil) if !w.isApSelected() { - columns = []string{"RSSI", "BSSID", "SSID", "Encryption", "Channel", "Clients", "Sent", "Recvd", "Last Seen"} + if withWPS { + columns = []string{"RSSI", "BSSID", "SSID", "Encryption", "WPS", "Ch", "Clients", "Sent", "Recvd", "Last Seen"} + } else { + columns = []string{"RSSI", "BSSID", "SSID", "Encryption", "Ch", "Clients", "Sent", "Recvd", "Last Seen"} + } } else if nrows > 0 { - columns = []string{"RSSI", "MAC", "Channel", "Sent", "Received", "Last Seen"} + columns = []string{"RSSI", "MAC", "Ch", "Sent", "Received", "Last Seen"} fmt.Printf("\n%s clients:\n", w.ap.HwAddress) } else { fmt.Printf("\nNo authenticated clients detected for %s.\n", w.ap.HwAddress) @@ -220,7 +240,7 @@ func (w *WiFiModule) colNames(nrows int) []string { case "bssid": w.colDecorate(columns, "BSSID", w.selector.SortSymbol) case "channel": - w.colDecorate(columns, "Channel", w.selector.SortSymbol) + w.colDecorate(columns, "Ch", w.selector.SortSymbol) case "clients": w.colDecorate(columns, "Clients", w.selector.SortSymbol) case "encryption": @@ -243,15 +263,19 @@ func (w *WiFiModule) Show() (err error) { return } + hasWPS := false rows := make([][]string, 0) for _, s := range stations { if row, include := w.getRow(s); include { + if len(s.WPS) > 0 { + hasWPS = true + } rows = append(rows, row) } } nrows := len(rows) if nrows > 0 { - tui.Table(os.Stdout, w.colNames(nrows), rows) + tui.Table(os.Stdout, w.colNames(nrows, hasWPS), rows) } w.Session.Queue.Stats.RLock() @@ -270,3 +294,42 @@ func (w *WiFiModule) Show() (err error) { return nil } + +func (w *WiFiModule) ShowWPS(bssid string) (err error) { + toShow := []*network.AccessPoint{} + + if bssid == network.BroadcastMac { + for _, station := range w.Session.WiFi.List() { + if station.HasWPS() { + toShow = append(toShow, station) + } + } + } else { + if station, found := w.Session.WiFi.Get(bssid); found { + if station.HasWPS() { + toShow = append(toShow, station) + } + } + } + + if len(toShow) == 0 { + return fmt.Errorf("no WPS enabled access points matched the criteria") + } + + for _, station := range toShow { + ssid := station.ESSID() + if ssid == "" { + ssid = tui.Dim(ssid) + } + + fmt.Println() + fmt.Printf("* %s (%s ch:%d):\n", tui.Bold(ssid), tui.Dim(station.BSSID()), station.Channel()) + for name, value := range station.WPS { + fmt.Printf(" %s: %s\n", name, tui.Yellow(value)) + } + } + + fmt.Println() + + return nil +} diff --git a/network/wifi_station.go b/network/wifi_station.go index df734538..2c40e588 100644 --- a/network/wifi_station.go +++ b/network/wifi_station.go @@ -6,13 +6,14 @@ import ( type Station struct { *Endpoint - Frequency int `json:"frequency"` - RSSI int8 `json:"rssi"` - Sent uint64 `json:"sent"` - Received uint64 `json:"received"` - Encryption string `json:"encryption"` - Cipher string `json:"cipher"` - Authentication string `json:"authentication"` + Frequency int `json:"frequency"` + RSSI int8 `json:"rssi"` + Sent uint64 `json:"sent"` + Received uint64 `json:"received"` + Encryption string `json:"encryption"` + Cipher string `json:"cipher"` + Authentication string `json:"authentication"` + WPS map[string]string `json:"wps"` } func cleanESSID(essid string) string { @@ -47,3 +48,7 @@ func (s *Station) ESSID() string { func (s *Station) Channel() int { return Dot11Freq2Chan(s.Frequency) } + +func (s *Station) HasWPS() bool { + return len(s.WPS) > 0 +} diff --git a/packets/dot11_wps.go b/packets/dot11_wps.go new file mode 100644 index 00000000..16fa7026 --- /dev/null +++ b/packets/dot11_wps.go @@ -0,0 +1,118 @@ +package packets + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "net" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +type wpsAttrType int + +const ( + wpsHex wpsAttrType = 0 + wpsStr wpsAttrType = 1 +) + +type wpsAttr struct { + Name string + Type wpsAttrType + Desc map[string]string +} + +var ( + wpsSignatureBytes = []byte{0x00, 0x50, 0xf2, 0x04} + wpsAttributes = map[uint16]wpsAttr{ + 0x104A: wpsAttr{Name: "Version", Desc: map[string]string{ + "10": "1.0", + "11": "1.1", + }}, + 0x1044: wpsAttr{Name: "State", Desc: map[string]string{ + "01": "Not Configured", + "02": "Configured", + }}, + 0x1057: wpsAttr{Name: "AP Setup Locked"}, + 0x1041: wpsAttr{Name: "Selected Registrar"}, + 0x1012: wpsAttr{Name: "Device Password ID"}, + 0x1053: wpsAttr{Name: "Selected Registrar Config Methods", Desc: map[string]string{ + "0001": "USB", + "0002": "Ethernet", + "0004": "Label", + "0008": "Display", + "0010": "External NFC", + "0020": "Internal NFC", + "0040": "NFC Interface", + "0080": "Push Button", + "0100": "Keypad", + }}, + 0x103B: wpsAttr{Name: "Response Type"}, + 0x1047: wpsAttr{Name: "UUID-E"}, + 0x1021: wpsAttr{Name: "Manufacturer", Type: wpsStr}, + 0x1023: wpsAttr{Name: "Model Name", Type: wpsStr}, + 0x1024: wpsAttr{Name: "Model Number", Type: wpsStr}, + 0x1042: wpsAttr{Name: "Serial Number", Type: wpsStr}, + 0x1054: wpsAttr{Name: "Primary Device Type"}, + 0x1011: wpsAttr{Name: "Device Name", Type: wpsStr}, + 0x1008: wpsAttr{Name: "Config Methods"}, + 0x103C: wpsAttr{Name: "RF Bands"}, + 0x1045: wpsAttr{Name: "SSID", Type: wpsStr}, + 0x102D: wpsAttr{Name: "OS Version", Type: wpsStr}, + 0x1049: wpsAttr{Name: "Vendor Extension"}, + } +) + +func dot11ParseWPSData(data []byte) (ok bool, info map[string]string) { + info = map[string]string{} + size := len(data) + + for offset := 0; offset < size; { + tagId := binary.BigEndian.Uint16(data[offset:]) + offset += 2 + tagLen := binary.BigEndian.Uint16(data[offset:]) + offset += 2 + tagData := data[offset : offset+int(tagLen)] + + if attr, found := wpsAttributes[tagId]; found { + val := "" + if attr.Type == wpsStr { + val = string(tagData) + } else { + val = hex.EncodeToString(tagData) + } + + if attr.Desc != nil { + if desc, found := attr.Desc[val]; found { + val = desc + } + } + + info[attr.Name] = val + } else { + info[fmt.Sprintf("0x%X", tagId)] = hex.EncodeToString(tagData) + } + + offset += int(tagLen) + } + + return true, info +} + +func Dot11ParseWPS(packet gopacket.Packet, dot11 *layers.Dot11) (ok bool, bssid net.HardwareAddr, info map[string]string) { + ok = false + for _, layer := range packet.Layers() { + if layer.LayerType() == layers.LayerTypeDot11InformationElement { + if dot11info, infoOk := layer.(*layers.Dot11InformationElement); infoOk && dot11info.ID == layers.Dot11InformationElementIDVendor { + if bytes.Equal(dot11info.OUI, wpsSignatureBytes) { + bssid = dot11.Address3 + ok, info = dot11ParseWPSData(dot11info.Info) + return + } + } + } + } + return +}