refact: big refactoring and improvements of wifi.* modules

This commit is contained in:
evilsocket 2018-02-20 18:14:38 +01:00
commit da5d1d46d4
6 changed files with 266 additions and 123 deletions

View file

@ -19,31 +19,30 @@ func (s EventsStream) viewLogEvent(e session.Event) {
e.Data.(session.LogMessage).Message)
}
func (s EventsStream) viewStationEvent(e session.Event) {
st := e.Data.(*network.Station)
func (s EventsStream) viewApEvent(e session.Event) {
ap := e.Data.(*network.AccessPoint)
vend := ""
if st.Vendor != "" {
vend = fmt.Sprintf(" (%s)", st.Vendor)
if ap.Vendor != "" {
vend = fmt.Sprintf(" (%s)", ap.Vendor)
}
if e.Tag == "wifi.station.new" {
fmt.Printf("[%s] WiFi station %s detected as %s%s.\n",
if e.Tag == "wifi.ap.new" {
fmt.Printf("[%s] WiFi access point %s detected as %s%s.\n",
e.Time.Format(eventTimeFormat),
core.Bold(st.ESSID()),
core.Green(st.BSSID()),
core.Bold(ap.ESSID()),
core.Green(ap.BSSID()),
vend)
} else if e.Tag == "wifi.station.lost" {
fmt.Printf("[%s] WiFi station %s (%s) lost.\n",
} else if e.Tag == "wifi.ap.lost" {
fmt.Printf("[%s] WiFi access point %s (%s) lost.\n",
e.Time.Format(eventTimeFormat),
core.Red(st.ESSID()),
st.BSSID())
core.Red(ap.ESSID()),
ap.BSSID())
} else {
fmt.Printf("[%s] [%s] %s\n",
e.Time.Format(eventTimeFormat),
core.Green(e.Tag),
st)
ap)
}
}
func (s EventsStream) viewEndpointEvent(e session.Event) {
@ -101,8 +100,8 @@ func (s *EventsStream) View(e session.Event, refresh bool) {
s.viewLogEvent(e)
} else if strings.HasPrefix(e.Tag, "endpoint.") {
s.viewEndpointEvent(e)
} else if strings.HasPrefix(e.Tag, "wifi.station.") {
s.viewStationEvent(e)
} else if strings.HasPrefix(e.Tag, "wifi.ap.") {
s.viewApEvent(e)
} else if strings.HasPrefix(e.Tag, "mod.") {
s.viewModuleEvent(e)
} else if strings.HasPrefix(e.Tag, "net.sniff.") {

View file

@ -31,14 +31,16 @@ type WiFiRecon struct {
handle *pcap.Handle
channel int
frequencies []int
apBSSID net.HardwareAddr
ap *network.AccessPoint
stickChan int
}
func NewWiFiRecon(s *session.Session) *WiFiRecon {
w := &WiFiRecon{
SessionModule: session.NewSessionModule("wifi.recon", s),
channel: 0,
apBSSID: nil,
stickChan: 0,
ap: nil,
}
w.AddHandler(session.NewModuleHandler("wifi.recon on", "",
@ -56,41 +58,33 @@ func NewWiFiRecon(s *session.Session) *WiFiRecon {
w.AddHandler(session.NewModuleHandler("wifi.recon MAC", "wifi.recon ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))",
"Set 802.11 base station address to filter for.",
func(args []string) error {
var err error
w.Session.WiFi.Clear()
w.apBSSID, err = net.ParseMAC(args[0])
bssid, err := net.ParseMAC(args[0])
if err != nil {
return err
} else if ap, found := w.Session.WiFi.Get(bssid.String()); found == true {
w.ap = ap
w.stickChan = mhz2chan(ap.Frequency)
return nil
}
return fmt.Errorf("Could not find station with BSSID %s", args[0])
}))
w.AddHandler(session.NewModuleHandler("wifi.recon clear", "",
"Remove the 802.11 base station filter.",
func(args []string) error {
w.Session.WiFi.Clear()
w.apBSSID = nil
w.ap = nil
w.stickChan = 0
return nil
}))
w.AddHandler(session.NewModuleHandler("wifi.deauth AP-BSSID TARGET-BSSID", `wifi\.deauth ([a-fA-F0-9\s:]+)`,
"Start a 802.11 deauth attack, while the AP-BSSID is mandatory, if no TARGET-BSSID is specified the deauth will be executed against every connected client.",
w.AddHandler(session.NewModuleHandler("wifi.deauth AP-BSSID TARGET-BSSID", `wifi\.deauth ((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))`,
"Start a 802.11 deauth attack, if an access point BSSID is provided, every client will be deauthenticated, otherwise only the selected client.",
func(args []string) error {
err := (error)(nil)
apMac := (net.HardwareAddr)(nil)
clMac := (net.HardwareAddr)(nil)
parts := strings.SplitN(args[0], " ", 2)
if len(parts) == 2 {
if apMac, err = net.ParseMAC(parts[0]); err != nil {
return err
} else if clMac, err = net.ParseMAC(parts[1]); err != nil {
bssid, err := net.ParseMAC(args[0])
if err != nil {
return err
}
} else {
if apMac, err = net.ParseMAC(parts[0]); err != nil {
return err
}
}
return w.startDeauth(apMac, clMac)
return w.startDeauth(bssid)
}))
w.AddHandler(session.NewModuleHandler("wifi.show", "",
@ -115,7 +109,7 @@ func (w WiFiRecon) Description() string {
}
func (w WiFiRecon) Author() string {
return "Gianluca Braga <matrix86@protonmail.com>"
return "Gianluca Braga <matrix86@protonmail.com> && Simone Margaritelli <evilsocket@protonmail.com>>"
}
func (w *WiFiRecon) getRow(station *network.Station) []string {
@ -164,6 +158,11 @@ func (w *WiFiRecon) getRow(station *network.Station) []string {
seen,
}
} else {
// this is ugly, but necessary in order to have this
// method handle both access point and clients
// transparently
ap, _ := w.Session.WiFi.Get(station.HwAddress)
return []string{
fmt.Sprintf("%d dBm", station.RSSI),
bssid,
@ -171,6 +170,7 @@ func (w *WiFiRecon) getRow(station *network.Station) []string {
station.Vendor,
encryption,
strconv.Itoa(mhz2chan(station.Frequency)),
strconv.Itoa(ap.NumClients()),
sent,
recvd,
seen,
@ -200,11 +200,23 @@ func (w *WiFiRecon) showTable(header []string, rows [][]string) {
}
func (w *WiFiRecon) isApSelected() bool {
return w.apBSSID != nil
return w.ap != nil
}
func (w *WiFiRecon) Show(by string) error {
stations := w.Session.WiFi.List()
var stations []*network.Station
apSelected := w.isApSelected()
if apSelected {
if ap, found := w.Session.WiFi.Get(w.ap.HwAddress); found == true {
stations = ap.Clients()
} else {
return fmt.Errorf("Could not find station %s", w.ap.HwAddress)
}
} else {
stations = w.Session.WiFi.Stations()
}
if by == "seen" {
sort.Sort(ByWiFiSeenSorter(stations))
} else if by == "essid" {
@ -221,15 +233,15 @@ func (w *WiFiRecon) Show(by string) error {
}
nrows := len(rows)
columns := []string{"RSSI", "BSSID", "SSID", "Vendor", "Encryption", "Channel", "Sent", "Recvd", "Last Seen"}
if w.isApSelected() {
columns := []string{"RSSI", "BSSID", "SSID", "Vendor", "Encryption", "Channel", "Clients", "Sent", "Recvd", "Last Seen"}
if apSelected {
// these are clients
columns = []string{"RSSI", "MAC", "Vendor", "Channel", "Sent", "Received", "Last Seen"}
if nrows == 0 {
fmt.Printf("\nNo authenticated clients detected for %s.\n", w.apBSSID.String())
fmt.Printf("\nNo authenticated clients detected for %s.\n", w.ap.HwAddress)
} else {
fmt.Printf("\n%s clients:\n", w.apBSSID.String())
fmt.Printf("\n%s clients:\n", w.ap.HwAddress)
}
}
@ -305,7 +317,20 @@ func (w *WiFiRecon) sendDeauthPacket(ap net.HardwareAddr, client net.HardwareAdd
}
}
func (w *WiFiRecon) startDeauth(apMac net.HardwareAddr, clMac net.HardwareAddr) error {
func (w *WiFiRecon) onChannel(channel int, cb func()) {
prev := w.stickChan
w.stickChan = channel
if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil {
log.Warning("Error while hopping to channel %d: %s", channel, err)
}
cb()
w.stickChan = prev
}
func (w *WiFiRecon) startDeauth(to net.HardwareAddr) error {
// if not already running, temporarily enable the pcap handle
// for packet injection
if w.Running() == false {
@ -315,21 +340,34 @@ func (w *WiFiRecon) startDeauth(apMac net.HardwareAddr, clMac net.HardwareAddr)
defer w.handle.Close()
}
// deauth a specific client
if clMac != nil {
log.Info("Deauthing client %s from AP %s ...", clMac.String(), apMac.String())
w.sendDeauthPacket(apMac, clMac)
} else {
log.Info("Deauthing clients from AP %s ...", apMac.String())
// deauth every authenticated client
for _, station := range w.Session.WiFi.List() {
if station.IsAP == false {
w.sendDeauthPacket(apMac, station.HW)
bssid := to.String()
// are we deauthing every client of a given access point?
if ap, found := w.Session.WiFi.Get(bssid); found == true {
clients := ap.Clients()
log.Info("Deauthing %d clients from AP %s ...", len(clients), ap.ESSID())
w.onChannel(mhz2chan(ap.Frequency), func() {
for _, c := range clients {
w.sendDeauthPacket(ap.HW, c.HW)
}
})
return nil
}
// search for a client
aps := w.Session.WiFi.List()
for _, ap := range aps {
if c, found := ap.Get(bssid); found == true {
log.Info("Deauthing client %s from AP %s ...", c.HwAddress, ap.ESSID())
w.onChannel(mhz2chan(ap.Frequency), func() {
w.sendDeauthPacket(ap.HW, c.HW)
})
return nil
}
}
return nil
return fmt.Errorf("%s is an unknown BSSID.", bssid)
}
func (w *WiFiRecon) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) {
@ -337,17 +375,17 @@ func (w *WiFiRecon) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layer
if ok, ssid := packets.Dot11ParseIDSSID(packet); ok == true {
bssid := dot11.Address3.String()
frequency := int(radiotap.ChannelFrequency)
w.Session.WiFi.AddIfNew(ssid, bssid, true, frequency, radiotap.DBMAntennaSignal)
w.Session.WiFi.AddIfNew(ssid, bssid, frequency, radiotap.DBMAntennaSignal)
}
}
func (w *WiFiRecon) discoverClients(radiotap *layers.RadioTap, dot11 *layers.Dot11, ap net.HardwareAddr, packet gopacket.Packet) {
func (w *WiFiRecon) discoverClients(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) {
w.Session.WiFi.EachAccessPoint(func(bssid string, ap *network.AccessPoint) {
// packet going to this specific BSSID?
if packets.Dot11IsDataFor(dot11, ap) == true {
src := dot11.Address2
frequency := int(radiotap.ChannelFrequency)
w.Session.WiFi.AddIfNew("", src.String(), false, frequency, radiotap.DBMAntennaSignal)
if packets.Dot11IsDataFor(dot11, ap.HW) == true {
ap.AddClient(dot11.Address2.String(), int(radiotap.ChannelFrequency), radiotap.DBMAntennaSignal)
}
})
}
func (w *WiFiRecon) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) {
@ -387,6 +425,12 @@ func (w *WiFiRecon) channelHopper() {
for _, frequency := range w.frequencies {
channel := mhz2chan(frequency)
// stick to the access point channel as long as it's selected
// or as long as we're deauthing on it
if w.stickChan != 0 {
channel = w.stickChan
}
if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil {
log.Warning("Error while hopping to channel %d: %s", channel, err)
}
@ -438,15 +482,9 @@ func (w *WiFiRecon) Start() error {
// perform initial dot11 parsing and layers validation
if ok, radiotap, dot11 := packets.Dot11Parse(packet); ok == true {
// keep collecting traffic statistics
w.updateStats(dot11, packet)
// no access point bssid selected, keep scanning for other aps
if w.isApSelected() == false {
w.discoverAccessPoints(radiotap, dot11, packet)
} else {
// discover stations connected to the selected access point bssid
w.discoverClients(radiotap, dot11, w.apBSSID, packet)
}
w.discoverClients(radiotap, dot11, packet)
}
}
})

View file

@ -6,25 +6,25 @@ import (
"time"
)
type StationNewCallback func(s *Station)
type StationLostCallback func(s *Station)
type APNewCallback func(ap *AccessPoint)
type APLostCallback func(ap *AccessPoint)
type WiFi struct {
sync.Mutex
stations map[string]*Station
aps map[string]*AccessPoint
iface *Endpoint
newCb StationNewCallback
lostCb StationLostCallback
newCb APNewCallback
lostCb APLostCallback
}
type wifiJSON struct {
Stations []*Station `json:"stations"`
AccessPoints []*AccessPoint `json:"aps"`
}
func NewWiFi(iface *Endpoint, newcb StationNewCallback, lostcb StationLostCallback) *WiFi {
func NewWiFi(iface *Endpoint, newcb APNewCallback, lostcb APLostCallback) *WiFi {
return &WiFi{
stations: make(map[string]*Station),
aps: make(map[string]*AccessPoint),
iface: iface,
newCb: newcb,
lostCb: lostcb,
@ -33,23 +33,43 @@ func NewWiFi(iface *Endpoint, newcb StationNewCallback, lostcb StationLostCallba
func (w *WiFi) MarshalJSON() ([]byte, error) {
doc := wifiJSON{
Stations: make([]*Station, 0),
AccessPoints: make([]*AccessPoint, 0),
}
for _, s := range w.stations {
doc.Stations = append(doc.Stations, s)
for _, ap := range w.aps {
doc.AccessPoints = append(doc.AccessPoints, ap)
}
return json.Marshal(doc)
}
func (w *WiFi) List() (list []*Station) {
func (w *WiFi) EachAccessPoint(cb func(mac string, ap *AccessPoint)) {
w.Lock()
defer w.Unlock()
for m, ap := range w.aps {
cb(m, ap)
}
}
func (w *WiFi) Stations() (list []*Station) {
w.Lock()
defer w.Unlock()
list = make([]*Station, 0)
for _, t := range w.stations {
list = append(list, t)
for _, ap := range w.aps {
list = append(list, ap.Station)
}
return
}
func (w *WiFi) List() (list []*AccessPoint) {
w.Lock()
defer w.Unlock()
list = make([]*AccessPoint, 0)
for _, ap := range w.aps {
list = append(list, ap)
}
return
}
@ -58,45 +78,45 @@ func (w *WiFi) Remove(mac string) {
w.Lock()
defer w.Unlock()
if s, found := w.stations[mac]; found {
delete(w.stations, mac)
if ap, found := w.aps[mac]; found {
delete(w.aps, mac)
if w.lostCb != nil {
w.lostCb(s)
w.lostCb(ap)
}
}
}
func (w *WiFi) AddIfNew(ssid, mac string, isAp bool, frequency int, rssi int8) *Station {
func (w *WiFi) AddIfNew(ssid, mac string, frequency int, rssi int8) *AccessPoint {
w.Lock()
defer w.Unlock()
mac = NormalizeMac(mac)
if station, found := w.stations[mac]; found {
station.LastSeen = time.Now()
station.RSSI = rssi
return station
if ap, found := w.aps[mac]; found {
ap.LastSeen = time.Now()
ap.RSSI = rssi
return ap
}
newStation := NewStation(ssid, mac, isAp, frequency, rssi)
w.stations[mac] = newStation
newAp := NewAccessPoint(ssid, mac, frequency, rssi)
w.aps[mac] = newAp
if w.newCb != nil {
w.newCb(newStation)
w.newCb(newAp)
}
return nil
}
func (w *WiFi) Get(mac string) (*Station, bool) {
func (w *WiFi) Get(mac string) (*AccessPoint, bool) {
w.Lock()
defer w.Unlock()
mac = NormalizeMac(mac)
station, found := w.stations[mac]
return station, found
ap, found := w.aps[mac]
return ap, found
}
func (w *WiFi) Clear() error {
w.stations = make(map[string]*Station)
w.aps = make(map[string]*AccessPoint)
return nil
}

88
network/wifi_ap.go Normal file
View file

@ -0,0 +1,88 @@
package network
import (
"encoding/json"
"sync"
"time"
)
type AccessPoint struct {
*Station
sync.Mutex
clients map[string]*Station
}
type apJSON struct {
*Station
Clients []*Station `json:"clients"`
}
func NewAccessPoint(essid, bssid string, frequency int, rssi int8) *AccessPoint {
return &AccessPoint{
Station: NewStation(essid, bssid, frequency, rssi),
clients: make(map[string]*Station),
}
}
func (ap *AccessPoint) MarshalJSON() ([]byte, error) {
doc := apJSON{
Station: ap.Station,
Clients: make([]*Station, 0),
}
for _, c := range ap.clients {
doc.Clients = append(doc.Clients, c)
}
return json.Marshal(doc)
}
func (ap *AccessPoint) Get(bssid string) (*Station, bool) {
ap.Lock()
defer ap.Unlock()
bssid = NormalizeMac(bssid)
if s, found := ap.clients[bssid]; found == true {
return s, true
}
return nil, false
}
func (ap *AccessPoint) AddClient(bssid string, frequency int, rssi int8) *Station {
ap.Lock()
defer ap.Unlock()
bssid = NormalizeMac(bssid)
if s, found := ap.clients[bssid]; found == true {
// update
s.Frequency = frequency
s.RSSI = rssi
s.LastSeen = time.Now()
return s
}
s := NewStation("", bssid, frequency, rssi)
ap.clients[bssid] = s
return s
}
func (ap *AccessPoint) NumClients() int {
ap.Lock()
defer ap.Unlock()
return len(ap.clients)
}
func (ap *AccessPoint) Clients() (list []*Station) {
ap.Lock()
defer ap.Unlock()
list = make([]*Station, 0)
for _, c := range ap.clients {
list = append(list, c)
}
return
}

View file

@ -2,18 +2,16 @@ package network
type Station struct {
*Endpoint
IsAP bool
Frequency int
RSSI int8
Sent uint64
Received uint64
Encryption string
Frequency int `json:"frequency"`
RSSI int8 `json:"rssi"`
Sent uint64 `json:"sent"`
Received uint64 `json:"received"`
Encryption string `json:"encryption"`
}
func NewStation(essid, bssid string, isAp bool, frequency int, rssi int8) *Station {
func NewStation(essid, bssid string, frequency int, rssi int8) *Station {
return &Station{
Endpoint: NewEndpointNoResolve(MonitorModeAddress, bssid, essid, 0),
IsAP: isAp,
Frequency: frequency,
RSSI: rssi,
}

View file

@ -378,10 +378,10 @@ func (s *Session) Start() error {
s.Firewall = firewall.Make(s.Interface)
s.WiFi = network.NewWiFi(s.Interface, func(st *network.Station) {
s.Events.Add("wifi.station.new", st)
}, func(st *network.Station) {
s.Events.Add("wifi.station.lost", st)
s.WiFi = network.NewWiFi(s.Interface, func(ap *network.AccessPoint) {
s.Events.Add("wifi.ap.new", ap)
}, func(ap *network.AccessPoint) {
s.Events.Add("wifi.ap.lost", ap)
})
s.Lan = network.NewLAN(s.Interface, s.Gateway, func(e *network.Endpoint) {