Skip to content

Commit

Permalink
Get the files combined into watchtower.py
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Salmon committed Sep 28, 2018
1 parent 8e09baa commit f1569e1
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 3 deletions.
6 changes: 3 additions & 3 deletions hostapd-configs/testNetwork-rogue.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@

# wpa: This is a bit field like auth_algs.
# The first bit enables WPA1 (1), the second bit enables WPA2 (2), and both enables both (3)
interface=wlan0mon
interface=wlan1mon
hw_mode=g
channel=6
ieee80211d=1
country_code=US
ieee80211n=1
wmm_enabled=1
ssid=testdontuseme
ssid=[2.4Ghz]Mount Olympus
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=TKIP
rsn_pairwise=CCMP
wpa_passphrase=areallystrongpassword
227 changes: 227 additions & 0 deletions watchtower.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
#!/usr/bin/env python

import sys, os, signal
from multiprocessing import Process
import json
from scapy.all import *

interface = '' # monitor interface
aps = set() # dictionary to store unique APs
clients = set()
deauthTimes = {}
deauthAlertTimes = {}
deauthAlertTimeout = 5 # How long (in seconds) minimum to wait between detected deauths to call it a new attack


def checkAP(ap_mac, ap_channel, ap_enc, ap_cipher, ap_auth):
if config['checks']['checkMAC']:
if ap_mac.upper() not in config['macs']:
return False

if config['checks']['checkChannel']:
if ap_channel != config['channel']:
print("Bad channel: ", ap_channel, " ", config['channel'])
return False

if config['checks']['checkEncryption']:
if ap_enc != config['encryption']:
print("Bad encryption: ", ap_enc, " ", config['encryption'])
return False

if config['checks']['checkCipher']:
if ap_cipher != config['cipher']:
print("Bad cipher: ", ap_cipher, " ", config['cipher'])
return False

if config['checks']['checkAuthentication']:
if ap_auth != config['authentication']:
print("Bad auth: ", ap_auth, " - ", config['authentication'])
return False

return True


def noise_filter(addr1, addr2):
# Broadcast, broadcast, IPv6mcast, spanning tree, spanning tree, multicast, broadcast
ignore = ['ff:ff:ff:ff:ff:ff', '00:00:00:00:00:00', '33:33:00:', '33:33:ff:', '01:80:c2:00:00:00', '01:00:5e:'] # possibly add our detecting MAC address

for i in ignore:
if i in addr1 or i in addr2:
return True


def getWPA2info(pkt):
# pkt = p.getlayer(Dot11Elt, ID=48)
# print(pkt.ID, len(pkt.info), pkt.info)

# Array slices don't include end index so add 1

# 00-0F-AC-01 WEP40
# 00-0F-AC-05 WEP104
# 00-0F-AC-04 CCMP
# 00-0F-AC-02 TKIP

# OUI = [2:4]
groupCipherOUI = pkt.info[2:5]

# Group Cipher Type = [5]
# 1 = WEP40, 2 = TKIP, 4 = CCMP, 5 = WEP104
groupCipherType = pkt.info[5]

if groupCipherType == 1:
cipher = "WEP40"
elif groupCipherType == 2:
cipher = "TKIP"
elif groupCipherType == 4:
cipher = "CCMP"
elif groupCipherType == 5:
cipher = "WEP104"
else:
cipher = "???"

# Pairwise Cipher Count = [6:7]
# pairwiseCipherCount = pkt.info[6]

# PairwiseKey Cipher List (array?) = [8:11]
# pairwiseCipherOUI =

# AuthKey Mngmnt Count = [12:13]
authKeyMgmtCount = pkt.info[12]

# AuthKey Mngmnt Suite List = [14:17]
# 00-0f-ac-02 PSK
# 00-0f-ac-01 802.1x (EAP)
authKeyMgmtSuite = pkt.info[14:18]
if authKeyMgmtSuite == b'\x00\x0f\xac\x02':
auth = "PSK"
elif authKeyMgmtSuite == b'\x00\x0f\xac\x01':
auth = "EAP"
else:
auth = "???"

