def remove_cipher_suite(self, cipher_suite): """ Remove a cipher suite from the list. """ try: self._cipher_suites_keys.remove(cipher_suite) except AttributeError as missing_key: g_exception_logger.exception(missing_key)
def remove_monitor_interface(monitor_iface_name): """ Remove the virtual monitor interface that should have been added at startup. """ index = get_interface_index(monitor_iface_name) if index: try: with IW() as iw: iw.del_interface(index) except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err)
def _associate(self): """ Send an Association request. """ err_occurred = False # Prepare the RSN IEs information_elements = bytes() if self._rsn: information_elements = bytes(self._rsn) if self._ms_wpa_ie: information_elements += bytes(self._ms_wpa_ie) try: with IW() as iw: # pylint: disable-msg=C0103 g_default_logger.info("Trying to associate ...") iw.associate( self._iface_index, self._bssid, self._ssid, self._frequency, info_elements=information_elements ) except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) err_occurred = True # At this point, the association process probably failed while err_occurred: time.sleep(0.05) try: with IW() as iw: # pylint: disable-msg=C0103 g_default_logger.info("Trying to associate ...") iw.associate( self._iface_index, self._bssid, self._ssid, self._frequency, info_elements=information_elements ) err_occurred = False except NetlinkError as netlink_err_: g_exception_logger.exception(netlink_err_) err_occurred = True return err_occurred
def get_interface_index(iface_name): """ Return the interface index. """ interface_index = None try: with IPRoute() as ip: data = ip.link_lookup(ifname=iface_name) if data: interface_index = data[0] except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) return interface_index
def get_wireless_interface_mac_addr(interface_index): """ Return the MAC address of a wireless interface. """ mac_address = None try: with IW() as iw: data = iw.get_interface_by_ifindex(interface_index) if data: mac_address = data[0].get_attr("NL80211_ATTR_MAC") except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) return mac_address
def _authenticate(self): """ Send an Authentication frame. """ err_occurred = False try: with IW() as iw: # pylint: disable-msg=C0103 iw.authenticate(self._iface_index, self._bssid, self._ssid, self._frequency) except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) err_occurred = True return err_occurred
def _scan(self): """ Perform a scan and retrieve information related to the target SSID. """ err_occurred = False bss_status_attr = None found = False rsn_ie_data = None vendor_ie_data = None try: with IW() as iw: # pylint: disable-msg=C0103 scan_results = iw.scan( self._iface_index, [self._ssid], flush_cache=True ) bss_attr = None for result in scan_results: bss_attr = result.get_attr('NL80211_ATTR_BSS') if bss_attr is not None: attrs = bss_attr["attrs"] bss_ie = [x[1] for x in attrs if x[0] == \ "NL80211_BSS_INFORMATION_ELEMENTS"] if bss_ie: # Get SSID ssid = bss_ie[0].get("SSID", None) if ssid.decode("ascii") == self._ssid: # Get RSN IEs rsn_ie_data = bss_ie[0].get("RSN", None) vendor_ie_data = bss_ie[0].get("VENDOR", None) found = True break except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) err_occurred = True if found: self._bssid = bss_attr.get_attr("NL80211_BSS_BSSID") self._frequency = bss_attr.get_attr("NL80211_BSS_FREQUENCY") from_presp = bss_attr.get_attr('NL80211_BSS_PRESP_DATA') self._ssid_info_learned_in_presp = True if from_presp else False bss_status_attr = bss_attr.get_attr("NL80211_BSS_STATUS") return err_occurred, bss_status_attr, rsn_ie_data, vendor_ie_data
def _deauthenticate(self): """ Send a Deauthentication frame. """ err_occurred = False try: with IW() as iw: # pylint: disable-msg=C0103 iw.deauthenticate(self._iface_index, self._bssid, reason_code=0x01) except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) err_occurred = True return err_occurred
def _disassociate(self): """ Send a Disassociation request. """ err_occurred = False try: with IW() as iw: # pylint: disable-msg=C0103 iw.disassociate(self._iface_index, self._bssid, reason_code=0x08) except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) err_occurred = True return err_occurred
def get_mac_addr(interface_name): """ Returns the MAC address of a given network interface. """ mac_addr = None try: with IPDB() as ipdb: with ipdb.interfaces[interface_name] as interface: mac_addr = interface["address"] except KeyError as key_err: g_exception_logger.exception(key_err) except Exception as exception: g_exception_logger.exception(exception) return mac_addr
def set_interface_operstate(iface_index, linkmode, operstate): """ Set interface operational state. https://www.kernel.org/doc/Documentation/networking/operstates.txt """ done = True try: with RawIPRoute() as rawip: rawip.link('set', index=iface_index, IFLA_LINKMODE=linkmode, IFLA_OPERSTATE=operstate) except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) done = False return done
def network_interface_status(interface_name): """ Returns True if the network interface "interface_name" is up, False otherwise. """ interface_up = False try: with IPDB() as ipdb: with ipdb.interfaces[interface_name] as interface: interface_up = interface["operstate"] except KeyError as key_err: g_exception_logger.exception(key_err) except Exception as exception: g_exception_logger.exception(exception) return interface_up
def get_interface_operstate(iface_name): """ Set interface operational state. https://www.kernel.org/doc/Documentation/networking/operstates.txt """ operstate = None try: with IPDB() as ipdb: if iface_name in ipdb.interfaces.keys(): with ipdb.interfaces[iface_name] as interface: operstate = interface["operstate"] except KeyError as key_err: g_exception_logger.exception(key_err) except Exception as exception: g_exception_logger.exception(exception) return operstate
def setup_wireless_interfaces(wireless_iface_name): """ Create a monitor interface ("ra0"), if it does not exist already. Bring the wireless interface up if necessary. """ setup_complete = True # Get interface index iface_index = get_interface_index(wireless_iface_name) # Check if it is a provided interface if not is_wireless_interface(iface_index): setup_complete = False g_exception_logger.error("%s is not a wireless interface.",\ wireless_iface_name) else: if not get_interface_index(MONITOR_INTERFACE_NAME): # Create a monitor interface try: with IW() as iw: iw.add_interface(MONITOR_INTERFACE_NAME, 6, iface_index) except NetlinkError as netlink_err: g_exception_logger.exception(netlink_err) return False # Set the monitor interface "administratively" up if not set_interface_admin_state(MONITOR_INTERFACE_NAME, admin_up=True): g_exception_logger.error("Could not set %s administratively up",\ MONITOR_INTERFACE_NAME) setup_complete = False # Set the interface "administratively" up if not set_interface_admin_state(wireless_iface_name, admin_up=True): g_exception_logger.error("Could not set %s administratively up",\ wireless_iface_name) setup_complete = False return setup_complete
def set_interface_admin_state(iface_name, admin_up=True): """ Set interface "iface_name" administrative state. """ done = True try: with IPDB() as ipdb: with ipdb.interfaces[iface_name] as interface: if admin_up: interface["flags"] |= IFF_UP else: interface["flags"] = ~IFF_UP except KeyError as key_err: g_exception_logger.exception(key_err) done = False except Exception as exception: g_exception_logger.exception(exception) done = False return done
def start(self): """ Start listening for incoming frames. """ resume_scanning = True self._running = True try: self._socket = L2ListenSocket(iface=self._iface) except OSError as err: g_default_logger.error( "Cannot listen on the provided interface \ (%s).", self._iface) g_exception_logger.exception(err) while self._running: try: read, _, __ = select.select([self._socket], [], [], 0.05) if read: frame = self._socket.recv() if frame.src != self._local_mac_addr: self._handle_received_frame(frame) if resume_scanning: # Send an EAPOL-Start frame to try to start the authentication # process. eapol_start_frame = EAPOL(version=self._eapol_version, type=1) self.send_frame(eapol_start_frame) resume_scanning = False except socket.error as err_mess: g_exception_logger.exception(err_mess) self._running = False except KeyboardInterrupt as keyboard_interrupt: # This exception should be caught (ra.py) raise keyboard_interrupt
def main(argv): """ Handles command line options. """ program_name = __prog__ program_version = "v{}".format(__version__) program_build_date = str(__updated__) program_version_message = "{} {} ({})".format(program_name, program_version, program_build_date) program_shortdesc = "radius-audit - A tool to audit RADIUS authentication servers." program_license = """{} USAGE Attempt to discover the phase 1 EAP authentication methods: ra -iface eth0 -identity anonymous --discover-phase-1 Test if the server supports TLS_RSA_WITH_AES_128_CBC_SHA using TLS 1.1: ra -iface eth0 -identity anonymous -tls-versions tls11 -tls-ciphers 0x002f Attempt to perform a complete SSL/TLS scan: ra -iface eth0 -identity anonymous --tls-scan """.format(program_shortdesc) scanner = None try: parser = argparse.ArgumentParser( prog=__prog__, description=program_license, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %(default)s]") parser.add_argument("-V", "--version", action="version", version=program_version_message) parser.add_argument( "-iface", action="store", dest="interface", type=str, required=True, help="provide the network interface to use. Example: eth0") parser.add_argument("-ssid", action="store", dest="ssid", type=str, required=False, help="provide the SSID. Example: test-ssid") parser.add_argument( "-identity", action="store", dest="identity", type=str, default="anonymous", required=False, help="provide the identity of the peer. Example: anonymous") parser.add_argument( "--discover-phase-1", action="store_true", required=False, help="attempt to discover authorized phase 1 EAP authentication \ methods.") parser.add_argument( "-phase-1-methods", action="store", type=str, required=False, help="test if the given EAP authentication methods are supported. \ Example: 13,21,25 (list of EAP types)") parser.add_argument( "-with-eap-method", action="store", type=str, required=False, help="specify an EAP authentication method based on TLS: \ \"tls\", \"ttls\", or \"peap\".") parser.add_argument( "-tls-versions", action="store", type=str, required=False, help="provide a list of specific SSL/TLS versions (sslv2, sslv3, \ tls10, tls11, tls12).") parser.add_argument( "-tls-ciphers", action="store", type=str, required=False, help="provide a list of SSL/TLS ciphersuites (hex format). \ Example: 0x002e,0x002f") parser.add_argument( "--tls-scan", action="store_true", required=False, help="attempt to discover available SSL/TLS versions, as well as \ supported ciphersuites. By default, attempts will be made using EAP-TLS. To \ specify another EAP method based on TLS, use the -with-eap-method parameter.") parser.add_argument( "-min-tls-version", action="store", type=str, required=False, help="start the scan from the given SSL/TLS version (sslv2, sslv3,\ tls10, tls11, tls12).") parser.add_argument("--json-output", action="store_true", required=False, help="print the results in JSON format.") if len(argv) < 2: parser.print_help() return -1 args = parser.parse_args() verbose = args.verbose or 0 g_default_logger.setLevel(max(3 - verbose, 0) * 10) g_traffic_logger.setLevel(max(3 - verbose, 0) * 10) # Check if provided network interface exists interface_index = get_interface_index(args.interface) if not interface_index: g_default_logger.error("Could not find interface %s",\ args.interface) return -1 mac_addr = None ssid = None if args.ssid: if not is_wireless_interface(interface_index): g_default_logger.error( "Provided network interface \ (%s) is not a wireless interface.", args.interface) return -1 ssid = args.ssid mac_addr = get_wireless_interface_mac_addr(interface_index) if not mac_addr: g_default_logger.error( "Provided network interface \ (%s) doe s not exist.", args.interface) return -1 if not setup_wireless_interfaces(args.interface): g_default_logger.error("Could not setup the interfaces.") return -1 else: if is_wireless_interface(interface_index): g_default_logger.error( "Provided network interface \ (%s) is not a wired interface.", args.interface) return -1 # Check if provided network interface is up if not network_interface_status(args.interface): g_default_logger.error( "Provided network interface (%s) is \ down.", args.interface) return -1 mac_addr = get_mac_addr(args.interface) if not mac_addr: g_default_logger.error( "Provided network interface \ (%s) does not exist.", args.interface) return -1 # Set the interface operational state to "DORMANT" if not set_interface_operstate(interface_index, 1, "DORMANT"): g_default_logger.warn( "Could not set %s operational state to \ DORMANT.", args.interface) discover_phase_1 = False phase_1_methods = None with_eap_method = None tls_scan = False tls_versions = None tls_ciphers = None tls_min_version = None json_output = None if args.discover_phase_1: discover_phase_1 = True if args.phase_1_methods: phase_1_methods = [] tmp = args.phase_1_methods.split(',') for val in tmp: if is_integer(val): phase_1_methods.append(int(val)) if args.with_eap_method: tmp = args.with_eap_method.strip().split(',') if not tmp[0] in TLS_BASED_METHODS_BY_NAME: print("{} is not a valid / supported \"TLS-based\" EAP \ authentication method. Please choose one of the following: \ \"tls\", \"ttls\", or \"peap\".".format(tmp[0]), file=sys.stderr) return -1 with_eap_method = TLS_BASED_METHODS_BY_NAME[tmp[0]] if args.tls_versions: tls_versions = [] tmp = args.tls_versions.split(',') for val in tmp: tls_version = get_tls_version_value_from_name(val) if not tls_version: print("{} is not a valid / supported SSL/TLS version. \ Please choose one of the following: sslv2, sslv3, tls10, tls11, tls12." .\ format(val), file=sys.stderr) return -1 else: tls_versions.append(tls_version) tls_ciphers = [] if args.tls_ciphers: tmp = args.tls_ciphers.split(',') for val in tmp: if is_hex_value(val): cs_val = int(val, 16) if not cipher_suite_is_valid(cs_val): print("{} is not a valid / supported cipher suite.".\ format(val), file=sys.stderr) return -1 else: tls_ciphers.append(cs_val) if args.tls_scan: tls_scan = True if args.min_tls_version: tls_version = get_tls_version_value_from_name(args.min_tls_version) if not tls_version: print("{} is not a valid / supported SSL/TLS version.\n\ Please choose one of the following: sslv2, sslv3, tls10, tls11, tls12." .\ format(tls_version), file=sys.stderr) return -1 # Keep the "name" of the version instead of its actual value tls_min_version = args.min_tls_version if args.json_output: json_output = True if discover_phase_1: with_eap_method = None tls_scan = False tls_versions = None tls_ciphers = None tls_min_version = None elif phase_1_methods: with_eap_method = None tls_scan = False tls_versions = None tls_ciphers = None tls_min_version = None else: # By default, the tool will attempt to setup TLS sessions using # EAP-TLS. tls_scan = True # Create and start the "scanner" scanner = Scanner(args.interface, mac_addr, args.identity, ssid, phase_1_methods, with_eap_method, tls_versions, tls_ciphers, tls_scan, tls_min_version, json_output) scanner.start() except KeyboardInterrupt as keyboard_exception: g_exception_logger.exception(keyboard_exception) if scanner: scanner.stop() if is_wireless_interface(interface_index): remove_monitor_interface(MONITOR_INTERFACE_NAME) return -1 except Exception as exception: g_exception_logger.exception(exception) if scanner: scanner.stop() if is_wireless_interface(interface_index): remove_monitor_interface(MONITOR_INTERFACE_NAME) return -1 if is_wireless_interface(interface_index): remove_monitor_interface(MONITOR_INTERFACE_NAME) return 0
def start(self): """ Start listening for incoming frames. """ # Open a socket that will be used to send frames self._socket = socket.socket( socket.AF_PACKET, socket.SOCK_DGRAM, socket.htons(ETH_P_ALL) ) # Open a socket on the monitor interface self._monitor_socket = L2ListenSocket(iface=MONITOR_INTERFACE_NAME) i = 0 self._running = True while self._running: try: read, _, __ = select.select([self._monitor_socket.fileno()], [], [], 0.05) if read: data_ = self._monitor_socket.recv(4096) frame = RadioTap(data_) if Dot11 in frame and\ frame[Dot11].addr2 != self._local_mac_addr: self._handle_received_frame(frame) # Every 2.5+ seconds, try to detect any association loss if not i % 50: association_loss = self._detect_association_loss() if association_loss: g_default_logger.info("Not associated with %s \ anymore.", self._bssid) self._clear_ssid_info() i = 0 # If we're not associated, trigger a new scan in order to # retrieve necessary information if not self._has_ssid_info(): self._prepare() else: if self._association_status != \ WiFiTrafficHandler.ASSO_STATUS_NOT_ASSOCIATED: # Info retrieved in a Probe Response ? # If so, scan in order to get (hopefully) up-to-date # data in another Probe Response if self._previously_associated and \ self._ssid_info_learned_in_presp: self._prepare() else: if not self._authentication_status == \ WiFiTrafficHandler.AUTH_STATUS_AUTHENTICATING or \ not i % 20: if not self._authenticate(): self._authentication_status = \ WiFiTrafficHandler.AUTH_STATUS_AUTHENTICATING else: g_default_logger.warning("Could not \ authenticate") i = i + 1 except socket.error as err_mess: g_exception_logger.exception(err_mess) self._running = False except KeyboardInterrupt as keyboard_interrupt: # This exception should be caught (ra.py) raise keyboard_interrupt