diff --git a/AUTHORS b/AUTHORS index a22d0e7..6cdcc4a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,2 +1,3 @@ Cedric Blancher Oliver Lavery +Gabriel Ryan diff --git a/Changelog b/Changelog index 34e9e78..cd2f78a 100644 --- a/Changelog +++ b/Changelog @@ -44,3 +44,16 @@ 0.4.1 - Updated to use modern scapy and work with Linux radiotap headers Oliver Lavery, Gotham Digital Science (olavery@gdssecurity.com) + +0.4.2 - Gabriel Ryan, Gotham Digital Science (gryan@gdssecurity.com), March 28th 2017 + Fixed indentation caused by mixing tabs and spaces. Project now uses spaces only. + Fixed import statements to work with modern versions of Scapy. + Removed unused imports. + Psyco is no longer maintained and does not support Python 2.7. Removed from project in favor of PyPy. + Code cleanup and optimizations. + Project now uses the newer argparse module instead of getopt to parse command line arguments. + Input validation is now done through argparse as much as possible. + Added utils.py file containing commonly used utility functions. + Added include guard to all files. + Added pip requirements file. + Updated README. diff --git a/README b/README deleted file mode 100644 index 75a2307..0000000 --- a/README +++ /dev/null @@ -1,154 +0,0 @@ -######################################### -# -# wifitap.py --- WiFi injection tool through tun/tap device -# Cedric Blancher -# -# http://sid.rstack.org/index.php/Wifitap (french) -# http://sid.rstack.org/index.php/Wifitap_EN (english) -# -######################################### - -This program is a proof of concept tool allowing WiFi communications using -traffic injection. -You'll need: - - . Python >= 2.2 - . Psyco Python optimizer (optional) - . Philippe Biondi's Scapy - . Injection ready wireless adapter - -It's been tested on GNU/Linux using Atheros chipset based adapter with patched -Madwifi driver and Intersil Prism GT Full MACchipset with Prism54 driver. It -should as well work with Prism2/2.5/3 chipset hostap driver or wlan-ng driver, -Ralink rt2500/2750 chipset using rt2500 driver and Realtek RTL8180 chipset -using rtl8180-sa2400 driver. - -I didn't take time to test Prism2/2.5/3 support and don't have Ralink or Realtek -based hardware for testing. By the way, I would be glad to have feedback for -Wifitap attempts with thoses chipsets. - -Drivers patches are written by Christophe Devine and updated by Aircrack-ng -people. For details about drivers patch and installation, see PATCHING file. - - -To get wifitap work on other Unix operating systems than GNU/Linux, you have to -install pcap or dnet wrappers for Python so Scapy can work (see -http://www.secdev.org/projects/scapy/portability.html). Then, and it's the most -important part, you have to find a wireless adapter driver that supports raw -wireless traffic injection if any. - - -NB : Python is so slow... - - -o Getting Wifitap ;) - - Wifitap is available at: - - http://sid.rstack.org/index.php/Wifitap (french) - http://sid.rstack.org/index.php/Wifitap_EN (english) - - Lastest version is downloadable at: - - http://sid.rstack.org/code/wifitap.tgz - - Repository available at: - - http://sid.rstack.org/code/wifitap/ - - -o Getting Scapy - - A working Scapy version is attached, so Wifitap is ready to work. - However, you can get a more featured version of the tool at: - - http://www.secdev.org/projects/scapy/ - - Download "work-in-progress" version or (better) use provided version... - - -o Preparing WiFi adapter - - Download, patch and install driver (see PATCHING). - - Supposing channel is 11: - - ~# iwconfig $IFACE mode monitor channel 11 - ~# ifconfig $IFACE up promisc - - NB: Atheros driver Madwifi requires specific configuration to get driver - in promisc mode and/or activate traffic injection. See website - (http://www.madwifi.org/) for details if you use madwifi-ng or - madwifi-old. - -o Launching Wifitap - - ~# ./wifitap.py -b - - A wj0 interface will be created that needs to be configured as a - regular interface, with optional MAC address specification: - - ~# ifconfig wj0 [hw ether ] 192.168.1.1 [mtu ] - - -o Using Wifitap - - Now, you can us wj0 interface just as a usual interface to communicate - with your prefered applications and tools, according to system routing - table :) - - -o Wifitap command line arguments - - Usage : wifitap -b [-o ] [-i [-s ] - [-w [-k ]] [-d [-v]] [-h] - - -b Specifies BSSID in ususal 6 hex digits MAC address format: - . 00:01:02:03:04:05 - - -o Specifies output WiFi interface for frames injection - - -i Specifies input WiFi interface for frames sniffing - - -s Specifies source MAC address - . 00:01:02:03:04:05 - - -w Activates WEP encryption/decryption with specified WEP key - Key can be given using following formats: - . 0102030405 or 0102030405060708090a0b0c0d - . 01:02:03:04:05 or - 01:02:03:04:05:06:07:08:09:0a:0b:0c:0d - . 0102-0304-05 or 0102-0304-0506-0708-090a-0b0c-0d - - -k Specifies WEP key id, from 0 to 3 - - -d Activates debugging - - -v Increases debugging verbosity - - -h Help screen - -o Latest libpcap fully supports Wi-Fi specific headers, typically Prism Headers. - However, if your system uses old libpcap, you will need to apply provided - patch: - - patch -p0 < prismheaders.patch - - It will add a flag (-p) to tell Wifitap to shift 144 bits of Prism Headers to - access 802.11 frame. - - -######################################### -# -# Copyright (C) 2005 Cedric Blancher -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation; version 2. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -######################################### diff --git a/README.md b/README.md new file mode 100644 index 0000000..18f4712 --- /dev/null +++ b/README.md @@ -0,0 +1,143 @@ +Wifitap +======= + + ######################################### + # + # wifitap.py --- WiFi injection tool through tun/tap device + # Cedric Blancher + # + # http://sid.rstack.org/index.php/Wifitap (french) + # http://sid.rstack.org/index.php/Wifitap_EN (english) + # + ######################################### + +This program is a proof of concept tool allowing WiFi communications using +traffic injection. + +You'll need: + + . Python >= 2.2 + . Philippe Biondi's Scapy + . Injection ready wireless adapter + +It's been tested on GNU/Linux using Atheros chipset based adapter with patched +Madwifi driver and Intersil Prism GT Full MACchipset with Prism54 driver. It +should as well work with Prism2/2.5/3 chipset hostap driver or wlan-ng driver, +Ralink rt2500/2750 chipset using rt2500 driver and Realtek RTL8180 chipset +using rtl8180-sa2400 driver. + +I didn't take time to test Prism2/2.5/3 support and don't have Ralink or Realtek +based hardware for testing. By the way, I would be glad to have feedback for +Wifitap attempts with thoses chipsets. + +Drivers patches are written by Christophe Devine and updated by Aircrack-ng +people. For details about drivers patch and installation, see PATCHING file. + +To get wifitap work on other Unix operating systems than GNU/Linux, you have to +install pcap or dnet wrappers for Python so Scapy can work (see +http://www.secdev.org/projects/scapy/portability.html). Then, and it's the most +important part, you have to find a wireless adapter driver that supports raw +wireless traffic injection if any. + + * [Setup Guide](#setup-guide) + * [Getting Wifitap ;)](#getting-wifitap-) + * [Installing Dependencies](#installing-dependencies) + * [Usage Guide](#usage-guide) + * [Launching Wifitap](#launching-wifitap) + * [Wifitap Command Line Arguments](#wifitap-command-line-arguments) + +Setup Guide +=========== + +Getting Wifitap ;) +------------------ + +The latest Wifitap can be obtained from this Git repo. + +Older versions of Wifitap are available at the following locations: + +- [http://sid.rstack.org/index.php/Wifitap (french)](http://sid.rstack.org/index.php/Wifitap) +- [http://sid.rstack.org/index.php/Wifitap\_EN (english)](http://sid.rstack.org/index.php/Wifitap_EN) + + + +Installing Dependencies +----------------------- + +Python dependencies are enumerated in `pip.req`, can be installed using the pip as shown below. + + pip install -r pip.req + +Wifitap's most important dependency is Scapy, which you should check out because it's totally awesome. + + - [http://www.secdev.org/projects/scapy/](http://www.secdev.org/projects/scapy/) + + +Usage Guide +=========== + +Launching Wifitap +----------------- + +Before you start, make sure your wireless interface is in monitor mode. In the commands shown below, substitute "wlan0" for the name of your wireless interface. + + ifconfig wlan0 down + iw dev wlan0 set monitor none + ifconfig wlan0 up promisc + +To launch Wifitap with basic options, use the following command: + + ./wifitap.py -b + +This will create a wj0 interface. Next, you'll need to configure this new interface using syntax shown below. Note that you can optionally specify the MAC in the following command. + + ifconfig wj0 [hw ether ] 192.168.1.1 [mtu ] + +You'll now be able to use your newly created wj0 interface as if it were normal network interface, according to your system routing table. :) + + +Wifitap Command Line Arguments +------------------------------ + + Usage : wifitap -b [-o ] [-i [-s ] + [-w [-k ]] [-d [-v]] [-h] + + -b Specifies BSSID in ususal 6 hex digits MAC address format: + . 00:01:02:03:04:05 + + -o Specifies output WiFi interface for frames injection + + -i Specifies input WiFi interface for frames sniffing + + -s Specifies source MAC address + . 00:01:02:03:04:05 + + -w Activates WEP encryption/decryption with specified WEP key + Key can be given using following formats: + . 0102030405 or 0102030405060708090a0b0c0d + . 01:02:03:04:05 or + 01:02:03:04:05:06:07:08:09:0a:0b:0c:0d + . 0102-0304-05 or 0102-0304-0506-0708-090a-0b0c-0d + + -k Specifies WEP key id, from 0 to 3 + + -d Activates debugging + + -v Increases debugging verbosity + + -h Help screen + + ######################################### + # + # Copyright (C) 2005 Cedric Blancher + # + # This program is free software; you can redistribute it and/or modify it + # under the terms of the GNU General Public License version 2 as + # published by the Free Software Foundation; version 2. + # + # This program is distributed in the hope that it will be useful, but + # WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + # General Public License for more details. + # + ######################################### diff --git a/pip.req b/pip.req new file mode 100644 index 0000000..e69de29 diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..c5b47b2 --- /dev/null +++ b/utils.py @@ -0,0 +1,49 @@ +import re +import argparse + +def parse_wep_key(wepkey, keyid): + + print wepkey + + # Match and parse WEP key + tmp_key = "" + if re.match('^([0-9a-fA-F]{2}){5}$', wepkey) or re.match ('^([0-9a-fA-F]{2}){13}$', wepkey): + tmp_key = wepkey + elif re.match('^([0-9a-fA-F]{2}[:]){4}[0-9a-fA-F]{2}$', wepkey) or re.match('^([0-9a-fA-F]{2}[:]){12}[0-9a-fA-F]{2}$', wepkey): + tmp_key = re.sub(':', '', wepkey) + elif re.match ('^([0-9a-fA-F]{4}[-]){2}[0-9a-fA-F]{2}$', wepkey) or re.match ('^([0-9a-fA-F]{4}[-]){6}[0-9a-fA-F]{2}$', wepkey): + tmp_key = re.sub('-', '', wepkey) + else: + return None + g = lambda x: chr(int(tmp_key[::2][x],16)*16+int(tmp_key[1::2][x],16)) + + conf_wepkey = '' + for i in range(len(tmp_key)/2): + conf_wepkey += g(i) + + return conf_wepkey + +def sanitize_bssid(bssid): + + # Match and parse BSSID + if re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', bssid): + return bssid.lower() + + msg = 'Invalid bssid.' + raise argparse.ArgumentTypeError(msg) + +def sanitize_smac(smac): + + if type(smac) == str and re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', smac): + return smac.lower() + + msg = 'Invalid SMAC.' + raise argparse.ArgumentTypeError(msg) + +def sanitize_ipdns(ipdns): + + if re.match('^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$', ipdns): + return ipdns + + msg = 'Error: Wrong IP address' + raise argparse.ArgumentTypeError(msg) diff --git a/wifiarp.py b/wifiarp.py index a4ceac1..17c88c2 100755 --- a/wifiarp.py +++ b/wifiarp.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#!/usr/bin/env python ######################################## # @@ -17,200 +17,173 @@ # ######################################### -import os,sys,getopt,struct,re,string, logging - -# Import Psyco if available to speed up execution -try: - import psyco - psyco.full() -except ImportError: - print "Psyco optimizer not installed, running anyway..." - pass +import os +import sys +import argparse +import struct +import logging +import utils from socket import * from fcntl import ioctl from select import select logging.getLogger("scapy").setLevel(1) -from scapy.all import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf - -# We want to build an ICMP Echo Request answering machine - -from scapy import ARP - -IN_IFACE = "wlan0" -OUT_IFACE = "wlan0" -WEP = 0 -KEYID = 0 -DEBUG = 0 -VERB = 0 -BSSID = "" -WEPKEY = "" -HWSRC = "" - - -def usage(status=0): - print "Usage: wifitap -b -s [-o ] [-i ]" - print " [-w [-k ]] [-d [-v]]" - print " [-h]" - print " -b specify BSSID for injection" - print " -s specify source MAC address for 802.11 and ARP headers" - print " -o specify interface for injection (default: wlan0)" - print " -w WEP mode and key" - print " -k WEP key id (default: 0)" - print " -d activate debug" - print " -v verbose debugging" - print " -h this so helpful output" - sys.exit(status) - -opts = getopt.getopt(sys.argv[1:],"b:o:i:s:w:k:dvh") - -for opt,optarg in opts[0]: - if opt == "-b": - BSSID = optarg - elif opt == "-o": - OUT_IFACE = optarg - elif opt == "-i": - IN_IFACE = optarg - elif opt == "-s": - HWSRC = optarg - elif opt == "-w": - WEP = 1 - WEPKEY = optarg - elif opt == "-k": - KEYID = int(optarg) - elif opt == "-d": - DEBUG = 1 - elif opt == "-v": - VERB = 1 - elif opt == "-h": - usage() - -if not BSSID: - print "\nError: BSSID not defined\n" - usage() - -# Match and parse BSSID -if re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', BSSID): - BSSID = BSSID.lower() -else: - print "\nError: Wrong format for BSSID\n" - usage () - -if not HWSRC: - print "\nError: HWSRC not defined\n" - usage() - -# Match and parse HWSRC -if re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', HWSRC): - HWSRC = HWSRC.lower() -else: - print "\nError: Wrong format for HWSRC\n" - usage () - -print "IN_IFACE: %s" % IN_IFACE -print "OUT_IFACE: %s" % OUT_IFACE -print "BSSID: %s" % BSSID -print "HWSRC: %s" % HWSRC - -if WEP: - # Match and parse WEP key - tmp_key = "" - if re.match('^([0-9a-fA-F]{2}){5}$', WEPKEY) or re.match ('^([0-9a-fA-F]{2}){13}$', WEPKEY): - tmp_key = WEPKEY - elif re.match('^([0-9a-fA-F]{2}[:]){4}[0-9a-fA-F]{2}$', WEPKEY) or re.match('^([0-9a-fA-F]{2}[:]){12}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub(':', '', WEPKEY) - elif re.match ('^([0-9a-fA-F]{4}[-]){2}[0-9a-fA-F]{2}$', WEPKEY) or re.match ('^([0-9a-fA-F]{4}[-]){6}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub('-', '', WEPKEY) - else: - print "\nError : Wrong format for WEP key\n" - usage() - g = lambda x: chr(int(tmp_key[::2][x],16)*16+int(tmp_key[1::2][x],16)) - for i in range(len(tmp_key)/2): - conf.wepkey += g(i) - print "WEP key: %s (%dbits)" % (WEPKEY, len(tmp_key)*4) - if KEYID > 3 or KEYID < 0: - print "Key id: %s (defaulted to 0 due to wrong -k argument)" % KEYID - KEYID = 0 - else: - print "Key id: %s" % KEYID -else: - if KEYID != 0: - print "WEP not activated, key id ignored" - -if not DEBUG: - if VERB: - print "DEBUG not activated, verbosity ignored" -else: - print "DEBUG activated" - if VERB: - print "Verbose debugging" - -conf.iface = OUT_IFACE - -# Here we put a BPF filter so only 802.11 Data/to-DS frames are captured -s = conf.L2listen(iface = IN_IFACE, - filter = "link[0]&0xc == 8 and link[1]&0xf == 1") - -# Speed optimization si Scapy does not have to parse payloads -ARP.payload_guess=[] - -try: - while 1: - dot11_frame = s.recv(2346) - - # WEP handling is automagicly done by Scapy if conf.wepkey is set - # Nothing to do to decrypt (although not yet tested) - # WEP frames have Dot11WEP layer, others don't - if DEBUG and VERB: - if dot11_frame.haslayer(Dot11WEP): # WEP frame - os.write(1,"Received WEP from %s\n" % IN_IFACE) - else: # Cleartext frame - os.write(1,"Received from %s\n" % IN_IFACE) - os.write(1,"%s\n" % dot11_frame.summary()) - - if dot11_frame.getlayer(Dot11).addr1 != BSSID: - continue - - # Identifying ARP Requests - if dot11_frame.haslayer(ARP) and dot11_frame.getlayer(ARP).op == 1: - if DEBUG: - os.write(1,"Received ARP Request on %s\n" % IN_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_frame.summary()) - - # Building ARP Reply answer for injection - dot11_answer = RadioTap()/Dot11( - type = "Data", - FCfield = "from-DS", - addr1 = dot11_frame.getlayer(Dot11).addr2, - addr2 = BSSID) - dot11_answer.addr3 = HWSRC - if WEP: - dot11_answer.FCfield |= 0x40 - dot11_answer /= Dot11WEP( - iv = "111", - keyid = KEYID) - dot11_answer /= LLC(ctrl=3)/SNAP()/ARP( - op = "is-at", - hwsrc = HWSRC, - psrc = dot11_frame.getlayer(ARP).pdst, - hwdst = dot11_frame.getlayer(ARP).hwsrc, - pdst = dot11_frame.getlayer(ARP).psrc) - dot11_answer /= dot11_frame.getlayer(ARP).payload - - if DEBUG: - os.write(1,"Sending ARP Reply on %s\n" % OUT_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_answer.summary()) - - # Frame injection : - sendp(dot11_answer,verbose=0) # Send frame - -# Program killed -except KeyboardInterrupt: - print "Stopped by user." - -s.close() - -sys.exit() +from scapy.all import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf,ARP + +def setup(): + + global conf + + parser = argparse.ArgumentParser() + parser.add_argument('-b', + dest='bssid', + type=utils.sanitize_bssid, + required=True, + help='Specify BSSID for injection') + + parser.add_argument('-i', + dest='in_iface', + default='wlan0', + type=str, + help='Specify iface for injection (default: wlan0)') + + parser.add_argument('-o', + dest='out_iface', + default='wlan0', + type=str, + help='Specify iface for injection (default: wlan0)') + + parser.add_argument('-s', + dest='smac', + type=utils.sanitize_smac, + help='Specify source MAC address for injected frames.') + + parser.add_argument('-w', + dest='wepkey', + type=str, + help='WEP mode and key') + + parser.add_argument('-k', + dest='keyid', + type=int, + choices=xrange(0,4), + default=0, + help='WEP key id (default: 0)') + + parser.add_argument('-d', + dest='debug', + action='store_true', + help='Activate debug mode.') + + parser.add_argument('-v', + dest='verb', + action='store_true', + help='Use verbose debugging.') + + args = parser.parse_args() + + # validate wepkey if WEP in use + if args.wepkey is not None: + wepkey = utils.parse_wep_key(args.wepkey, args.keyid) + if wepkey is None: + parser.print_usage() + print + print '[!] Invalid WEP key' + sys.exit() + + conf.iface = args.out_iface + conf.wepkey = args.wepkey + + return args + +def print_options(options): + + print "out_iface: %s" % options.out_iface + print "bssid: %s" % options.bssid + if options.smac is not None: + + print "smac: %s" % options.smac + + if options.wepkey is not None: + + print "WEP key: %s (%dbits)" % (options.wepkey, len(options.wepkey)*4) + + print 'DEBUG mode:', options.debug + +if __name__ == '__main__': + + options = setup() + + # Here we put a BPF filter so only 802.11 Data/to-DS frames are captured + s = conf.L2listen(iface = options.in_iface, + filter = "link[0]&0xc == 8 and link[1]&0xf == 1") + + # Speed optimization si Scapy does not have to parse payloads + ARP.payload_guess=[] + + try: + while True: + dot11_frame = s.recv(2346) + + # WEP handling is automagicly done by Scapy if conf.wepkey is set + # Nothing to do to decrypt (although not yet tested) + # WEP frames have Dot11WEP layer, others don't + if options.debug and options.verb: + if dot11_frame.haslayer(Dot11WEP): # WEP frame + os.write(1,"Received WEP from %s\n" % options.in_iface) + else: # Cleartext frame + os.write(1,"Received from %s\n" % options.in_iface) + os.write(1,"%s\n" % dot11_frame.summary()) + + if dot11_frame.getlayer(Dot11).addr1 != options.bssid: + continue + + # Identifying ARP Requests + if dot11_frame.haslayer(ARP) and dot11_frame.getlayer(ARP).op == 1: + + if options.debug: + + os.write(1,"Received ARP Request on %s\n" % options.in_iface) + + if optios.verb: + os.write(1,"%s\n" % dot11_frame.summary()) + + # Building ARP Reply answer for injection + dot11_answer = RadioTap()/Dot11( + type = "Data", + FCfield = "from-DS", + addr1 = dot11_frame.getlayer(Dot11).addr2, + addr2 = options.bssid) + dot11_answer.addr3 = options.smac + + if options.wepkey is not None: + dot11_answer.FCfield |= 0x40 + dot11_answer /= Dot11WEP( + iv = "111", + keyid = options.keyid) + + dot11_answer /= LLC(ctrl=3)/SNAP()/ARP( + op="is-at", + hwsrc=options.smac, + psrc=dot11_frame.getlayer(ARP).pdst, + hwdst=dot11_frame.getlayer(ARP).hwsrc, + pdst=dot11_frame.getlayer(ARP).psrc) + + dot11_answer /= dot11_frame.getlayer(ARP).payload + + if options.debug: + os.write(1,"Sending ARP Reply on %s\n" % optios.out_iface) + if options.verb: + os.write(1,"%s\n" % dot11_answer.summary()) + + # Frame injection : + sendp(dot11_answer,verbose=0) # Send frame + + # Program killed + except KeyboardInterrupt: + print "Stopped by user." + + s.close() + + sys.exit() diff --git a/wifidns.py b/wifidns.py index 4b00094..8344a78 100755 --- a/wifidns.py +++ b/wifidns.py @@ -17,237 +17,193 @@ # ######################################### -import os,sys,getopt,struct,re,string,logging - -# Import Psyco if available to speed up execution -try: - import psyco - psyco.full() -except ImportError: - print "Psyco optimizer not installed, running anyway..." - pass +import os +import sys +import argparse +import struct +import logging +import utils from socket import * from fcntl import ioctl from select import select logging.getLogger("scapy").setLevel(1) + #from scapy import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf from scapy.all import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf,RadioTap - -# We want to build a DNS Query answering machine - from scapy.all import IP,UDP,DNS,DNSRR -IN_IFACE = "wlan0" -OUT_IFACE = "wlan0" -HAS_SMAC = 0 -SMAC = "" -WEP = 0 -KEYID = 0 -DEBUG = 0 -VERB = 0 -TTL = 64 -BSSID = "" -IPDNS = "" -WEPKEY = "" - - -def usage(status=0): - print "Usage: wifidns -b -a [-o ] [-i ]" - print " [-s ] [-t ] [-w ]" - print " [-k ]] [-d [-v]] [-h]" - print " -b specify BSSID for injection" - print " -a specify IP address for DNS answers" - print " -t Set TTL (default: 64)" - print " -o specify interface for injection (default: wlan0)" - print " -i specify interface for listening (default: wlan0)" - print " -s specify source MAC address for injected frames" - print " -w WEP mode and key" - print " -k WEP key id (default: 0)" - print " -d activate debug" - print " -v verbose debugging" - print " -h this so helpful output" - sys.exit(status) - -opts = getopt.getopt(sys.argv[1:],"b:a:o:i:s:t:w:k:dvh") - -for opt,optarg in opts[0]: - if opt == "-b": - BSSID = optarg - elif opt == "-a": - IPDNS = optarg - elif opt == "-o": - OUT_IFACE = optarg - elif opt == "-i": - IN_IFACE = optarg - elif opt == "-s": - HAS_SMAC += 1 - SMAC = optarg - elif opt == "-w": - WEP += 1 - WEPKEY = optarg - elif opt == "-k": - KEYID = int(optarg) - elif opt == "-t": - TTL = int(optarg) - elif opt == "-d": - DEBUG += 1 - elif opt == "-v": - VERB += 1 - elif opt == "-h": - usage() - -if not BSSID: - print "\nError: BSSID not defined\n" - usage() - -if not IPDNS: - print "\nError: IP not defined\n" - usage() - -# Match and parse BSSID -if re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', BSSID): - BSSID = BSSID.lower() -else: - print "\nError: Wrong format for BSSID\n" - usage() - -if HAS_SMAC: - if not SMAC: - print "\nError: SMAC not defined\n" - usage() - # Match and parse SMAC - elif re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', SMAC): - SMAC = SMAC.lower() - else: - print "\nError: Wrong format for SMAC\n" - usage() - -# Match and parse IP -if not re.match('^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$', IPDNS): - print "\nError: Wrong IP address\n" - usage() - -print "IN_IFACE: %s" % IN_IFACE -print "OUT_IFACE: %s" % OUT_IFACE -print "BSSID: %s" % BSSID -if HAS_SMAC: - print "SMAC: %s" % SMAC -print "IP: %s" % IPDNS - -if WEP: - # Match and parse WEP key - tmp_key = "" - if re.match('^([0-9a-fA-F]{2}){5}$', WEPKEY) or re.match ('^([0-9a-fA-F]{2}){13}$', WEPKEY): - tmp_key = WEPKEY - elif re.match('^([0-9a-fA-F]{2}[:]){4}[0-9a-fA-F]{2}$', WEPKEY) or re.match('^([0-9a-fA-F]{2}[:]){12}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub(':', '', WEPKEY) - elif re.match ('^([0-9a-fA-F]{4}[-]){2}[0-9a-fA-F]{2}$', WEPKEY) or re.match ('^([0-9a-fA-F]{4}[-]){6}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub('-', '', WEPKEY) - else: - print "\nError : Wrong format for WEP key\n" - usage() - g = lambda x: chr(int(tmp_key[::2][x],16)*16+int(tmp_key[1::2][x],16)) - for i in range(len(tmp_key)/2): - conf.wepkey += g(i) - print "WEP key: %s (%dbits)" % (WEPKEY, len(tmp_key)*4) - if KEYID > 3 or KEYID < 0: - print "Key id: %s (defaulted to 0 due to wrong -k argument)" % KEYID - KEYID = 0 - else: - print "Key id: %s" % KEYID -else: - if KEYID != 0: - print "WEP not activated, key id ignored" - -print "TTL: %s" % TTL - -if not DEBUG: - if VERB: - print "DEBUG not activated, verbosity ignored" -else: - print "DEBUG activated" - if VERB: - print "Verbose debugging" - -conf.iface = OUT_IFACE - -# Here we put a BPF filter so only 802.11 Data/to-DS frames are captured -s = conf.L2listen(iface = IN_IFACE, - filter = "link[0]&0xc == 8 and link[1]&0xf == 1") - -# Speed optimization si Scapy does not have to parse payloads -DNS.payload_guess=[] - -try: - while 1: - dot11_frame = s.recv(2346) - - # WEP handling is automagicly done by Scapy if conf.wepkey is set - # Nothing to do to decrypt - # WEP frames have Dot11WEP layer, others don't - if DEBUG and VERB: - if dot11_frame.haslayer(Dot11WEP): # WEP frame - os.write(1,"Received WEP from %s\n" % IN_IFACE) - else: # Cleartext frame - os.write(1,"Received from %s\n" % IN_IFACE) - os.write(1,"%s\n" % dot11_frame.summary()) - - if dot11_frame.getlayer(Dot11).addr1 != BSSID: - continue - - # Identifying DNS Queries - if dot11_frame.haslayer(DNS) and dot11_frame.getlayer(DNS).qr == 0: - if DEBUG: - os.write(1,"Received DNS Query on %s\n" % IN_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_frame.summary()) - - # Building DNS Reply answer for injection - dot11_answer = RadioTap()/Dot11( - type = "Data", - FCfield = "from-DS", - addr1 = dot11_frame.getlayer(Dot11).addr2, - addr2 = BSSID) - if not HAS_SMAC: - dot11_answer.addr3 = dot11_frame.getlayer(Dot11).addr1 - else: - dot11_answer.addr3 = SMAC - if WEP: - dot11_answer.FCfield |= 0x40 - dot11_answer /= Dot11WEP( - iv = "111", - keyid = KEYID) - dot11_answer /= LLC(ctrl = 3)/SNAP()/IP( - src = dot11_frame.getlayer(IP).dst, - dst = dot11_frame.getlayer(IP).src, - ttl = TTL) - dot11_answer /= UDP( - sport = dot11_frame.getlayer(UDP).dport, - dport = dot11_frame.getlayer(UDP).sport) - dot11_answer /= DNS( - id = dot11_frame.getlayer(DNS).id, - qr = 1, - qd = dot11_frame.getlayer(DNS).qd, - an = DNSRR( - rrname = dot11_frame.getlayer(DNS).qd.qname, - ttl = 10, - rdata = IPDNS) - ) - - if DEBUG: - os.write(1,"Sending DNS Reply on %s\n" % OUT_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_frame.summary()) - - # Frame injection : - sendp(dot11_answer,verbose=0) # Send frame - -# Program killed -except KeyboardInterrupt: - print "Stopped by user." - -s.close() - -sys.exit() +def setup(): + + global conf + + parser = argparse.ArgumentParser() + + parser.add_argument('-b', + dest='bssid', + type=utils.sanitize_bssid, + required=True, + help='Specify BSSID for injection') + + parser.add_argument('-i', + dest='in_iface', + default='wlan0', + type=str, + help='Specify iface for injection (default: wlan0)') + + parser.add_argument('-o', + dest='out_iface', + default='wlan0', + type=str, + help='Specify iface for injection (default: wlan0)') + + parser.add_argument('-a', + dest='ipdns', + required=True, + type=utils.sanitize_ipdns, + help="Specify IP address for DNS answers.") + + parser.add_argument('-s', + dest='smac', + type=utils.sanitize_smac, + help='Specify source MAC address for injected frames.') + + parser.add_argument('-w', + dest='wepkey', + type=str, + help='WEP mode and key') + + parser.add_argument('-k', + dest='keyid', + type=int, + choices=xrange(0,4), + default=0, + help='WEP key id (default: 0)') + + parser.add_argument('-d', + dest='debug', + action='store_true', + help='Activate debug mode.') + + parser.add_argument('-v', + dest='verbe', + action='store_true', + help='Use verbose debugging.') + + parser.add_argument('-t', + dest='ttl', + type=int, + default=64, + help='Set TTL (default: 64)') + + args = parser.parse_args() + + # validate wepkey if WEP in use + if args.wepkey is not None: + wepkey = utils.parse_wep_key(args.wepkey, args.keyid) + if wepkey is None: + parser.print_usage() + print + print '[!] Invalid WEP key' + sys.exit() + + conf.iface = args.out_iface + conf.wepkey = args.wepkey + + return args + +def print_options(options): + + print "out_iface: %s" % options.out_iface + print "bssid: %s" % options.bssid + print "ttl: %s" % options.ttl + print "ipdns: %s" % options.ipdns + if options.smac is not None: + + print "smac: %s" % options.smac + + if options.wepkey is not None: + + print "WEP key: %s (%dbits)" % (options.wepkey, len(options.wepkey)*4) + + print 'DEBUG mode:', options.debug + +if __name__ == '__main__': + + options = setup() + + # Here we put a BPF filter so only 802.11 Data/to-DS frames are captured + s = conf.L2listen(iface=options.in_iface, + filter="link[0]&0xc == 8 and link[1]&0xf == 1") + + # Speed optimization si Scapy does not have to parse payloads + DNS.payload_guess=[] + + try: + while True: + dot11_frame = s.recv(2346) + + # WEP handling is automagicly done by Scapy if conf.wepkey is set + # Nothing to do to decrypt + # WEP frames have Dot11WEP layer, others don't + if options.debug and options.verb: + if dot11_frame.haslayer(Dot11WEP): # WEP frame + os.write(1,"Received WEP from %s\n" % options.in_iface) + else: # Cleartext frame + os.write(1,"Received from %s\n" % options.in_iface) + os.write(1,"%s\n" % dot11_frame.summary()) + + if dot11_frame.getlayer(Dot11).addr1 != options.bssid: + continue + + # Identifying DNS Queries + if dot11_frame.haslayer(DNS) and dot11_frame.getlayer(DNS).qr == 0: + if options.debug: + os.write(1,"Received DNS Query on %s\n" % options.in_iface) + if options.verb: + os.write(1,"%s\n" % dot11_frame.summary()) + + # Building DNS Reply answer for injection + dot11_answer = RadioTap()/Dot11(type="Data", + FCfield="from-DS", + addr1=dot11_frame.getlayer(Dot11).addr2, + addr2=options.bssid) + + if options.smac is not None: + dot11_answer.addr3 = dot11_frame.getlayer(Dot11).addr1 + else: + dot11_answer.addr3 = options.smac + + if options.wepkey is not None: + dot11_answer.FCfield |= 0x40 + dot11_answer /= Dot11WEP(iv="111", keyid=options.keyid) + + dot11_answer /= LLC(ctrl = 3)/SNAP()/IP(src=dot11_frame.getlayer(IP).dst, + dst=dot11_frame.getlayer(IP).src, + ttl=options.ttl) + + dot11_answer /= UDP(sport=dot11_frame.getlayer(UDP).dport, + dport=dot11_frame.getlayer(UDP).sport) + + dot11_answer /= DNS(id=dot11_frame.getlayer(DNS).id, + qr=1, + qd=dot11_frame.getlayer(DNS).qd, + an=DNSRR(rrname=dot11_frame.getlayer(DNS).qd.qname, + ttl=10, + rdata=options.ipdns)) + + if options.debug: + os.write(1,"Sending DNS Reply on %s\n" % options.out_iface) + if options.verb: + os.write(1,"%s\n" % dot11_frame.summary()) + + # Frame injection : + sendp(dot11_answer,verbose=0) # Send frame + + # Program killed + except KeyboardInterrupt: + print "\nStopped by user." + + s.close() + sys.exit() diff --git a/wifiping.py b/wifiping.py index 87c0ee8..b6b2828 100755 --- a/wifiping.py +++ b/wifiping.py @@ -17,215 +17,186 @@ # ######################################### -import os,sys,getopt,struct,re,string, logging - -# Import Psyco if available to speed up execution -try: - import psyco - psyco.full() -except ImportError: - print "Psyco optimizer not installed, running anyway..." - pass +import os +import sys +import getopt +import struct +import logging +import utils +import argparse from socket import * from fcntl import ioctl from select import select logging.getLogger("scapy").setLevel(1) -from scapy.all import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf - -# We want to build an ICMP Echo Request answering machine - -from scapy import IP,ICMP - -IN_IFACE = "wlan0" -OUT_IFACE = "wlan0" -HAS_SMAC = 0 -SMAC = "" -WEP = 0 -KEYID = 0 -DEBUG = 0 -VERB = 0 -TTL = 64 -BSSID = "" -WEPKEY = "" - - -def usage(status=0): - print "Usage: wifitap -b [-t ] [-o ] [-i ]" - print " [-s ] [-w [-k ]]" - print " [-d [-v]] [-h]" - print " -b specify BSSID for injection" - print " -t Set TTL (default: 64)" - print " -o specify interface for injection (default: wlan0)" - print " -i specify interface for listening (default: wlan0)" - print " -s specify source MAC address for injected frames" - print " -w WEP mode and key" - print " -k WEP key id (default: 0)" - print " -d activate debug" - print " -v verbose debugging" - print " -h this so helpful output" - sys.exit(status) - -opts = getopt.getopt(sys.argv[1:],"b:o:i:s:w:k:t:dvh") - -for opt,optarg in opts[0]: - if opt == "-b": - BSSID = optarg - elif opt == "-o": - OUT_IFACE = optarg - elif opt == "-i": - IN_IFACE = optarg - elif opt == "-s": - HAS_SMAC += 1 - SMAC = optarg - elif opt == "-w": - WEP += 1 - WEPKEY = optarg - elif opt == "-k": - KEYID = int(optarg) - elif opt == "-t": - TTL = int(optarg) - elif opt == "-d": - DEBUG += 1 - elif opt == "-v": - VERB += 1 - elif opt == "-h": - usage() - -if not BSSID: - print "\nError: BSSID not defined\n" - usage() - -# Match and parse BSSID -if re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', BSSID): - BSSID = BSSID.lower() -else: - print "\nError: Wrong format for BSSID\n" - usage () - -if HAS_SMAC: - if not SMAC: - print "\nError: SMAC not defined\n" - usage() - # Match and parse SMAC - elif re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', SMAC): - SMAC = SMAC.lower() - else: - print "\nError: Wrong format for SMAC\n" - usage() - -print "IN_IFACE: %s" % IN_IFACE -print "OUT_IFACE: %s" % OUT_IFACE -print "BSSID: %s" % BSSID -if HAS_SMAC: - print "SMAC: %s" % SMAC - -if WEP: - # Match and parse WEP key - tmp_key = "" - if re.match('^([0-9a-fA-F]{2}){5}$', WEPKEY) or re.match ('^([0-9a-fA-F]{2}){13}$', WEPKEY): - tmp_key = WEPKEY - elif re.match('^([0-9a-fA-F]{2}[:]){4}[0-9a-fA-F]{2}$', WEPKEY) or re.match('^([0-9a-fA-F]{2}[:]){12}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub(':', '', WEPKEY) - elif re.match ('^([0-9a-fA-F]{4}[-]){2}[0-9a-fA-F]{2}$', WEPKEY) or re.match ('^([0-9a-fA-F]{4}[-]){6}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub('-', '', WEPKEY) - else: - print "\nError : Wrong format for WEP key\n" - usage() - g = lambda x: chr(int(tmp_key[::2][x],16)*16+int(tmp_key[1::2][x],16)) - for i in range(len(tmp_key)/2): - conf.wepkey += g(i) - print "WEP key: %s (%dbits)" % (WEPKEY, len(tmp_key)*4) - if KEYID > 3 or KEYID < 0: - print "Key id: %s (defaulted to 0 due to wrong -k argument)" % KEYID - KEYID = 0 - else: - print "Key id: %s" % KEYID -else: - if KEYID != 0: - print "WEP not activated, key id ignored" - -print "TTL: %s" % TTL - -if not DEBUG: - if VERB: - print "DEBUG not activated, verbosity ignored" -else: - print "DEBUG activated" - if VERB: - print "Verbose debugging" - -conf.iface = OUT_IFACE - -# Here we put a BPF filter so only 802.11 Data/to-DS frames are captured -s = conf.L2listen(iface = IN_IFACE, - filter = "link[0]&0xc == 8 and link[1]&0xf == 1") - -# Speed optimization si Scapy does not have to parse payloads -ICMP.payload_guess=[] - -try: - while 1: - dot11_frame = s.recv(2346) - - # WEP handling is automagicly done by Scapy if conf.wepkey is set - # Nothing to do to decrypt (although not yet tested) - # WEP frames have Dot11WEP layer, others don't - if DEBUG and VERB: - if dot11_frame.haslayer(Dot11WEP): # WEP frame - os.write(1,"Received WEP from %s\n" % IN_IFACE) - else: # Cleartext frame - os.write(1,"Received from %s\n" % IN_IFACE) - # os.write(1,"%s\n" % dot11_frame.summary()) - - if dot11_frame.getlayer(Dot11).addr1 != BSSID: - continue - - # Identifying ICMP Echo Requests - if dot11_frame.haslayer(ICMP) and dot11_frame.getlayer(ICMP).type == 8: - if DEBUG: - os.write(1,"Received ICMP Echo Request on %s\n" % IN_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_frame.summary()) - - # Building ICMP Echo Reply answer for injection - dot11_answer = RadioTap()/Dot11( - type = "Data", - FCfield = "from-DS", - addr1 = dot11_frame.getlayer(Dot11).addr2, - addr2 = BSSID) - if not HAS_SMAC: - dot11_answer.addr3 = dot11_frame.getlayer(Dot11).addr1 - else: - dot11_answer.addr3 = SMAC - if WEP: - dot11_answer.FCfield |= 0x40 - dot11_answer /= Dot11WEP( - iv = "111", - keyid = KEYID) - dot11_answer /= LLC(ctrl=3)/SNAP()/IP( - src = dot11_frame.getlayer(IP).dst, - dst = dot11_frame.getlayer(IP).src, - ttl = TTL) - dot11_answer /= ICMP( - type = "echo-reply", - id = dot11_frame.getlayer(ICMP).id, - seq = dot11_frame.getlayer(ICMP).seq) - dot11_answer /= dot11_frame.getlayer(ICMP).payload - - if DEBUG: - os.write(1,"Sending ICMP Echo Reply on %s\n" % OUT_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_answer.summary()) - - # Frame injection : - sendp(dot11_answer,verbose=0) # Send frame - -# Program killed -except KeyboardInterrupt: - print "Stopped by user." - -s.close() - -sys.exit() +from scapy.all import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf,IP,ICMP + +def setup(): + + global conf + + parser = argparse.ArgumentParser() + + parser.add_argument('-b', + dest='bssid', + type=utils.sanitize_bssid, + required=True, + help='Specify BSSID for injection') + + parser.add_argument('-i', + dest='in_iface', + default='wlan0', + type=str, + help='Specify iface for injection (default: wlan0)') + + parser.add_argument('-o', + dest='out_iface', + default='wlan0', + type=str, + help='Specify iface for injection (default: wlan0)') + + parser.add_argument('-s', + dest='smac', + type=utils.sanitize_smac, + help='Specify source MAC address for injected frames.') + + parser.add_argument('-w', + dest='wepkey', + type=str, + help='WEP mode and key') + + parser.add_argument('-k', + dest='keyid', + type=int, + choices=xrange(0,4), + default=0, + help='WEP key id (default: 0)') + + parser.add_argument('-d', + dest='debug', + action='store_true', + help='Activate debug mode.') + + parser.add_argument('-v', + dest='verb', + action='store_true', + help='Use verbose debugging.') + + parser.add_argument('-t', + dest='ttl', + type=int, + default=64, + help='Set TTL (default: 64)') + + args = parser.parse_args() + + # validate wepkey if WEP in use + if args.wepkey is not None: + wepkey = utils.parse_wep_key(args.wepkey, args.keyid) + if wepkey is None: + parser.print_usage() + print + print '[!] Invalid WEP key' + sys.exit() + + conf.iface = args.out_iface + conf.wepkey = args.wepkey + + return args + +def print_options(options): + + print "out_iface: %s" % options.out_iface + print "bssid: %s" % options.bssid + print "ttl: %s" % options.ttl + if options.smac is not None: + + print "smac: %s" % options.smac + + if options.wepkey is not None: + + print "WEP key: %s (%dbits)" % (options.wepkey, len(options.wepkey)*4) + + print 'DEBUG mode:', options.debug + +if __name__ == '__main__': + + options = setup() + + # Here we put a BPF filter so only 802.11 Data/to-DS frames are captured + s = conf.L2listen(iface = options.in_iface, + filter="link[0]&0xc == 8 and link[1]&0xf == 1") + + # Speed optimization si Scapy does not have to parse payloads + ICMP.payload_guess=[] + + try: + + while True: + dot11_frame = s.recv(2346) + + # WEP handling is automagicly done by Scapy if conf.wepkey is set + # Nothing to do to decrypt (although not yet tested) + # WEP frames have Dot11WEP layer, others don't + if options.debug and options.verb: + if dot11_frame.haslayer(Dot11WEP): # WEP frame + os.write(1,"Received WEP from %s\n" % options.in_iface) + else: # Cleartext frame + os.write(1,"Received from %s\n" % options.in_iface) + #os.write(1,"%s\n" % dot11_frame.summary()) + + if dot11_frame.getlayer(Dot11).addr1 != options.bssid: + continue + + # Identifying ICMP Echo Requests + if dot11_frame.haslayer(ICMP) and dot11_frame.getlayer(ICMP).type == 8: + + if options.debug: + os.write(1,"Received ICMP Echo Request on %s\n" % options.in_iface) + if options.verb: + os.write(1,"%s\n" % dot11_frame.summary()) + + # Building ICMP Echo Reply answer for injection + dot11_answer = RadioTap()/Dot11( + type = "Data", + FCfield = "from-DS", + addr1 = dot11_frame.getlayer(Dot11).addr2, + addr2 = options.bssid) + + if options.smac is None: + dot11_answer.addr3 = dot11_frame.getlayer(Dot11).addr1 + else: + dot11_answer.addr3 = options.smac + + if options.wepkey is not None: + + dot11_answer.FCfield |= 0x40 + + dot11_answer /= Dot11WEP(iv="111", + keyid=options.keyid) + + dot11_answer /= LLC(ctrl=3)/SNAP()/IP(src=dot11_frame.getlayer(IP).dst, + dst=dot11_frame.getlayer(IP).src, + ttl=options.ttl) + + dot11_answer /= ICMP(type="echo-reply", + id=dot11_frame.getlayer(ICMP).id, + seq=dot11_frame.getlayer(ICMP).seq) + + dot11_answer /= dot11_frame.getlayer(ICMP).payload + + if options.debug: + os.write(1,"Sending ICMP Echo Reply on %s\n" % options.out_iface) + if options.verb: + os.write(1,"%s\n" % dot11_answer.summary()) + + # Frame injection : + sendp(dot11_answer,verbose=0) # Send frame + + # Program killed + except KeyboardInterrupt: + print "Stopped by user." + + s.close() + sys.exit() diff --git a/wifitap.py b/wifitap.py index d0935c6..79a1d2a 100755 --- a/wifitap.py +++ b/wifitap.py @@ -17,14 +17,12 @@ # ######################################### -import os,sys,getopt,struct,re,string,logging - -# Import Psyco if available to speed up execution -try: - import psyco - psyco.full() -except ImportError: - print "Psyco optimizer not installed, running anyway..." +import os +import sys +import struct +import logging +import argparse +import utils from socket import * from fcntl import ioctl @@ -33,237 +31,209 @@ logging.getLogger("scapy").setLevel(1) #from scapy import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf from scapy.all import Raw,Ether,PrismHeader,Dot11,Dot11WEP,LLC,SNAP,sendp,conf,RadioTap + TUNSETIFF = 0x400454ca IFF_TAP = 0x0002 TUNMODE = IFF_TAP -IN_IFACE = "wlan0" -OUT_IFACE = "wlan0" -HAS_SMAC = 0 -SMAC = "" -WEP = 0 -KEYID = 0 -DEBUG = 0 -VERB = 0 -BSSID = "" -WEPKEY = "" - - -def usage(status=0): - print "Usage: wifitap -b [-o ] [-i ] [-s ]" - print " [-w [-k ]] [-d [-v]] [-h]" - print " -b specify BSSID for injection" - print " -o specify interface for injection (default: wlan0)" - print " -i specify interface for listening (default: wlan0)" - print " -s specify source MAC address for injected frames" - print " -w WEP mode and key" - print " -k WEP key id (default: 0)" - print " -d activate debug" - print " -v verbose debugging" - print " -h this so helpful output" - sys.exit(status) - -opts = getopt.getopt(sys.argv[1:],"b:o:i:s:w:k:dvh") - -for opt,optarg in opts[0]: - if opt == "-b": - BSSID = optarg - elif opt == "-o": - OUT_IFACE = optarg - elif opt == "-i": - IN_IFACE = optarg - elif opt == "-s": - HAS_SMAC += 1 - SMAC = optarg - elif opt == "-w": - WEP += 1 - WEPKEY = optarg - elif opt == "-k": - KEYID = int(optarg) - elif opt == "-d": - DEBUG += 1 - elif opt == "-v": - VERB += 1 - elif opt == "-h": - usage() - -if not BSSID: - print "\nError: BSSID not defined\n" - usage() - -# Match and parse BSSID -if re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', BSSID): - BSSID = BSSID.lower() -else: - print "\nError: Wrong format for BSSID\n" - usage () - -# Support for source MAC spoofing for adhoc support -if HAS_SMAC: - if not SMAC: - print "\nError: SMAC not defined\n" - usage() - # Match and parse SMAC - elif re.match('^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', SMAC): - SMAC = SMAC.lower() - else: - print "\nError: Wrong format for SMAC\n" - usage() - -print "IN_IFACE: %s" % IN_IFACE -print "OUT_IFACE: %s" % OUT_IFACE -print "BSSID: %s" % BSSID -if HAS_SMAC: - print "SMAC: %s" % SMAC - -if WEP: - # Match and parse WEP key - tmp_key = "" - if re.match('^([0-9a-fA-F]{2}){5}$', WEPKEY) or re.match ('^([0-9a-fA-F]{2}){13}$', WEPKEY): - tmp_key = WEPKEY - elif re.match('^([0-9a-fA-F]{2}[:]){4}[0-9a-fA-F]{2}$', WEPKEY) or re.match('^([0-9a-fA-F]{2}[:]){12}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub(':', '', WEPKEY) - elif re.match ('^([0-9a-fA-F]{4}[-]){2}[0-9a-fA-F]{2}$', WEPKEY) or re.match ('^([0-9a-fA-F]{4}[-]){6}[0-9a-fA-F]{2}$', WEPKEY): - tmp_key = re.sub('-', '', WEPKEY) - else: - print "\nError : Wrong format for WEP key\n" - usage() - g = lambda x: chr(int(tmp_key[::2][x],16)*16+int(tmp_key[1::2][x],16)) - for i in range(len(tmp_key)/2): - conf.wepkey += g(i) - print "WEP key: %s (%dbits)" % (WEPKEY, len(tmp_key)*4) - if KEYID > 3 or KEYID < 0: - print "Key id: %s (defaulted to 0 due to wrong -k argument)" % KEYID - KEYID = 0 - else: - print "Key id: %s" % KEYID -else: - if KEYID != 0: - print "WEP not activated, key id ignored" - -if not DEBUG: - if VERB: - print "DEBUG not activated, verbosity ignored" -else: - print "DEBUG activated" - if VERB: - print "Verbose debugging" - -conf.iface = OUT_IFACE - -# Here we put a BPF filter so only 802.11 Data/to-DS frames are captured -s = conf.L2listen(iface = IN_IFACE, - filter = "link[0]&0xc == 8 and link[1]&0xf == 1") -#s = conf.L2listen(iface = IN_IFACE) - -# Open /dev/net/tun in TAP (ether) mode -f = os.open("/dev/net/tun", os.O_RDWR) -ifs = ioctl(f, TUNSETIFF, struct.pack("16sH", "wj%d", TUNMODE)) -ifname = ifs[:16].strip("\x00") -print "Interface %s created. Configure it and use it" % ifname - -# Speed optimization si Scapy does not have to parse payloads -Ether.payload_guess=[] -SNAP.payload_guess=[] - -try: - while 1: - r = select([f,s],[],[])[0] - - # frame from /dev/net/tun - if f in r: - - # tuntap frame max. size is 1522 (ethernet, see RFC3580) + 4 - buf = os.read(f,1526) - eth_rcvd_frame=Ether(buf[4:]) - - if DEBUG: - os.write(1,"Received from %s\n" % ifname) - if VERB: - os.write(1,"%s\n" % eth_rcvd_frame.summary()) - - # Prepare Dot11 frame for injection - dot11_sent_frame = RadioTap()/Dot11( - type = "Data", - FCfield = "from-DS", - addr1 = eth_rcvd_frame.getlayer(Ether).dst, - addr2 = BSSID) - # It doesn't seem possible to set tuntap interface MAC address - # when we create it, so we set source MAC here - if not HAS_SMAC: - dot11_sent_frame.addr3 = eth_rcvd_frame.getlayer(Ether).src - else: - dot11_sent_frame.addr3 = SMAC - if WEP: - dot11_sent_frame.FCfield |= 0x40 - dot11_sent_frame /= Dot11WEP( - iv = "111", - keyid = KEYID) - dot11_sent_frame /= LLC(ctrl = 3)/SNAP(code=eth_rcvd_frame.getlayer(Ether).type)/eth_rcvd_frame.getlayer(Ether).payload - - if DEBUG: - os.write(1,"Sending from-DS to %s\n" % OUT_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_sent_frame.summary()) - - # Frame injection : - sendp(dot11_sent_frame,verbose=0) # Send from-DS frame - - # Frame from WiFi network - if s in r: - - # 802.11 maximum frame size is 2346 bytes (cf. RFC3580) - # However, WiFi interfaces are always MTUed to 1500 - dot11_rcvd_frame = s.recv(2346) - - # WEP handling is automagicly done by Scapy if conf.wepkey is set - # Nothing to do to decrypt (although not yet tested) - # WEP frames have Dot11WEP layer, others don't - - if DEBUG: - if dot11_rcvd_frame.haslayer(Dot11WEP): # WEP frame - os.write(1,"Received WEP from %s\n" % IN_IFACE) - else: # Cleartext frame - os.write(1,"Received from %s\n" % IN_IFACE) - if VERB: - os.write(1,"%s\n" % dot11_rcvd_frame.summary()) - - # if dot11_frame.getlayer(Dot11).FCfield & 1: # Frame is to-DS - # For now, we only take care of to-DS frames... - - if dot11_rcvd_frame.getlayer(Dot11).addr1 != BSSID: - if VERB: - os.write(1,"Frame not to/from BSSID\n") - continue - - # One day, we'll try to take care of AP to DS trafic (cf. TODO) - # else: # Frame is from-DS - # if dot11_frame.getlayer(Dot11).addr2 != BSSID: - # continue - # eth_frame = Ether(dst=dot11_frame.getlayer(Dot11).addr1, - # src=dot11_frame.getlayer(Dot11).addr3) - - if dot11_rcvd_frame.haslayer(SNAP): - eth_sent_frame = Ether( - dst=dot11_rcvd_frame.getlayer(Dot11).addr3, - src=dot11_rcvd_frame.getlayer(Dot11).addr2, - type=dot11_rcvd_frame.getlayer(SNAP).code) - eth_sent_frame.payload = dot11_rcvd_frame.getlayer(SNAP).payload - - if DEBUG: - os.write(1, "Sending to %s\n" % ifname) - if VERB: - os.write(1, "%s\n" % eth_sent_frame.summary()) - - # Add Tun/Tap header to frame, convert to string and send - buf = "\x00\x00" + struct.pack("!H",eth_sent_frame.type) + str(eth_sent_frame) - os.write(f, buf) - -# Program killed -except KeyboardInterrupt: - print "Stopped by user." - -s.close() -os.close(f) - -sys.exit() +def setup(): + + global conf + + parser = argparse.ArgumentParser() + parser.add_argument('-b', + dest='bssid', + type=utils.sanitize_bssid, + required=True, + help='Specify BSSID for injection') + + parser.add_argument('-o', + dest='out_iface', + default='wlan0', + type=str, + help='Specify iface for injection (default: wlan0)') + + parser.add_argument('-i', + dest='in_iface', + default='wlan0', + type=str, + help='Specify iface for listening (default: wlan0)') + + parser.add_argument('-s', + dest='smac', + type=utils.sanitize_smac, + help='Specify source MAC address for injected frames.') + + parser.add_argument('-w', + dest='wepkey', + type=str, + help='WEP mode and key') + + parser.add_argument('-k', + dest='keyid', + type=int, + choices=xrange(0,4), + default=0, + help='WEP key id (default: 0)') + + parser.add_argument('-d', + dest='debug', + action='store_true', + help='Activate debug mode.') + + parser.add_argument('-v', + dest='verb', + action='store_true', + help='Use verbose debugging.') + + args = parser.parse_args() + + # validate wepkey if WEP in use + if args.wepkey is not None: + wepkey = utils.parse_wep_key(args.wepkey, args.keyid) + if wepkey is None: + parser.print_usage() + print + print '[!] Invalid WEP key' + sys.exit() + + conf.iface = args.out_iface + conf.wepkey = args.wepkey + + return args + +def print_options(options): + + print "in_iface: %s" % options.in_iface + print "out_iface: %s" % options.out_iface + print "bssid: %s" % options.bssid + if options.smac is not None: + + print "smac: %s" % options.smac + + if options.wepkey is not None: + + print "WEP key: %s (%dbits)" % (options.wepkey, len(options.wepkey)*4) + + print 'DEBUG mode:', options.debug + +if __name__ == '__main__': + + options = setup() + print_options(options) + + # Here we put a BPF filter so only 802.11 Data/to-DS frames are captured + s = conf.L2listen(iface = options.in_iface, + filter = "link[0]&0xc == 8 and link[1]&0xf == 1") + #s = conf.L2listen(iface = options.in_iface) + + # Open /dev/net/tun in TAP (ether) mode + f = os.open("/dev/net/tun", os.O_RDWR) + ifs = ioctl(f, TUNSETIFF, struct.pack("16sH", "wj%d", TUNMODE)) + ifname = ifs[:16].strip("\x00") + print "Interface %s created. Configure it and use it" % ifname + + # Speed optimization si Scapy does not have to parse payloads + Ether.payload_guess=[] + SNAP.payload_guess=[] + + try: + + while True: + + r = select([f,s],[],[])[0] + + # frame from /dev/net/tun + if f in r: + + # tuntap frame max. size is 1522 (ethernet, see RFC3580) + 4 + buf = os.read(f,1526) + eth_rcvd_frame=Ether(buf[4:]) + + if options.debug: + os.write(1,"Received from %s\n" % ifname) + if options.verb: + os.write(1,"%s\n" % eth_rcvd_frame.summary()) + + # Prepare Dot11 frame for injection + dot11_sent_frame = RadioTap()/Dot11( + type = "Data", + FCfield = "from-DS", + addr1 = eth_rcvd_frame.getlayer(Ether).dst, + addr2 = options.bssid) + # It doesn't seem possible to set tuntap interface MAC address + # when we create it, so we set source MAC here + if options.smac is None: + dot11_sent_frame.addr3 = eth_rcvd_frame.getlayer(Ether).src + else: + dot11_sent_frame.addr3 = options.smac + if options.wepkey is not None: + dot11_sent_frame.FCfield |= 0x40 + dot11_sent_frame /= Dot11WEP(iv = "111", keyid = options.keyid) + + dot11_sent_frame /= LLC(ctrl = 3)/SNAP(code=eth_rcvd_frame.getlayer(Ether).type)/eth_rcvd_frame.getlayer(Ether).payload + + if options.debug: + os.write(1,"Sending from-DS to %s\n" % options.out_iface) + if options.verb: + os.write(1,"%s\n" % dot11_sent_frame.summary()) + + # Frame injection : + sendp(dot11_sent_frame,verbose=0) # Send from-DS frame + + # Frame from WiFi network + if s in r: + + # 802.11 maximum frame size is 2346 bytes (cf. RFC3580) + # However, WiFi interfaces are always MTUed to 1500 + dot11_rcvd_frame = s.recv(2346) + + # WEP handling is automagicly done by Scapy if conf.wepkey is set + # Nothing to do to decrypt (although not yet tested) + # WEP frames have Dot11WEP layer, others don't + + if options.debug: + if dot11_rcvd_frame.haslayer(Dot11WEP): # WEP frame + os.write(1,"Received WEP from %s\n" % options.in_iface) + else: # Cleartext frame + os.write(1,"Received from %s\n" % options.in_iface) + if options.verb: + os.write(1,"%s\n" % dot11_rcvd_frame.summary()) + + # if dot11_frame.getlayer(Dot11).FCfield & 1: # Frame is to-DS + # For now, we only take care of to-DS frames... + if dot11_rcvd_frame.getlayer(Dot11).addr1 != options.bssid: + if options.verb: + os.write(1,"Frame not to/from BSSID\n") + continue + + # One day, we'll try to take care of AP to DS trafic (cf. TODO) + # else: # Frame is from-DS + # if dot11_frame.getlayer(Dot11).addr2 != options.bssid: + # continue + # eth_frame = Ether(dst=dot11_frame.getlayer(Dot11).addr1, + # src=dot11_frame.getlayer(Dot11).addr3) + + if dot11_rcvd_frame.haslayer(SNAP): + eth_sent_frame = Ether(dst=dot11_rcvd_frame.getlayer(Dot11).addr3, + src=dot11_rcvd_frame.getlayer(Dot11).addr2, + type=dot11_rcvd_frame.getlayer(SNAP).code) + eth_sent_frame.payload = dot11_rcvd_frame.getlayer(SNAP).payload + + if options.debug: + os.write(1, "Sending to %s\n" % ifname) + if options.verb: + os.write(1, "%s\n" % eth_sent_frame.summary()) + + # Add Tun/Tap header to frame, convert to string and send + buf = "\x00\x00" + struct.pack("!H",eth_sent_frame.type) + str(eth_sent_frame) + os.write(f, buf) + + # Program killed + except KeyboardInterrupt: + print "\nStopped by user." + + s.close() + os.close(f) + + sys.exit()