bettercap/modules/wifi/wifi_recon_handshakes.go
Sniffleupagus cb5f7679d8
Improve backwards compatibility with getHandshakeFileFor
The getHandshakeFile function was using "path.Dir(shakesFileName)", which drops the last element of the path.  This is not backwards compatible with prior versions that used the variable as the dir name. In particular this change causes pwnagotchi to store handshakes in /root instead of /root/handshakes.
This commit checks for an existing directory at shakesFileName and will use that as the path instead of taking the parent directory of the path.
2024-11-13 11:33:25 -08:00

202 lines
6.4 KiB
Go

package wifi
import (
"bytes"
"fmt"
"net"
"os"
"path"
"github.com/bettercap/bettercap/v2/network"
"github.com/bettercap/bettercap/v2/packets"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
func allZeros(s []byte) bool {
for _, v := range s {
if v != 0 {
return false
}
}
return true
}
func (mod *WiFiModule) getHandshakeFileFor(ap *network.AccessPoint) string {
shakesFileName := mod.shakesFile
if !mod.shakesAggregate {
parentDir := path.Dir(shakesFileName)
// check for existing directory at "shakesFileName" for backwards compatibility
fileInfo, err := os.Stat(shakesFileName)
if (err == nil) && (fileInfo.IsDir()) {
parentDir = shakesFileName
}
shakesFileName = path.Join(parentDir, fmt.Sprintf("%s.pcap", ap.PathFriendlyName()))
}
return shakesFileName
}
func (mod *WiFiModule) discoverHandshakes(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) {
isEAPOL := false
if ok, key, apMac, staMac := packets.Dot11ParseEAPOL(packet, dot11); ok {
isEAPOL = true
// first, locate the AP in our list by its BSSID
ap, found := mod.Session.WiFi.Get(apMac.String())
if !found {
mod.Warning("could not find AP with BSSID %s", apMac.String())
return
}
// locate the client station, if its BSSID is ours, it means we sent
// an association request via wifi.assoc because we're trying to capture
// the PMKID from the first EAPOL sent by the AP.
// (Reference about PMKID https://hashcat.net/forum/thread-7717.html)
// In this case, we need to add ourselves as a client station of the AP
// in order to have a consistent association of AP, client and handshakes.
staIsUs := bytes.Equal(staMac, mod.iface.HW)
station, found := ap.Get(staMac.String())
staAdded := false
if !found {
station, staAdded = ap.AddClientIfNew(staMac.String(), ap.Frequency, ap.RSSI)
}
rawPMKID := []byte(nil)
if !key.Install && key.KeyACK && !key.KeyMIC {
// [1] (ACK) AP is sending ANonce to the client
rawPMKID = station.Handshake.AddAndGetPMKID(packet)
PMKID := "without PMKID"
if rawPMKID != nil {
PMKID = "with PMKID"
}
mod.Debug("got frame 1/4 of the %s <-> %s handshake (%s) (anonce:%x)",
apMac,
staMac,
PMKID,
key.Nonce)
//add the ap's station's beacon packet to be saved as part of the handshake cap file
//https://github.com/ZerBea/hcxtools/issues/92
//https://github.com/bettercap/bettercap/issues/592
if ap.Station.Handshake.Beacon != nil {
mod.Debug("adding beacon frame to handshake for %s", apMac)
station.Handshake.AddFrame(1, ap.Station.Handshake.Beacon)
}
} else if !key.Install && !key.KeyACK && key.KeyMIC && !allZeros(key.Nonce) {
// [2] (MIC) client is sending SNonce+MIC to the API
station.Handshake.AddFrame(1, packet)
mod.Debug("got frame 2/4 of the %s <-> %s handshake (snonce:%x mic:%x)",
apMac,
staMac,
key.Nonce,
key.MIC)
} else if key.Install && key.KeyACK && key.KeyMIC {
// [3]: (INSTALL+ACK+MIC) AP informs the client that the PTK is installed
station.Handshake.AddFrame(2, packet)
mod.Debug("got frame 3/4 of the %s <-> %s handshake (mic:%x)",
apMac,
staMac,
key.MIC)
}
// if we have unsaved packets as part of the handshake, save them.
numUnsaved := station.Handshake.NumUnsaved()
shakesFileName := mod.getHandshakeFileFor(ap)
doSave := numUnsaved > 0
if doSave && shakesFileName != "" {
mod.Debug("(aggregate %v) saving handshake frames to %s", mod.shakesAggregate, shakesFileName)
if err := mod.Session.WiFi.SaveHandshakesTo(shakesFileName, mod.handle.LinkType()); err != nil {
mod.Error("error while saving handshake frames to %s: %s", shakesFileName, err)
}
}
validPMKID := rawPMKID != nil
validHalfHandshake := !staIsUs && station.Handshake.Half()
validFullHandshake := station.Handshake.Complete()
// if we have unsaved packets AND
// if we captured a PMKID OR
// if we captured am half handshake which is not ours OR
// if we captured a full handshake
if doSave && (validPMKID || validHalfHandshake || validFullHandshake) {
mod.Session.Events.Add("wifi.client.handshake", HandshakeEvent{
File: shakesFileName,
NewPackets: numUnsaved,
AP: apMac.String(),
Station: staMac.String(),
PMKID: rawPMKID,
Half: station.Handshake.Half(),
Full: station.Handshake.Complete(),
})
// make sure the info that we have key material for this AP
// is persisted even after stations are pruned due to inactivity
ap.WithKeyMaterial(true)
}
// if we added ourselves as a client station but we didn't get any
// PMKID, just remove it from the list of clients of this AP.
if staAdded || (staIsUs && rawPMKID == nil) {
ap.RemoveClient(staMac.String())
}
}
// quick and dirty heuristic, see thread here https://github.com/bettercap/bettercap/issues/810#issuecomment-805145392
if isEAPOL || (dot11.Type.MainType() != layers.Dot11TypeData && dot11.Type.MainType() != layers.Dot11TypeCtrl) {
target := (*network.Station)(nil)
targetAP := (*network.AccessPoint)(nil)
// collect target bssids
bssids := make([]net.HardwareAddr, 0)
for _, addr := range []net.HardwareAddr{dot11.Address1, dot11.Address2, dot11.Address3, dot11.Address4} {
if bytes.Equal(addr, network.BroadcastHw) == false {
bssids = append(bssids, addr)
}
}
// for each AP
mod.Session.WiFi.EachAccessPoint(func(mac string, ap *network.AccessPoint) {
// only check APs we captured handshakes of
if target == nil && ap.HasKeyMaterial() {
// search client station
ap.EachClient(func(mac string, station *network.Station) {
// any valid key material for this station?
if station.Handshake.Any() {
// check if target
for _, a := range bssids {
if bytes.Equal(a, station.HW) {
target = station
targetAP = ap
break
}
}
}
})
}
})
if target != nil {
mod.Debug("saving extra %s frame (%d bytes) for %s",
dot11.Type.String(),
len(packet.Data()),
target.String())
target.Handshake.AddExtra(packet)
shakesFileName := mod.getHandshakeFileFor(targetAP)
if shakesFileName != "" {
mod.Debug("(aggregate %v) saving handshake frames to %s", mod.shakesAggregate, shakesFileName)
if err := mod.Session.WiFi.SaveHandshakesTo(shakesFileName, mod.handle.LinkType()); err != nil {
mod.Error("error while saving handshake frames to %s: %s", shakesFileName, err)
}
}
}
}
}