mirror of
https://github.com/bettercap/bettercap
synced 2025-08-20 13:33:21 -07:00
new: new WPS parsing capabilities and wifi.show.wps command
This commit is contained in:
parent
dcd1fbfe27
commit
50bf22af9c
4 changed files with 224 additions and 23 deletions
|
@ -141,6 +141,12 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
||||||
"true",
|
"true",
|
||||||
"If true, the fake access point will use WPA2, otherwise it'll result as an open AP."))
|
"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", "",
|
w.AddHandler(session.NewModuleHandler("wifi.show", "",
|
||||||
"Show current wireless stations list (default sorting by essid).",
|
"Show current wireless stations list (default sorting by essid).",
|
||||||
func(args []string) error {
|
func(args []string) error {
|
||||||
|
@ -266,6 +272,23 @@ func (w *WiFiModule) Configure() error {
|
||||||
return nil
|
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) {
|
func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) {
|
||||||
// collect stats from data frames
|
// collect stats from data frames
|
||||||
if dot11.Type.MainType() == layers.Dot11TypeData {
|
if dot11.Type.MainType() == layers.Dot11TypeData {
|
||||||
|
@ -281,15 +304,6 @@ func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) {
|
||||||
station.Sent += bytes
|
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 {
|
func (w *WiFiModule) Start() error {
|
||||||
|
@ -331,6 +345,7 @@ func (w *WiFiModule) Start() error {
|
||||||
w.discoverProbes(radiotap, dot11, packet)
|
w.discoverProbes(radiotap, dot11, packet)
|
||||||
w.discoverAccessPoints(radiotap, dot11, packet)
|
w.discoverAccessPoints(radiotap, dot11, packet)
|
||||||
w.discoverClients(radiotap, dot11, packet)
|
w.discoverClients(radiotap, dot11, packet)
|
||||||
|
w.updateInfo(dot11, packet)
|
||||||
w.updateStats(dot11, packet)
|
w.updateStats(dot11, packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,6 @@ func (w *WiFiModule) getRow(station *network.Station) ([]string, bool) {
|
||||||
return []string{
|
return []string{
|
||||||
fmt.Sprintf("%d dBm", station.RSSI),
|
fmt.Sprintf("%d dBm", station.RSSI),
|
||||||
bssid,
|
bssid,
|
||||||
/* station.Vendor, */
|
|
||||||
strconv.Itoa(station.Channel()),
|
strconv.Itoa(station.Channel()),
|
||||||
sent,
|
sent,
|
||||||
recvd,
|
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{
|
return []string{
|
||||||
fmt.Sprintf("%d dBm", station.RSSI),
|
fmt.Sprintf("%d dBm", station.RSSI),
|
||||||
bssid,
|
bssid,
|
||||||
ssid,
|
ssid,
|
||||||
/* station.Vendor, */
|
|
||||||
encryption,
|
encryption,
|
||||||
|
wps,
|
||||||
strconv.Itoa(station.Channel()),
|
strconv.Itoa(station.Channel()),
|
||||||
clients,
|
clients,
|
||||||
sent,
|
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)
|
columns := []string(nil)
|
||||||
|
|
||||||
if !w.isApSelected() {
|
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 {
|
} 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)
|
fmt.Printf("\n%s clients:\n", w.ap.HwAddress)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\nNo authenticated clients detected for %s.\n", w.ap.HwAddress)
|
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":
|
case "bssid":
|
||||||
w.colDecorate(columns, "BSSID", w.selector.SortSymbol)
|
w.colDecorate(columns, "BSSID", w.selector.SortSymbol)
|
||||||
case "channel":
|
case "channel":
|
||||||
w.colDecorate(columns, "Channel", w.selector.SortSymbol)
|
w.colDecorate(columns, "Ch", w.selector.SortSymbol)
|
||||||
case "clients":
|
case "clients":
|
||||||
w.colDecorate(columns, "Clients", w.selector.SortSymbol)
|
w.colDecorate(columns, "Clients", w.selector.SortSymbol)
|
||||||
case "encryption":
|
case "encryption":
|
||||||
|
@ -243,15 +263,19 @@ func (w *WiFiModule) Show() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasWPS := false
|
||||||
rows := make([][]string, 0)
|
rows := make([][]string, 0)
|
||||||
for _, s := range stations {
|
for _, s := range stations {
|
||||||
if row, include := w.getRow(s); include {
|
if row, include := w.getRow(s); include {
|
||||||
|
if len(s.WPS) > 0 {
|
||||||
|
hasWPS = true
|
||||||
|
}
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nrows := len(rows)
|
nrows := len(rows)
|
||||||
if nrows > 0 {
|
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()
|
w.Session.Queue.Stats.RLock()
|
||||||
|
@ -270,3 +294,42 @@ func (w *WiFiModule) Show() (err error) {
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -6,13 +6,14 @@ import (
|
||||||
|
|
||||||
type Station struct {
|
type Station struct {
|
||||||
*Endpoint
|
*Endpoint
|
||||||
Frequency int `json:"frequency"`
|
Frequency int `json:"frequency"`
|
||||||
RSSI int8 `json:"rssi"`
|
RSSI int8 `json:"rssi"`
|
||||||
Sent uint64 `json:"sent"`
|
Sent uint64 `json:"sent"`
|
||||||
Received uint64 `json:"received"`
|
Received uint64 `json:"received"`
|
||||||
Encryption string `json:"encryption"`
|
Encryption string `json:"encryption"`
|
||||||
Cipher string `json:"cipher"`
|
Cipher string `json:"cipher"`
|
||||||
Authentication string `json:"authentication"`
|
Authentication string `json:"authentication"`
|
||||||
|
WPS map[string]string `json:"wps"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanESSID(essid string) string {
|
func cleanESSID(essid string) string {
|
||||||
|
@ -47,3 +48,7 @@ func (s *Station) ESSID() string {
|
||||||
func (s *Station) Channel() int {
|
func (s *Station) Channel() int {
|
||||||
return Dot11Freq2Chan(s.Frequency)
|
return Dot11Freq2Chan(s.Frequency)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Station) HasWPS() bool {
|
||||||
|
return len(s.WPS) > 0
|
||||||
|
}
|
||||||
|
|
118
packets/dot11_wps.go
Normal file
118
packets/dot11_wps.go
Normal 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
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue