diff --git a/modules/wifi/wifi.go b/modules/wifi/wifi.go index 2a48b4a1..b27e8f59 100644 --- a/modules/wifi/wifi.go +++ b/modules/wifi/wifi.go @@ -39,6 +39,7 @@ type WiFiModule struct { ap *network.AccessPoint stickChan int shakesFile string + shakesAggregate bool skipBroken bool pktSourceChan chan gopacket.Packet pktSourceChanClosed bool @@ -59,26 +60,27 @@ type WiFiModule struct { func NewWiFiModule(s *session.Session) *WiFiModule { mod := &WiFiModule{ - SessionModule: session.NewSessionModule("wifi", s), - iface: s.Interface, - minRSSI: -200, - channel: 0, - stickChan: 0, - hopPeriod: 250 * time.Millisecond, - hopChanges: make(chan bool), - ap: nil, - skipBroken: true, - apRunning: false, - deauthSkip: []net.HardwareAddr{}, - deauthSilent: false, - deauthOpen: false, - assocSkip: []net.HardwareAddr{}, - assocSilent: false, - assocOpen: false, - showManuf: false, - writes: &sync.WaitGroup{}, - reads: &sync.WaitGroup{}, - chanLock: &sync.Mutex{}, + SessionModule: session.NewSessionModule("wifi", s), + iface: s.Interface, + minRSSI: -200, + channel: 0, + stickChan: 0, + hopPeriod: 250 * time.Millisecond, + hopChanges: make(chan bool), + ap: nil, + skipBroken: true, + apRunning: false, + deauthSkip: []net.HardwareAddr{}, + deauthSilent: false, + deauthOpen: false, + assocSkip: []net.HardwareAddr{}, + assocSilent: false, + assocOpen: false, + showManuf: false, + shakesAggregate: true, + writes: &sync.WaitGroup{}, + reads: &sync.WaitGroup{}, + chanLock: &sync.Mutex{}, } mod.InitState("channels") @@ -220,6 +222,10 @@ func NewWiFiModule(s *session.Session) *WiFiModule { "", "File path of the pcap file to save handshakes to.")) + mod.AddParam(session.NewBoolParameter("wifi.handshakes.aggregate", + "true", + "If true, all handshakes will be saved inside a single file, otherwise a folder with per-network pcap files will be created.")) + mod.AddParam(session.NewStringParameter("wifi.ap.ssid", "FreeWiFi", "", @@ -364,6 +370,8 @@ func (mod *WiFiModule) Configure() error { if mod.shakesFile, err = fs.Expand(mod.shakesFile); err != nil { return err } + } else if err, mod.shakesAggregate = mod.BoolParam("wifi.handshakes.aggregate"); err != nil { + return err } if err, ifName = mod.StringParam("wifi.interface"); err != nil { diff --git a/modules/wifi/wifi_recon_handshakes.go b/modules/wifi/wifi_recon_handshakes.go index af2c4f5d..f64ebf1e 100644 --- a/modules/wifi/wifi_recon_handshakes.go +++ b/modules/wifi/wifi_recon_handshakes.go @@ -2,6 +2,8 @@ package wifi import ( "bytes" + "fmt" + "path" "github.com/bettercap/bettercap/packets" @@ -85,11 +87,15 @@ func (mod *WiFiModule) discoverHandshakes(radiotap *layers.RadioTap, dot11 *laye // if we have unsaved packets as part of the handshake, save them. numUnsaved := station.Handshake.NumUnsaved() + shakesFileName := mod.shakesFile doSave := numUnsaved > 0 - if doSave && mod.shakesFile != "" { - mod.Debug("saving handshake frames to %s", mod.shakesFile) - if err := mod.Session.WiFi.SaveHandshakesTo(mod.shakesFile, mod.handle.LinkType()); err != nil { - mod.Error("error while saving handshake frames to %s: %s", mod.shakesFile, err) + if doSave && shakesFileName != "" { + if mod.shakesAggregate == false { + shakesFileName = path.Join(shakesFileName, fmt.Sprintf("%s.pcap", station.PathFriendlyName())) + } + mod.Debug("saving handshake frames to %s", 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) } } @@ -97,7 +103,7 @@ func (mod *WiFiModule) discoverHandshakes(radiotap *layers.RadioTap, dot11 *laye // or it contains the PMKID, generate a new event. if doSave && (rawPMKID != nil || station.Handshake.Half() || station.Handshake.Complete()) { mod.Session.Events.Add("wifi.client.handshake", HandshakeEvent{ - File: mod.shakesFile, + File: shakesFileName, NewPackets: numUnsaved, AP: apMac.String(), Station: staMac.String(), diff --git a/network/wifi.go b/network/wifi.go index af38bdd1..0fc57d35 100644 --- a/network/wifi.go +++ b/network/wifi.go @@ -3,6 +3,7 @@ package network import ( "encoding/json" "os" + "path/filepath" "strconv" "sync" "time" @@ -214,8 +215,15 @@ func (w *WiFi) SaveHandshakesTo(fileName string, linkType layers.LinkType) error w.Lock() defer w.Unlock() - doHead := !fs.Exists(fileName) + // check if folder exists first + dirName := filepath.Dir(fileName) + if _, err := os.Stat(dirName); err != nil { + if err = os.MkdirAll(dirName, os.ModePerm); err != nil { + return err + } + } + doHead := !fs.Exists(fileName) fp, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) if err != nil { return err diff --git a/network/wifi_station.go b/network/wifi_station.go index a197e3c4..2c83dcf7 100644 --- a/network/wifi_station.go +++ b/network/wifi_station.go @@ -1,7 +1,14 @@ package network import ( + "fmt" + "regexp" "strconv" + "strings" +) + +var ( + pathNameCleaner = regexp.MustCompile("[^a-zA-Z0-9]+") ) type Station struct { @@ -56,3 +63,11 @@ func (s *Station) HasWPS() bool { func (s *Station) IsOpen() bool { return s.Encryption == "" || s.Encryption == "OPEN" } + +func (s *Station) PathFriendlyName() string { + name := strings.Replace(s.HwAddress, ":", "", -1) + if essid := pathNameCleaner.ReplaceAllString(s.Hostname, ""); essid != "" { + name = fmt.Sprintf("%s_%s", name, essid) + } + return name +}