return {
"groupCipherOUI": groupCipherOUI,
"groupCipherType": groupCipherType,
"cipher": cipher,
"authKeyMgmtCount": authKeyMgmtCount,
"authKeyMgmtSuite": authKeyMgmtSuite,
"auth": auth
}


def sniffAP(pkt):

# Look for clients of our network
if pkt.haslayer(Dot11) and pkt.type == 2:
if noise_filter(pkt.addr1, pkt.addr2):
return

if pkt.addr1.upper() in config['macs'] and pkt.addr2.upper() not in clients:
clients.add(pkt.addr2.upper())
# print("AP", " ", pkt.addr2.upper())
elif pkt.addr2.upper() in config['macs'] and pkt.addr1.upper() not in clients:
clients.add(pkt.addr1.upper())
# print(pkt.addr1.upper(), " ", "AP")

# Watch for deauth-ing of our clients
elif pkt.haslayer(Dot11Deauth):

sourceMAC = str(pkt[Dot11].addr2).upper()

if sourceMAC not in clients: # We only care about our AP and clients
return

if sourceMAC in deauthTimes:
timeFromLastDeauth = time.time() - deauthTimes[sourceMAC]
if timeFromLastDeauth < 5:
if sourceMAC not in deauthAlertTimes or time.time() - deauthAlertTimes[sourceMAC] > deauthAlertTimeout:
print("Deauth detected! Targeted client: ", sourceMAC)
# print(deauthTimes)
deauthAlertTimes[sourceMAC] = time.time()
deauthTimes[sourceMAC] = time.time()


# process unique sniffed Beacons and ProbeResponses.
if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp):
ssid = pkt[Dot11Elt].info.decode('UTF-8')
bssid = str(pkt[Dot11].addr3).upper()
channel = int(ord(pkt[Dot11Elt:3].info))
capability = pkt.sprintf("{Dot11Beacon:%Dot11Beacon.cap%}\
{Dot11ProbeResp:%Dot11ProbeResp.cap%}")

# Check for encrypted networks
if re.search("privacy", capability):
priv = 'Y'
else:
priv = 'N'

# Determine encryption type
if pkt.getlayer(Dot11Elt, ID=48) is not None:
enc = "WPA2"
elif pkt.getlayer(Dot11Elt, ID=221) is not None and pkt.getlayer(Dot11Elt, ID=221).info.startswith(
b'\x00P\xf2\x01\x01\x00'):
enc = "WPA"
else:
if priv == 'Y':
enc = "WEP"
else:
enc = "OPEN"

if ssid == config['ssid']:
if enc == "WPA2":
apInfo = getWPA2info(pkt.getlayer(Dot11Elt, ID=48))
else:
apInfo = {}

currentAP = " {:>2d} {:s} {:s} {:s} {:s} {:s} {:s}".format(
int(channel), priv, enc, apInfo["cipher"], apInfo["auth"], bssid, ssid)

if currentAP not in aps: # This is an AP we haven't seen before
aps.add(currentAP)
if checkAP(bssid, channel, enc, apInfo["cipher"], apInfo["auth"]):
print(" GOOD ", currentAP)

else:
print(" BAD ", currentAP)


# Channel hopper
def channel_hopper():
while True:
try:
channel = random.randrange(1, 13)
os.system("iw dev %s set channel %d" % (interface, channel))
time.sleep(1)
except KeyboardInterrupt:
break


# Capture interrupt signal and cleanup before exiting
def signal_handler(signal, frame):
# p.terminate()
# p.join()

sys.exit(0)


if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage %s monitor_interface".format(sys.argv[0]))
sys.exit(1)

interface = sys.argv[1]

with open('config.json') as f:
config = json.load(f)

# Start the channel hopper
p = Process(target=channel_hopper)
# p.start()

# Capture CTRL-C
signal.signal(signal.SIGINT, signal_handler)

# print("\nSTATUS CHAN PRIV ENC CIPHER AUTH MAC SSID")
# print("====================================================")
# Start the sniffer
sniff(iface=interface, prn=sniffAP, store=0)

0 comments on commit f1569e1

Please sign in to comment.