new: new WPS parsing capabilities and wifi.show.wps command

This commit is contained in:
evilsocket 2019-01-27 14:52:59 +01:00
commit 50bf22af9c
No known key found for this signature in database
GPG key ID: 1564D7F30393A456
4 changed files with 224 additions and 23 deletions

View file

@ -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)
}
}

View file

@ -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 == "<hidden>" {
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
}

View file

@ -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
}

118
packets/dot11_wps.go Normal file
View file

@ -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
}