From 8f84b9d72c7c8ac99e567a55094786efe2748d90 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Sun, 11 Mar 2018 15:36:51 +0100 Subject: [PATCH] fix: make 802.11 packet parsing and validation more robust (fixes #167) --- modules/wifi_recon.go | 4 +- packets/dot11.go | 150 +---------------------------- packets/dot11_types.go | 207 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+), 149 deletions(-) create mode 100644 packets/dot11_types.go diff --git a/modules/wifi_recon.go b/modules/wifi_recon.go index 9b7d62dd..d9c481b6 100644 --- a/modules/wifi_recon.go +++ b/modules/wifi_recon.go @@ -163,10 +163,12 @@ func (w *WiFiRecon) getRow(station *network.Station) []string { encryption := station.Encryption if len(station.Cipher) > 0 { - encryption = fmt.Sprintf("%s [%s,%s]", station.Encryption, station.Cipher, station.Authentication) + encryption = fmt.Sprintf("%s (%s, %s)", station.Encryption, station.Cipher, station.Authentication) } if encryption == "OPEN" || encryption == "" { encryption = core.Green("OPEN") + ssid = core.Green(ssid) + bssid = core.Green(bssid) } sent := "" if station.Sent > 0 { diff --git a/packets/dot11.go b/packets/dot11.go index 74689466..919b9108 100644 --- a/packets/dot11.go +++ b/packets/dot11.go @@ -2,159 +2,13 @@ package packets import ( "bytes" - "encoding/binary" - "fmt" "net" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) -type Dot11CipherType uint8 - -const ( - Dot11CipherWep Dot11CipherType = 1 - Dot11CipherTkip Dot11CipherType = 2 - Dot11CipherWrap Dot11CipherType = 3 - Dot11CipherCcmp Dot11CipherType = 4 - Dot11CipherWep104 Dot11CipherType = 5 -) - -func (a Dot11CipherType) String() string { - switch a { - case Dot11CipherWep: - return "WEP" - case Dot11CipherTkip: - return "TKIP" - case Dot11CipherWrap: - return "WRAP" - case Dot11CipherCcmp: - return "CCMP" - case Dot11CipherWep104: - return "WEP104" - default: - return "UNK" - } -} - -type Dot11AuthType uint8 - -const ( - Dot11AuthMgt Dot11AuthType = 1 - Dot11AuthPsk Dot11AuthType = 2 -) - -func (a Dot11AuthType) String() string { - switch a { - case Dot11AuthMgt: - return "MGT" - case Dot11AuthPsk: - return "PSK" - default: - return "UNK" - } -} - -type CipherSuite struct { - OUI []byte // 3 bytes - Type Dot11CipherType -} - -type AuthSuite struct { - OUI []byte // 3 bytes - Type Dot11AuthType -} - -type CipherSuiteSelector struct { - Count uint16 - Suites []CipherSuite -} -type AuthSuiteSelector struct { - Count uint16 - Suites []AuthSuite -} - -type VendorInfo struct { - WPAVersion uint16 - Multicast CipherSuite - Unicast CipherSuiteSelector - AuthKey AuthSuiteSelector -} - -type RSNInfo struct { - Version uint16 - Group CipherSuite - Pairwise CipherSuiteSelector - AuthKey AuthSuiteSelector -} - -func Dot11InformationElementVendorInfoDecode(vendorInfo []byte) (VendorInfo, error) { - var v VendorInfo - var i uint16 - if len(vendorInfo) < 15 { - return v, fmt.Errorf("VendorInfo packet length %v too short, %v required", len(vendorInfo), 15) - } - - v.WPAVersion = binary.LittleEndian.Uint16(vendorInfo[0:2]) - v.Multicast.OUI = vendorInfo[2:5] - v.Multicast.Type = Dot11CipherType(vendorInfo[5]) - - v.Unicast.Count = binary.LittleEndian.Uint16(vendorInfo[6:8]) - - p := 8 - for i = 0; i < v.Unicast.Count && p < len(vendorInfo); i++ { - var suite CipherSuite - suite.OUI = vendorInfo[p : p+3] - suite.Type = Dot11CipherType(vendorInfo[p+3]) - v.Unicast.Suites = append(v.Unicast.Suites, suite) - p = p + 4 - } - - v.AuthKey.Count = binary.LittleEndian.Uint16(vendorInfo[p : p+2]) - p = p + 2 - for i = 0; i < v.AuthKey.Count && p < len(vendorInfo); i++ { - var suite AuthSuite - suite.OUI = vendorInfo[p : p+3] - suite.Type = Dot11AuthType(vendorInfo[p+3]) - v.AuthKey.Suites = append(v.AuthKey.Suites, suite) - p = p + 4 - } - - return v, nil -} - -func Dot11InformationElementRSNInfoDecode(info []byte) (RSNInfo, error) { - var rsn RSNInfo - if len(info) < 20 { - return rsn, fmt.Errorf("RSNInfo packet length %v too short, %v required", len(info), 20) - } - - rsn.Version = binary.LittleEndian.Uint16(info[0:2]) - rsn.Group.OUI = info[2:5] - rsn.Group.Type = Dot11CipherType(info[5]) - rsn.Pairwise.Count = binary.LittleEndian.Uint16(info[6:8]) - - p := 8 - for i := uint16(0); i < rsn.Pairwise.Count && p < len(info); i++ { - var suite CipherSuite - suite.OUI = info[p : p+3] - suite.Type = Dot11CipherType(info[p+3]) - rsn.Pairwise.Suites = append(rsn.Pairwise.Suites, suite) - p = p + 4 - } - - rsn.AuthKey.Count = binary.LittleEndian.Uint16(info[p : p+2]) - p = p + 2 - for i := uint16(0); i < rsn.AuthKey.Count && p < len(info); i++ { - var suite AuthSuite - suite.OUI = info[p : p+3] - suite.Type = Dot11AuthType(info[p+3]) - rsn.AuthKey.Suites = append(rsn.AuthKey.Suites, suite) - p = p + 4 - } - - return rsn, nil -} +var wpaSignatureBytes = []byte{0, 0x50, 0xf2, 1} func NewDot11Deauth(a1 net.HardwareAddr, a2 net.HardwareAddr, a3 net.HardwareAddr, seq uint16) (error, []byte) { return Serialize( @@ -240,7 +94,7 @@ func Dot11ParseEncryption(packet gopacket.Packet, dot11 *layers.Dot11) (bool, st auth = rsn.AuthKey.Suites[i].Type.String() } } - } else if enc == "" && info.ID == layers.Dot11InformationElementIDVendor && info.Length >= 8 && bytes.Compare(info.OUI, []byte{0, 0x50, 0xf2, 1}) == 0 && bytes.HasPrefix(info.Info, []byte{1, 0}) { + } else if enc == "" && info.ID == layers.Dot11InformationElementIDVendor && info.Length >= 8 && bytes.Compare(info.OUI, wpaSignatureBytes) == 0 && bytes.HasPrefix(info.Info, []byte{1, 0}) { enc = "WPA" vendor, err := Dot11InformationElementVendorInfoDecode(info.Info) if err == nil { diff --git a/packets/dot11_types.go b/packets/dot11_types.go new file mode 100644 index 00000000..b0f7c3c7 --- /dev/null +++ b/packets/dot11_types.go @@ -0,0 +1,207 @@ +package packets + +import ( + "encoding/binary" + "fmt" +) + +type Dot11CipherType uint8 + +const ( + Dot11CipherWep Dot11CipherType = 1 + Dot11CipherTkip Dot11CipherType = 2 + Dot11CipherWrap Dot11CipherType = 3 + Dot11CipherCcmp Dot11CipherType = 4 + Dot11CipherWep104 Dot11CipherType = 5 +) + +func (a Dot11CipherType) String() string { + switch a { + case Dot11CipherWep: + return "WEP" + case Dot11CipherTkip: + return "TKIP" + case Dot11CipherWrap: + return "WRAP" + case Dot11CipherCcmp: + return "CCMP" + case Dot11CipherWep104: + return "WEP104" + default: + return "UNK" + } +} + +type Dot11AuthType uint8 + +const ( + Dot11AuthMgt Dot11AuthType = 1 + Dot11AuthPsk Dot11AuthType = 2 +) + +func (a Dot11AuthType) String() string { + switch a { + case Dot11AuthMgt: + return "MGT" + case Dot11AuthPsk: + return "PSK" + default: + return "UNK" + } +} + +type CipherSuite struct { + OUI []byte // 3 bytes + Type Dot11CipherType +} + +type AuthSuite struct { + OUI []byte // 3 bytes + Type Dot11AuthType +} + +type CipherSuiteSelector struct { + Count uint16 + Suites []CipherSuite +} + +type AuthSuiteSelector struct { + Count uint16 + Suites []AuthSuite +} + +type RSNInfo struct { + Version uint16 + Group CipherSuite + Pairwise CipherSuiteSelector + AuthKey AuthSuiteSelector +} + +type VendorInfo struct { + WPAVersion uint16 + Multicast CipherSuite + Unicast CipherSuiteSelector + AuthKey AuthSuiteSelector +} + +func canParse(what string, buf []byte, need int) error { + available := len(buf) + if need > available { + return fmt.Errorf("Malformed 802.11 packet, could not parse %s: needed %d bytes but only %d are available.", what, need, available) + } + return nil +} + +func parsePairwiseSuite(buf []byte) (suite CipherSuite, err error) { + if err = canParse("RSN.Pairwise.Suite", buf, 4); err == nil { + suite.OUI = buf[0:3] + suite.Type = Dot11CipherType(buf[3]) + } + return +} + +func parseAuthkeySuite(buf []byte) (suite AuthSuite, err error) { + if err = canParse("RSN.AuthKey.Suite", buf, 4); err == nil { + suite.OUI = buf[0:3] + suite.Type = Dot11AuthType(buf[3]) + } + return +} + +func Dot11InformationElementVendorInfoDecode(buf []byte) (v VendorInfo, err error) { + if err = canParse("Vendor", buf, 8); err == nil { + v.WPAVersion = binary.LittleEndian.Uint16(buf[0:2]) + v.Multicast.OUI = buf[2:5] + v.Multicast.Type = Dot11CipherType(buf[5]) + v.Unicast.Count = binary.LittleEndian.Uint16(buf[6:8]) + buf = buf[8:] + } else { + v.Unicast.Count = 0 + return + } + + // check what we're left with + if err = canParse("Vendor.Unicast.Suites", buf, int(v.Unicast.Count)*4); err == nil { + for i := uint16(0); i < v.Unicast.Count; i++ { + if suite, err := parsePairwiseSuite(buf); err == nil { + v.Unicast.Suites = append(v.Unicast.Suites, suite) + buf = buf[4:] + } else { + return v, err + } + } + } else { + return + } + + if err = canParse("Vendor.AuthKey.Count", buf, 2); err == nil { + v.AuthKey.Count = binary.LittleEndian.Uint16(buf[0:2]) + buf = buf[2:] + } else { + v.AuthKey.Count = 0 + return + } + + // just like before, check if we have enough data + if err = canParse("Vendor.AuthKey.Suites", buf, int(v.AuthKey.Count)*4); err == nil { + for i := uint16(0); i < v.AuthKey.Count; i++ { + if suite, err := parseAuthkeySuite(buf); err == nil { + v.AuthKey.Suites = append(v.AuthKey.Suites, suite) + buf = buf[4:] + } else { + return v, err + } + } + } + + return +} + +func Dot11InformationElementRSNInfoDecode(buf []byte) (rsn RSNInfo, err error) { + if err = canParse("RSN", buf, 8); err == nil { + rsn.Version = binary.LittleEndian.Uint16(buf[0:2]) + rsn.Group.OUI = buf[2:5] + rsn.Group.Type = Dot11CipherType(buf[5]) + rsn.Pairwise.Count = binary.LittleEndian.Uint16(buf[6:8]) + buf = buf[8:] + } else { + rsn.Pairwise.Count = 0 + return + } + + // check what we're left with + if err = canParse("RSN.Pairwise.Suites", buf, int(rsn.Pairwise.Count)*4); err == nil { + for i := uint16(0); i < rsn.Pairwise.Count; i++ { + if suite, err := parsePairwiseSuite(buf); err == nil { + rsn.Pairwise.Suites = append(rsn.Pairwise.Suites, suite) + buf = buf[4:] + } else { + return rsn, err + } + } + } else { + return + } + + if err = canParse("RSN.AuthKey.Count", buf, 2); err == nil { + rsn.AuthKey.Count = binary.LittleEndian.Uint16(buf[0:2]) + buf = buf[2:] + } else { + rsn.AuthKey.Count = 0 + return + } + + // just like before, check if we have enough data + if err = canParse("RSN.AuthKey.Suites", buf, int(rsn.AuthKey.Count)*4); err == nil { + for i := uint16(0); i < rsn.AuthKey.Count; i++ { + if suite, err := parseAuthkeySuite(buf); err == nil { + rsn.AuthKey.Suites = append(rsn.AuthKey.Suites, suite) + buf = buf[4:] + } else { + return rsn, err + } + } + } + + return +}