mirror of
https://github.com/bettercap/bettercap
synced 2025-07-31 12:10:10 -07:00
fix: make 802.11 packet parsing and validation more robust (fixes #167)
This commit is contained in:
parent
98d7fb5b86
commit
8f84b9d72c
3 changed files with 212 additions and 149 deletions
|
@ -163,10 +163,12 @@ func (w *WiFiRecon) getRow(station *network.Station) []string {
|
||||||
|
|
||||||
encryption := station.Encryption
|
encryption := station.Encryption
|
||||||
if len(station.Cipher) > 0 {
|
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 == "" {
|
if encryption == "OPEN" || encryption == "" {
|
||||||
encryption = core.Green("OPEN")
|
encryption = core.Green("OPEN")
|
||||||
|
ssid = core.Green(ssid)
|
||||||
|
bssid = core.Green(bssid)
|
||||||
}
|
}
|
||||||
sent := ""
|
sent := ""
|
||||||
if station.Sent > 0 {
|
if station.Sent > 0 {
|
||||||
|
|
150
packets/dot11.go
150
packets/dot11.go
|
@ -2,159 +2,13 @@ package packets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
"github.com/google/gopacket/layers"
|
"github.com/google/gopacket/layers"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Dot11CipherType uint8
|
var wpaSignatureBytes = []byte{0, 0x50, 0xf2, 1}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDot11Deauth(a1 net.HardwareAddr, a2 net.HardwareAddr, a3 net.HardwareAddr, seq uint16) (error, []byte) {
|
func NewDot11Deauth(a1 net.HardwareAddr, a2 net.HardwareAddr, a3 net.HardwareAddr, seq uint16) (error, []byte) {
|
||||||
return Serialize(
|
return Serialize(
|
||||||
|
@ -240,7 +94,7 @@ func Dot11ParseEncryption(packet gopacket.Packet, dot11 *layers.Dot11) (bool, st
|
||||||
auth = rsn.AuthKey.Suites[i].Type.String()
|
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"
|
enc = "WPA"
|
||||||
vendor, err := Dot11InformationElementVendorInfoDecode(info.Info)
|
vendor, err := Dot11InformationElementVendorInfoDecode(info.Info)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
207
packets/dot11_types.go
Normal file
207
packets/dot11_types.go
Normal file
|
@ -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
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue