예제 #1
0
def mac_sniffer(st=10):
    IFACE = "mon0"
    SNIFF_TIME = st
    t = AsyncSniffer(iface=IFACE, prn=store_packets)
    t.start()
    waiter(SNIFF_TIME)
    t.stop()
    def start_scapy_scan(self) -> List[bool]:
        """
        This function starts AsyncSniffer and scapy scan,
        finally stops AsyncSniffer.
        """

        no_error = False
        _targets = self._targets
        info("Start AsyncSniffer using Scapy...")

        sniffer = AsyncSniffer(
            iface=self.iface,
            lfilter=lambda p: ((ARP in p and p.psrc in _targets) or
                               (IP in p and p.src in _targets)),
            prn=self.scapy_match,
        )
        sniffer.start()

        try:
            results = self.scapy_scan()
        except Exception as e:
            error = e
        else:
            no_error = True
        finally:
            info("Stop AsyncSniffer, scan end.")
            sniffer.stop()

        if no_error:
            return results
        else:
            raise error
예제 #3
0
def collect_devices(iface, src_mac, timeout=5) -> List[EthernetDevice]:
    global PING_RESULT
    PING_RESULT = {}
    devices = []
    filter_exp = 'ether dst host {0} and ether[16:2] == 0x01cc'.format(
        src_mac)

    command_line = message_helper.build_eth_command(
        dest_mac="ff:ff:ff:ff:ff:ff",
        src_mac=src_mac,
        message_type=PING_PKT,
        message_bytes=[]
    )

    async_sniffer = AsyncSniffer(
        iface=iface,
        prn=handle_ping_receive_packet,
        filter=filter_exp
    )

    async_sniffer.start()
    time.sleep(.1)
    sendp(command_line, iface=iface, verbose=0, count=1)
    time.sleep(timeout)
    async_sniffer.stop()

    for key in PING_RESULT.keys():
        devices.append(EthernetDevice(key))

    return devices
예제 #4
0
class Sniffer:
    """Sniff incoming packets for SYN/ACK responses."""
    def __init__(self, scan_filter, timeout):
        """Initialize Sniffer."""
        self.packets = list()
        self.sniffer = AsyncSniffer(
            filter=scan_filter,
            prn=self.packets.append,
        )
        self.timeout = timeout

    def start(self):
        """Start sniffing."""
        self.sniffer.start()

    def stop(self):
        """Stop sniffing."""
        time.sleep(self.timeout)
        self.sniffer.stop()

    def open_ports(self):
        """Return open ports."""
        live_ports = set()
        for packet in self.packets:
            if packet["TCP"].flags == "SA":
                live_ports.add(packet.sport)
        return sorted(live_ports)
예제 #5
0
파일: wifi.py 프로젝트: lzchmily/wifuzz-1
 def run(self):
     a = AsyncSniffer(iface=self.iface, prn=self.callback)
     a.start()
     c = ProgressBar()
     while self.do_run:
         c.update(len(self.found))
         sleep(1)  # be nice to the cpu
     a.stop()
예제 #6
0
def init_test_sniffer():
    """
    For testing
    """
    # packets = AsyncSniffer(monitor=True, count=0)
    packets = AsyncSniffer(count=0)
    packets.start()

    input("Press enter to stop sniffing: ")

    packets.stop()
    packets.join()
예제 #7
0
def campaign1(s, HOST, TCP_PORT, UDP_PORT, client_ip):
    conn, addr = s.accept()
    conn_ip = addr[0]
    if (conn_ip != client_ip):
        return
    if (conn):
        try:
            sniffer = AsyncSniffer(iface='lo',
                                   prn=printPacket,
                                   filter='dst port {}'.format(UDP_PORT))
            sniffer.start()
            time.sleep(1)
            #Initialization check:
            check = conn.recv(255)
            print("[-] {}".format(check))
            response = 'yes'
            conn.sendall(response)
            time.sleep(1)
            #UDP packet:
            request = conn.recv(255)
            print("[-] {}".format(request))
            #Start UDP server
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                sock.bind((HOST, UDP_PORT))
                print('[+] UDP server started')
                response = 'yes'
            except:
                response = 'no'
                raise

            try:
                conn.sendall(response)
                data, address = sock.recvfrom(1024)
                time.sleep(1)
                print('[*] Received: {}'.format(data))
                result = 'Success, report is: \n{}'.format(info)
                sock.sendto(result, address)
                sniffer.stop()
            except:
                result = 'Failed, report is: \n{}'.format(info)
                sock.sendto(result, address)
                sniffer.stop()
        except:
            print('[+] Error connecting to client')
            s.shutdown(socket.SHUT_RDWR)
            s.close()
            raise
    s.shutdown(socket.SHUT_RDWR)
    s.close()
 def send_async_shake_hand(self, iface, dst_mac, src_mac, filter,
                           use_length_as_protocol):
     pG = [0x01, 0xcc]
     command = helper.build_ethernet_packet(
         dst_mac,
         src_mac,
         pG,
         use_length_as_protocol=use_length_as_protocol)
     async_sniffer = AsyncSniffer(iface=iface,
                                  prn=self.handle_iface_confirm_packet,
                                  filter=filter)
     async_sniffer.start()
     time.sleep(0.2)
     sendp(command.actual_command, iface=iface, verbose=0)
     time.sleep(0.5)
     async_sniffer.stop()
예제 #9
0
 def test_scapy_sniffer(self):
     send_data = [
         Ether(src="99:54:8f:91:12:f6", dst="44:35:a2:a6:d0:bd") /
         IPv6(src="fe80::1", dst="fe80::2") / ICMPv6EchoRequest(),
         Ether(src="44:35:a2:a6:d0:bd", dst="99:54:8f:91:12:f6") /
         IPv6(src="fe80::2", dst="fe80::1") / ICMPv6EchoReply(),
         Ether(src="1f:db:ed:9c:26:6e", dst="18:39:3c:e8:1f:ad") /
         IPv6(src="2001:db8:8a5a:4020:f160:162:c83d:527d",
              dst="2001:db8:2932:29c9:35eb:9377:ae68:634d") /
         UDP(sport=31245, dport=8788) / b"abcdef",
         Ether(src="18:39:3c:e8:1f:ad", dst="1f:db:ed:9c:26:6e") /
         IPv6(src="2001:db8:2932:29c9:35eb:9377:ae68:634d",
              dst="2001:db8:8a5a:4020:f160:162:c83d:527d") /
         UDP(sport=8788, dport=31245) / b"12345",
     ]
     sniffer = AsyncSniffer()
     sniffer.start()
     self.spawn.expect(r"Initializing L2listen with arguments")
     self.spawn.expect(r"Bound socket to '(.*)'\s")
     remote = self.spawn.match.group(1)
     # wait a bit to avoid race where socket is not bound yet
     time.sleep(.5)
     for packet in send_data:
         time.sleep(.001)  # some spacing for test stability
         self.assertEqual(len(raw(packet)),
                          self.comm_sock.sendto(raw(packet), remote))
     for packet in send_data:
         self.spawn.expect(r"Receiving on L2listen with arguments")
     results = sniffer.stop()
     self.assertIsNotNone(results)
     self.assertEqual(len(send_data), len(results))
     for pkt in send_data:
         self.assertIn(Ether(raw(pkt)), results)
class DeviceSniffer:
    def __init__(self, user_querier: UserQuerier, interface="mon0"):
        self.async_sniffer = AsyncSniffer(prn=self.handle_packet,
                                          store=False,
                                          iface=interface,
                                          monitor=True)
        self.device_dectect_stream = Subject()
        self.user_querier = user_querier

    def __del__(self):
        self.device_dectect_stream.on_completed()

    @staticmethod
    def is_probe_request(packet: Packet):
        return packet.type == 0 and packet.subtype == 4

    def handle_packet(self, packet: Packet):
        if not DeviceSniffer.is_probe_request(packet):
            return

        try:
            target_ssid = packet.getlayer(Dot11Elt).getfieldval("info").decode(
                "utf-8")
            if len(target_ssid) == 0:
                return

            source_mac_addr = packet.addr2.upper()
            userid = self.user_querier.get_userid(target_ssid, source_mac_addr)
            if userid is not None:
                self.device_dectect_stream.on_next(userid)

        except Exception as err:
            self.device_dectect_stream.on_error(err)

    def get_observable(self) -> Subject:
        return self.device_dectect_stream

    def start(self):
        self.async_sniffer.start()

    def stop(self):
        self.async_sniffer.stop()
예제 #11
0
def make_pcaps():
    #don't think I'll need more but make an hour worth of
    #one minute pcap files
    for i in range(60):
        #if we need we can narrow the interface we use to sniff by changing
        #the args to AsyncSniffer()
        t = AsyncSniffer()
        t.start()
        time.sleep(60)
        z = t.stop()
        wrpcap(f"test_{i}.pcap", z)
예제 #12
0
class DashSensor(Sensor):
    """Uses an AsyncSniffer to watch for ARP packets generated by an Amazon Dash
    button when the button is pressed.
    """
    def __init__(self, publishers, params):
        """Initializes and starts the background scanning for Dash Button ARP
        packets, publishing the MAC address to the destination when one is
        detected.
        """
        super().__init__(publishers, params)

        self.log.info("Configuing Dash Scanner")

        self.devices = get_sequential_param_pairs(params, "MAC", "Destination")

        if self.poll > 0:
            raise ValueError("DashSensor is not a polling sensor!")

        self.sniffer = AsyncSniffer(prn=self.arp_received,
                                    filter="arp",
                                    store=0,
                                    count=0)
        self.sniffer.start()

    def arp_received(self, pkt):
        """Called by the sniffer when an ARP packet is received. If it's from a
        device that we have a configured MAC address, we publish the MAC address
        to the configured destination.
        """
        # 1 = who-has, 2 = is-at
        if ARP in pkt and pkt[ARP].op in (1, 2):
            mac = pkt[ARP].hwsrc
            if mac in self.devices:
                self.log.info("Dash button pressed for %s publishing to %s",
                              mac, self.devices[mac])
                self._send(mac, self.devices[mac])

    def cleanup(self):
        """Stops and waits for the sniffer to exit."""
        self.sniffer.stop()
        self.sniffer.join()
예제 #13
0
    def __StartCap(self):
        global basesettings

        # Used to count how many packets have been recieved throughout capture
        global pkt_count
        pkt_count = 1
        
        # Determine if user specified a run time or a run period
        if basesettings['RunTime'] != '':
            # After calculating how long the program is specified to run for
            # the program starts an asynchronous sniffer that captures packets
            # in a seperate thread.
            dt_now, dt_run = self.__CalcRuntime()
            
            Asniff = AsyncSniffer(iface=basesettings['Interface'], prn=self.__DisplayPackets)
            Asniff.start()
            
            while dt_now <= dt_run:
                dt_now, str_now, date_now = self.__GetTimeNow()

            Asniff.stop()
            print(colored("\nScan has stopped!", 'green'))
        
        elif basesettings['RunPeriod'] != '':
            # After calculating how long the program is specified to run for
            # the program starts an asynchronous sniffer that captures packets
            # in a seperate thread.
            dt_now, dt_run = self.__CalcPeriod()

            Asniff = AsyncSniffer(iface=basesettings['Interface'], prn=self.__DisplayPackets)
            Asniff.start()
            
            while dt_now <= dt_run:
                dt_now, str_now, date_now = self.__GetTimeNow()

            Asniff.stop()
            print(colored("\nScan has stopped!", 'green'))
예제 #14
0
 def send_and_exp_any_dhcp6(cls, send_pkt, client=True, ipv6_src=None):
     sniffer = AsyncSniffer(iface=cls.tap)
     sniffer.start()
     time.sleep(cls.pre_sniffer_wait)
     cls._sendp(send_pkt, client=client, ipv6_src=ipv6_src)
     time.sleep(cls.post_sniffer_wait)
     return [
         pkt for pkt in sniffer.stop() if cls._contains_dhcp(pkt) and
         # filter out sent packet
         (UDP not in pkt or raw(pkt[UDP].payload) != raw(send_pkt)) and
         # filter out ICMPv6 since error notifications can also contain
         # the sent packets and we are not interested in error
         # notifications
         ICMPv6DestUnreach not in pkt
     ]
예제 #15
0
class Aggregator():
    def __init__(self):
        self.value = 0
        self.sniffer = None
        self.lock = Lock()

    def sniffer_callback(self, pkt):
        self.lock.acquire()
        self.value += len(pkt)
        self.lock.release()

    def sniffer_activate(self):
        self.sniffer = AsyncSniffer(prn=self.sniffer_callback)
        self.sniffer.start()

    def sniffer_deactivate(self):
        self.sniffer.stop()

    def flush(self):
        self.lock.acquire()
        res = self.value
        self.value = 0
        self.lock.release()
        return res
예제 #16
0
def sniff_routine() -> PacketList:
    sniffer = AsyncSniffer(prn=lambda x: x.summary(), filter='ip')

    while True:
        start = input("Type start to start capturing\n")
        if start == "start":
            print("Starting to capture...")
            break

    sniffer.start()

    while True:
        stop = input("To stop capturing type stop\n")
        if stop == "stop":
            break

    sniff_result = sniffer.stop()
    wrpcap("output/output.cap", sniff_result)
    return sniff_result
예제 #17
0
class DHCPClient:
    THREAD_YIELD_TIME = .1

    def __init__(self,
                 dhcp_store: MutableMapping[str, DHCPDescriptor],
                 gw_info: UplinkGatewayInfo,
                 dhcp_wait: Condition,
                 iface: str = "dhcp0",
                 lease_renew_wait_min: int = 200):
        """
        Implement DHCP client to allocate IP for given Mac address.
        DHCP client state is maintained in user provided hash table.
        Args:
            dhcp_store: maintain DHCP transactions, key is mac address.
            gw_info_map: stores GW IP info from DHCP server
            dhcp_wait: notify users on new DHCP packet
            iface: DHCP egress and ingress interface.
        """
        self._sniffer = AsyncSniffer(iface=iface,
                                     filter="udp and (port 67 or 68)",
                                     prn=self._rx_dhcp_pkt)

        self.dhcp_client_state = dhcp_store  # mac => DHCP_State
        self.dhcp_gw_info = gw_info
        self._dhcp_notify = dhcp_wait
        self._dhcp_interface = iface
        self._msg_xid = 0
        self._lease_renew_wait_min = lease_renew_wait_min
        self._monitor_thread = threading.Thread(
            target=self._monitor_dhcp_state)
        self._monitor_thread.daemon = True
        self._monitor_thread_event = threading.Event()

    def run(self):
        """
        Start DHCP sniffer thread.
        This initializes state required for DHCP sniffer thread anf starts it.
        Returns: None
        """
        self._sniffer.start()
        LOG.info("DHCP sniffer started")
        # give it time to schedule the thread and start sniffing.
        time.sleep(self.THREAD_YIELD_TIME)
        self._monitor_thread.start()

    def stop(self):
        self._sniffer.stop()
        self._monitor_thread_event.set()

    def send_dhcp_packet(self,
                         mac: MacAddress,
                         vlan: str,
                         state: DHCPState,
                         dhcp_desc: DHCPDescriptor = None):
        """
        Send DHCP packet and record state in dhcp_client_state.

        Args:
            mac: MAC address of interface
            state: state of DHCP packet
            dhcp_desc: DHCP protocol state.
        Returns:
        """
        ciaddr = None

        # generate DHCP request packet
        if state == DHCPState.DISCOVER:
            dhcp_opts = [("message-type", "discover")]
            dhcp_desc = DHCPDescriptor(mac=mac,
                                       ip="",
                                       vlan=vlan,
                                       state_requested=DHCPState.DISCOVER)
            self._msg_xid = self._msg_xid + 1
            pkt_xid = self._msg_xid
        elif state == DHCPState.REQUEST:
            dhcp_opts = [("message-type", "request"),
                         ("requested_addr", dhcp_desc.ip),
                         ("server_id", dhcp_desc.server_ip)]
            dhcp_desc.state_requested = DHCPState.REQUEST
            pkt_xid = dhcp_desc.xid
            ciaddr = dhcp_desc.ip
        elif state == DHCPState.RELEASE:
            dhcp_opts = [("message-type", "release"),
                         ("server_id", dhcp_desc.server_ip)]
            dhcp_desc.state_requested = DHCPState.RELEASE
            self._msg_xid = self._msg_xid + 1
            pkt_xid = self._msg_xid
            ciaddr = dhcp_desc.ip
        else:
            LOG.warning("Unknown egress request mac %s state %s", str(mac),
                        state)
            return

        dhcp_opts.append("end")
        dhcp_desc.xid = pkt_xid
        with self._dhcp_notify:
            self.dhcp_client_state[mac.as_redis_key(vlan)] = dhcp_desc

        pkt = Ether(src=str(mac), dst="ff:ff:ff:ff:ff:ff")
        if vlan and vlan != "0":
            pkt /= Dot1Q(vlan=int(vlan))
        pkt /= IP(src="0.0.0.0", dst="255.255.255.255")
        pkt /= UDP(sport=68, dport=67)
        pkt /= BOOTP(op=1, chaddr=mac.as_hex(), xid=pkt_xid, ciaddr=ciaddr)
        pkt /= DHCP(options=dhcp_opts)
        LOG.debug("DHCP pkt xmit %s", pkt.show(dump=True))

        sendp(pkt, iface=self._dhcp_interface, verbose=0)

    def get_dhcp_desc(self, mac: MacAddress,
                      vlan: str) -> Optional[DHCPDescriptor]:
        """
                Get DHCP description for given MAC.
        Args:
            mac: Mac address of the client
            vlan: vlan id if the IP allocated in a VLAN

        Returns: Current DHCP info.
        """

        key = mac.as_redis_key(vlan)
        if key in self.dhcp_client_state:
            return self.dhcp_client_state[key]

        LOG.debug("lookup error for %s", str(key))
        return None

    def release_ip_address(self, mac: MacAddress, vlan: str):
        """
                Release DHCP allocated IP.
        Args:
            mac: MAC address of the IP allocated.
            vlan: vlan id if the IP allocated in a VLAN

        Returns: None
        """
        key = mac.as_redis_key(vlan)
        if key not in self.dhcp_client_state:
            LOG.error("Unallocated DHCP release for MAC: %s", key)
            return

        dhcp_desc = self.dhcp_client_state[key]
        self.send_dhcp_packet(mac, dhcp_desc.vlan, DHCPState.RELEASE,
                              dhcp_desc)

    def _monitor_dhcp_state(self):
        """
        monitor DHCP client state.
        """
        while True:
            wait_time = self._lease_renew_wait_min
            with self._dhcp_notify:
                for dhcp_record in self.dhcp_client_state.values():
                    logging.debug("monitor: %s", dhcp_record)
                    # Only process active records.
                    if dhcp_record.state not in DHCP_ACTIVE_STATES:
                        continue

                    now = datetime.datetime.now()
                    logging.debug("monitor time: %s", now)
                    request_state = DHCPState.REQUEST
                    # in case of lost DHCP lease rediscover it.
                    if now >= dhcp_record.lease_expiration_time:
                        request_state = DHCPState.DISCOVER

                    if now >= dhcp_record.lease_renew_deadline:
                        logging.debug("sending lease renewal")
                        self.send_dhcp_packet(dhcp_record.mac,
                                              dhcp_record.vlan, request_state,
                                              dhcp_record)
                    else:
                        # Find next renewal wait time.
                        time_to_renew = dhcp_record.lease_renew_deadline - now
                        wait_time = min(wait_time,
                                        time_to_renew.total_seconds())

            # default in wait is 30 sec
            wait_time = max(wait_time, self._lease_renew_wait_min)
            logging.debug("lease renewal check after: %s sec" % wait_time)
            self._monitor_thread_event.wait(wait_time)
            if self._monitor_thread_event.is_set():
                break

    @staticmethod
    def _get_option(packet, name):
        for opt in packet[DHCP].options:
            if opt[0] == name:
                return opt[1]
        return None

    def _process_dhcp_pkt(self, packet, state: DHCPState):
        LOG.debug("DHCP pkt recv %s", packet.show(dump=True))

        mac_addr = MacAddress(hex_to_mac(packet[BOOTP].chaddr.hex()[0:12]))
        vlan = ""
        if Dot1Q in packet:
            vlan = str(packet[Dot1Q].vlan)
        mac_addr_key = mac_addr.as_redis_key(vlan)

        with self._dhcp_notify:
            if mac_addr_key in self.dhcp_client_state:
                state_requested = self.dhcp_client_state[
                    mac_addr_key].state_requested
                if BOOTP not in packet or packet[BOOTP].yiaddr is None:
                    LOG.error("no ip offered")
                    return

                ip_offered = packet[BOOTP].yiaddr
                subnet_mask = self._get_option(packet, "subnet_mask")
                if subnet_mask is not None:
                    ip_subnet = IPv4Network(ip_offered + "/" + subnet_mask,
                                            strict=False)
                else:
                    ip_subnet = IPv4Network(ip_offered + "/" + "32",
                                            strict=False)

                dhcp_router_opt = self._get_option(packet, "router")
                if dhcp_router_opt is not None:
                    router_ip_addr = ip_address(dhcp_router_opt)
                    self.dhcp_gw_info.update_ip(router_ip_addr, vlan)
                else:
                    router_ip_addr = None

                lease_expiration_time = self._get_option(packet, "lease_time")
                dhcp_state = DHCPDescriptor(
                    mac=mac_addr,
                    ip=ip_offered,
                    state=state,
                    vlan=vlan,
                    state_requested=state_requested,
                    subnet=str(ip_subnet),
                    server_ip=packet[IP].src,
                    router_ip=router_ip_addr,
                    lease_expiration_time=lease_expiration_time,
                    xid=packet[BOOTP].xid)
                LOG.info("Record DHCP for: %s state: %s", mac_addr_key,
                         dhcp_state)

                self.dhcp_client_state[mac_addr_key] = dhcp_state
                self._dhcp_notify.notifyAll()

                if state == DHCPState.OFFER:
                    #  let other thread work on fulfilling IP allocation request.
                    threading.Event().wait(self.THREAD_YIELD_TIME)
                    self.send_dhcp_packet(mac_addr, vlan, DHCPState.REQUEST,
                                          dhcp_state)
            else:
                LOG.debug("Unknown MAC: %s " % packet.summary())
                return

    # ref: https://fossies.org/linux/scapy/scapy/layers/dhcp.py
    def _rx_dhcp_pkt(self, packet):
        if DHCP not in packet:
            return

        # Match DHCP offer
        if packet[DHCP].options[0][1] == int(DHCPState.OFFER):
            self._process_dhcp_pkt(packet, DHCPState.OFFER)

        # Match DHCP ack
        elif packet[DHCP].options[0][1] == int(DHCPState.ACK):
            self._process_dhcp_pkt(packet, DHCPState.ACK)
예제 #18
0
class DHCPClient:
    THREAD_YIELD_TIME = .1

    def __init__(self,
                 dhcp_store: MutableMapping[str, DHCPDescriptor],
                 gw_info: UplinkGatewayInfo,
                 dhcp_wait: Condition,
                 iface: str = "dhcp0"):
        """
        Implement DHCP client to allocate IP for given Mac address.
        DHCP client state is maintained in user provided hash table.
        Args:
            dhcp_store: maintain DHCP transactions, key is mac address.
            gw_info_map: stores GW IP info from DHCP server
            dhcp_wait: notify users on new DHCP packet
            iface: DHCP egress and ingress interface.
        """
        self._sniffer = AsyncSniffer(iface=iface,
                                     filter="udp and (port 67 or 68)",
                                     prn=self._rx_dhcp_pkt)

        self.dhcp_client_state = dhcp_store  # mac => DHCP_State
        self.dhcp_gw_info = gw_info
        self._dhcp_notify = dhcp_wait
        self._dhcp_interface = iface
        self._msg_xid = 0

    def run(self):
        """
        Start DHCP sniffer thread.
        This initializes state required for DHCP sniffer thread anf starts it.
        Returns: None
        """
        self._sniffer.start()
        LOG.info("DHCP sniffer started")
        # give it time to schedule the thread and start sniffing.
        time.sleep(self.THREAD_YIELD_TIME)

    def stop(self):
        self._sniffer.stop()

    def send_dhcp_packet(self,
                         mac: MacAddress,
                         state: DHCPState,
                         dhcp_desc: DHCPDescriptor = None):
        """
        Send DHCP packet and record state in dhcp_client_state.

        Args:
            mac: MAC address of interface
            state: state of DHCP packet
            dhcp_desc: DHCP protocol state.
        Returns:
        """
        rel_ciaddr = None

        # generate DHCP request packet
        if state == DHCPState.DISCOVER:
            dhcp_opts = [("message-type", "discover")]
            dhcp_desc = DHCPDescriptor(mac, "", DHCPState.DISCOVER)
            self._msg_xid = self._msg_xid + 1
            pkt_xid = self._msg_xid
        elif state == DHCPState.REQUEST:
            dhcp_opts = [("message-type", "request"),
                         ("requested_addr", dhcp_desc.ip),
                         ("server_id", dhcp_desc.server_ip)]
            dhcp_desc.state = DHCPState.REQUEST
            pkt_xid = dhcp_desc.xid
        elif state == DHCPState.RELEASE:
            dhcp_opts = [("message-type", "release"),
                         ("server_id", dhcp_desc.server_ip)]
            dhcp_desc.state = DHCPState.RELEASE
            self._msg_xid = self._msg_xid + 1
            pkt_xid = self._msg_xid
            rel_ciaddr = dhcp_desc.ip
        else:
            LOG.warning("Unknown egress request mac %s state %s", str(mac),
                        state)
            return

        dhcp_opts.append("end")

        with self._dhcp_notify:
            self.dhcp_client_state[mac.as_redis_key()] = dhcp_desc

        LOG.debug("SEND %s mac %s hex %s xid %s", state.name, str(mac), mac,
                  self._msg_xid)
        pkt = Ether(src=str(mac), dst="ff:ff:ff:ff:ff:ff")
        pkt /= IP(src="0.0.0.0", dst="255.255.255.255")
        pkt /= UDP(sport=68, dport=67)
        pkt /= BOOTP(op=1, chaddr=mac.as_hex(), xid=pkt_xid, ciaddr=rel_ciaddr)
        pkt /= DHCP(options=dhcp_opts)
        LOG.debug("DHCP pkt %s", pkt.summary())

        sendp(pkt, iface=self._dhcp_interface, verbose=0)

    def get_dhcp_desc(self, mac: MacAddress) -> Optional[DHCPDescriptor]:
        """
                Get DHCP description for given MAC.
        Args:
            mac: Mac address of the client

        Returns: Current DHCP info.
        """

        key = mac.as_redis_key()
        if key in self.dhcp_client_state:
            return self.dhcp_client_state[key]

        LOG.debug("lookup error for %s", str(mac))
        return None

    def release_ip_address(self, mac: MacAddress):
        """
                Release DHCP allocated IP.
        Args:
            mac: MAC address of the IP allocated.

        Returns: None
        """

        if mac.as_redis_key() not in self.dhcp_client_state:
            LOG.error("Unallocated DHCP release for MAC: %s", str(mac))
            return

        dhcp_desc = self.dhcp_client_state[mac.as_redis_key()]
        self.send_dhcp_packet(mac, DHCPState.RELEASE, dhcp_desc)

    def manage_dhcp_state(self):
        """
        monitor DHCP client state.
        TODO:
        Handle IP address lease revoke.

        """

    @staticmethod
    def _get_option(packet, name):
        for opt in packet[DHCP].options:
            if opt[0] == name:
                return opt[1]
        return None

    def _process_dhcp_pkt(self, packet, state: DHCPState):
        mac_addr = create_mac_from_sid(packet[Ether].dst)
        mac_addr_key = mac_addr.as_redis_key()

        with self._dhcp_notify:
            if mac_addr_key in self.dhcp_client_state:
                ip_offered = packet[BOOTP].yiaddr
                subnet_mask = self._get_option(packet, "subnet_mask")
                if subnet_mask is not None:
                    ip_subnet = IPv4Network(ip_offered + "/" + subnet_mask,
                                            strict=False)
                else:
                    ip_subnet = None

                dhcp_router_opt = self._get_option(packet, "router")
                if dhcp_router_opt is not None:
                    router_ip_addr = ip_address(dhcp_router_opt)
                else:
                    router_ip_addr = None

                lease_time = self._get_option(packet, "lease_time")
                dhcp_state = DHCPDescriptor(mac_addr, ip_offered, state,
                                            str(ip_subnet), packet[IP].src,
                                            router_ip_addr, lease_time,
                                            packet[BOOTP].xid)
                LOG.info("Record mac %s IP %s", mac_addr_key, dhcp_state)

                self.dhcp_client_state[mac_addr_key] = dhcp_state

                self.dhcp_gw_info.update_ip(router_ip_addr)
                self._dhcp_notify.notifyAll()

                if state == DHCPState.OFFER:
                    #  let other thread work on fulfilling IP allocation request.
                    threading.Event().wait(self.THREAD_YIELD_TIME)
                    self.send_dhcp_packet(mac_addr, DHCPState.REQUEST,
                                          dhcp_state)
            else:
                LOG.debug("Unknown MAC: %s " % packet.summary())
                return

    # ref: https://fossies.org/linux/scapy/scapy/layers/dhcp.py
    def _rx_dhcp_pkt(self, packet):
        if DHCP not in packet:
            return

        LOG.debug("DHCP type %s", packet[DHCP].options[0][1])

        # Match DHCP offer
        if packet[DHCP].options[0][1] == int(DHCPState.OFFER):
            LOG.debug("Offer %s (%s) ", packet[IP].src, packet[Ether].src)
            self._process_dhcp_pkt(packet, DHCPState.OFFER)

        # Match DHCP ack
        elif packet[DHCP].options[0][1] == int(DHCPState.ACK):
            LOG.debug("Acked %s (%s) ", packet[IP].src, packet[Ether].src)
            self._process_dhcp_pkt(packet, DHCPState.ACK)
예제 #19
0
class Sniffer:
    """
    Constructs the sniffer object; notably takes a config
    keyword to control what mode to run in
    'testing' ignores ssh traffic
    """
    def __init__(self, config, mode="base", databaser=None, send_channel=None):

        self.config = config
        self.mode = mode
        self.db = databaser
        self.channel = send_channel
        # used to detect port scans
        self.portScanTimeout = None
        # also used to detect port scans
        self.PS_RECORD = dict()
        # set used for testing convenience
        self.RECORD = dict()
        # Hash used to tell if we properly updated Sniffer class;
        # there is probably a better way of making this hash
        self.currentHash = hash(self.config)
        self.index_map = {}
        for port in self.config.open_ports:
            self.index_map[port] = {}

    def start(self):
        """
        Runs the thread, begins sniffing with given config
        """
        print("Starting async sniffer")
        localIPList = []
        fltr = ""
        # Get a list of all IP addresses assigned to local NICs
        # and ensure that this packet is not from any of them
        for interface in interfaces():
            for link in ifaddresses(interface).get(AF_INET, ()):
                if fltr != "":
                    fltr += "and "
                localIPList.append(link["addr"])
                fltr += "not src host {} ".format(link["addr"])

        # Not to or from the database IP
        if self.db is not None:
            if fltr != "":
                fltr += "and "
            fltr += "not src host {} and not dst host {} ".format(
                socket.gethostbyname(self.db.db_ip),
                socket.gethostbyname(self.db.db_ip))

        # Honor the port whitelist for incoming packets
        for port in self.config.whitelist_ports:
            if fltr != "":
                fltr += "and "
            fltr += "not dst port {} ".format(port)
        # Honor the ip whitelist for both direction
        for ip in self.config.whitelist_addrs:
            if ip not in localIPList:  # avoid blocking all incoming packets in the case that our own IP is entered into whitelist
                if fltr != "":
                    fltr += "and "
                fltr += "not src host {} and not dst host {} ".format(ip, ip)

        print("Filter", fltr)

        # here's where the packet detection starts

        if self.mode == "testing":
            # this ignores the ssh spam you get when sending
            # packets between two ssh terminals
            if fltr != "":
                fltr += "and "
            fltr = fltr + "not (src port ssh or dst port ssh)"
        elif self.mode == "base":
            # this above filter ignores the ssh spam you get when sending packets
            #  between two ssh terminals - TODO: TAKE THIS OUT IN PROD
            if fltr != "":
                fltr += "and "
            fltr = fltr + "not (src port ssh or dst port ssh)"
        elif self.mode == "onlyUDP":
            # this last config option is used in testing
            fltr = "udp"

        self.sniffer = AsyncSniffer(filter=fltr,
                                    prn=self.save_packet,
                                    store=False)
        if not self.sniffer:
            raise Exception("Async sniffer not initialized")

        self.sniffer.start()

    def stop(self):
        """
        Attempts to stop the async sniffer
        """
        if not self.sniffer or not self.sniffer.running:
            raise Exception("Async sniffer not initialized")
        self.sniffer.stop()

    def configUpdate(self, conf):
        """
        Updates configuration options during runtime
        """
        print("Async sniffer updated")
        self.running = False
        self.config = conf
        # updates hash
        self.currentHash = hash(self.config)

        # restart's Sniffer
        try:
            self.stop()
        except Scapy_Exception as ex:
            print("Sniffer did not finish setting up before teardown: ",
                  str(ex))
        self.start()

    async def send_msg(self, destPort):
        await self.channel.send("{ port: " + str(destPort) + ", packet: {} }")

    def save_packet(self, packet):
        """
        Function for recording a packet during sniff runtime
        packet = the packet passed through the sniff function
        """
        # TODO: make this work with layer 2, for now just skip filtering those packets
        if not packet.haslayer("IP"):
            return

        # timestamp used for port scan detection
        currentTime = int(datetime.now().timestamp())
        if self.portScanTimeout is None:
            self.portScanTimeout = currentTime

        # how to tell if we need to reset our port scan record
        if currentTime > self.portScanTimeout + self.config.portscan_window:
            print("resetting timeout time")
            self.portScanTimeout = currentTime
            self.PS_RECORD = dict()

        # A bunch of packet data, collected to be stored
        sourceMAC = packet.src
        destMAC = packet.dst
        ipLayer = packet.getlayer("IP")
        # IP where this came from
        srcIP = ipLayer.src
        dstIP = ipLayer.dst
        destPort = ipLayer.dport if hasattr(ipLayer, "dport") else None
        srcPort = ipLayer.sport if hasattr(ipLayer, "sport") else None

        if (not ipLayer.haslayer("TCP") and not ipLayer.haslayer("UDP")
                and not ipLayer.haslayer("ICMP")):
            return

        # Testing config - does not utilize a database
        # isTest = self.config == "onlyUDP" or self.config == "testing"

        trafficType = ("TCP" if ipLayer.haslayer("TCP") else
                       "UDP" if ipLayer.haslayer("UDP") else
                       "ICMP" if ipLayer.haslayer("ICMP") else "Other")

        # Log Entry object we're saving
        log = LogEntry(
            srcPort,
            srcIP,
            sourceMAC,
            destPort,
            dstIP,
            destMAC,
            trafficType,
            ipLayer.len,
            destPort in self.config.open_ports,
        )

        # self.RECORD is where we save logs for easy testing
        if srcIP in self.RECORD.keys():
            self.RECORD[srcIP].append(log)
        else:
            self.RECORD[srcIP] = [log]

        # saving the database ID in case of port scan detection
        if self.mode == "base" and self.db is not None:
            dbID = self.db.saveLogObject(log)
        else:
            return

        # self.PS_RECORD is a separate dictionary used for port scan detection
        if srcIP not in self.PS_RECORD.keys():
            self.PS_RECORD[srcIP] = dict()
            self.PS_RECORD[srcIP][log.destPortNumber] = dbID
        else:
            self.PS_RECORD[srcIP][log.destPortNumber] = dbID

            # Sending out the port scan alert
            if len(self.PS_RECORD[srcIP]) > self.config.portscan_threshold:
                if self.db is not None:
                    self.db.saveAlertObject(
                        Alert(
                            variant="alert",
                            message="Port scan detected from IP {}".format(
                                srcIP),
                            references=list(self.PS_RECORD[srcIP].values()),
                        ))
                self.PS_RECORD[srcIP] = dict()
예제 #20
0
class PacketReader:
    """ 
    This class needs root privileges to run
    """
    def __init__(self, interface: str):
        self.interface = interface
        self.store = []
        self.sniffer = None
        self.last_index = 0

    def _store(self, packet):
        logger.debug('read packet {}'.format(packet.summary()))
        self.store.append(packet)

    def sniff(self, count=0):
        """
        start async sniffer
        """
        logger.info("starting to sniff packets")
        # TODO filter all outcoming packets
        self.sniffer = AsyncSniffer(session=TCPSession,
                                    prn=self._store,
                                    iface=self.interface,
                                    count=count,
                                    store=False)
        self.sniffer.start()

    def get_packets(self) -> list:
        """
        get new packets since last call of this function
        """
        logger.debug("getting new packets with index at {}".format(
            self.last_index))
        index = self.last_index
        self.last_index = len(self.store)
        logger.debug("new index is at {}".format(self.last_index))
        return self.store[index:]

    def end(self):
        logger.info("stopping reader")
        if not self.sniffer or not self.sniffer.running:
            logger.error("sniffer not started")
            return
        self.sniffer.stop()

    def remove_old_packets(self):
        """
        remove all already read packets by `get_packets`
        !! Warning - you will loose the references to those packets !!
        """
        logger.debug("removing old packets with index at {}".format(
            self.last_index))
        del self.store[:self.last_index]

    def started(self) -> bool:
        return True if self.sniffer else False

    def packet_q(self) -> int:
        """
        how many packets is currently in store, not processed
        """
        return len(self.store) - self.last_index
예제 #21
0
def icmpv4_probe(dst_host, timeout):
    icmptype_i = 0x8
    icmptype_name_i = 'ICMP ECHO'
    icmptype_o = 0x0
    icmptype_name_o = 'ICMP ECHO_REPLY'

    stack_name = None
    match = MATCH_NO_MATCH

    ip = IP(dst=dst_host, ttl=20, proto=0x01)

    # First, check if we can reach ICMP
    std_icmp_payload = '\xcd\x69\x08\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17' \
                       '\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27' \
                       '\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37'

    reply = sr1(ip / ICMP(id=0xff, seq=1, type=icmptype_i) /
                Raw(load=std_icmp_payload),
                filter='icmp[icmptype] = {}'.format(icmptype_o),
                timeout=timeout)
    if not reply:
        return (stack_name, MATCH_NO_REPLY)

    # If there is no reply to the second ICMP packet, either the target IP cannot be reached (or ICMP is
    # disabled), or we deal with the CycloneTCP stack that will accept only ICMP packets that have at least 1 byte
    # of data. To check for CycloneTCP, we craft such a packet: we expect the 1 byte of data back (+ optional padding).
    reply = sr1(ip / ICMP(id=0xff, seq=1, type=icmptype_i),
                filter='icmp[icmptype] = {}'.format(icmptype_o),
                timeout=timeout)
    if not reply:
        reply = sr1(ip / ICMP(id=0xff, seq=1, type=icmptype_i) /
                    Raw(load=b'\x41'),
                    filter='icmp[icmptype] = {}'.format(icmptype_o),
                    timeout=timeout)
        if reply and reply.ttl == 64:
            if Raw in reply and Padding in reply and reply[Raw].load == b'\x41':
                match = MATCH_MEDIUM
                stack_name = 'CycloneTCP'
                return (stack_name, match)

    # Next, we prepare a packet that should work with uIP/Contiki and PicoTCP
    icmp_raw = b'\x08\x01\x02'
    ipv4_probe = ip / Raw(load=icmp_raw)

    # Send the malformed ICMP packet
    # If we get the expected reply it is either PicoTCP or uIP/Contiki:
    #   - we first check that the TTL value of the echo packet is changed into 64 for the reply packet
    #   - we then check the payload sequence of the echo reply packet
    reply = sr1(ipv4_probe,
                filter='icmp[icmptype] = {}'.format(icmptype_o),
                timeout=timeout)
    if reply and reply.ttl == 64:
        if (hexlify(reply.load) == b'0001ff'):
            match = MATCH_HIGH
            stack_name = 'PicoTCP'
        elif (hexlify(reply.load) == b'00010a'):
            match = MATCH_HIGH
            stack_name = 'uIP/Contiki'

    else:  # we did not get a reply for the first malformed packet
        _id = 0xab
        _seq = 0xba
        # Nut/Net should reply to ICMP packets with incorrect IP and ICMP checksums
        ipv4_probe = IP(dst=dst_host, ttl=20, chksum=0xdead) / ICMP(
            id=_id, seq=_seq, type=icmptype_i, chksum=0xbeaf)
        reply = sr1(ipv4_probe,
                    filter='icmp[icmptype] = {}'.format(icmptype_o),
                    timeout=timeout)
        # TTL value must be 64 as well
        if reply and reply.ttl == 64:
            if (reply[ICMP].id == _id and reply[ICMP].seq == _seq
                    and reply[ICMP].type == 0x00):
                match = MATCH_MEDIUM
                stack_name = 'Nut/Net'

    # Here we handle all other cases
    if match == MATCH_NO_MATCH:

        # NDKTCPIP should reply to an ICMP packet that has at least 4 bytes of the header and a correct ICMP checksum
        # The code (2nd byte) must be 0x00
        icmp_raw = b'\x08\x00\xf7\xff'
        ipv4_probe = ip / Raw(load=icmp_raw)
        # For some reason Scapy will not get the reply to this packet, so I had to use asynchronous sniffing

        t = AsyncSniffer(iface=interface)
        t.start()
        send(ipv4_probe)
        time.sleep(timeout * 4)
        pkts = t.stop()

        for pkt in pkts:
            # first, let's check the source and the destination IP
            if IP in pkt and pkt[IP].src == dst_host and pkt[IP].dst == ip.src:
                # NDKTCPIP will reply with a TTL value of 255, the ICMP checksum will be 0xffff
                if ICMP in pkt and pkt[ICMP].type == 0x00 and pkt[
                        ICMP].chksum == 0xffff:
                    # NDKTCPIP will reply with a TTL value of 255, the ICMP checksum will be 0xffff
                    if pkt.ttl == 255:
                        match = MATCH_HIGH
                        stack_name = 'NDKTCPIP'
                        break

                    # Nucleus Net AND NicheStack will reply with a TTL value of 64, the ICMP checksum will be 0xffff.
                    # So far, we assume it is NicheStack.
                    elif pkt.ttl == 64:
                        match = MATCH_MEDIUM
                        stack_name = 'NicheStack'
                        break

    # We do an additional check for Nucleus Net: it will reply to a malformed ICMP packet that has only 1 byte in its header.
    # If we don't get a reply, NicheStack it is.
    if stack_name == 'NicheStack':
        icmp_raw = b'\x08'
        ipv4_probe = ip / Raw(load=icmp_raw)
        t = AsyncSniffer(iface=interface)
        t.start()
        send(ipv4_probe)
        time.sleep(timeout * 4)
        pkts = t.stop()
        for pkt in pkts:
            # first, let's check the source and the destination IP
            if IP in pkt and pkt[IP].src == dst_host and pkt[IP].dst == ip.src:
                if ICMP in pkt and pkt[ICMP].type == 0x00 and pkt[
                        ICMP].chksum == None:
                    match = MATCH_MEDIUM
                    stack_name = 'Nucleus Net'
                    break

    return (stack_name, match)
예제 #22
0
else: 
    sMynet=sys.argv[1]
    sInt=sys.argv[2]

## Show info
iAddressCount = len([str(ip) for ip in ipaddress.IPv4Network(sMynet)])
print('[!] Scanning ' + sMynet + ' ('+str(iAddressCount)+' IP\'s)')
## Start sniffer in separate thread
arrResponses = []
oSniffer = AsyncSniffer(prn=lambda x: arrResponses.append(x), store=False, filter="arp and arp[6:2] == 2")
oSniffer.start()
## Send all packets
sendp(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(pdst = sMynet), iface = sInt, verbose = False)
print('[!] Waiting 2 seconds for the responses to find their way back')
time.sleep(2)
oSniffer.stop()
## Parsing
arrLiveSystems = []
arrMacList = []
for arrReceived in arrResponses:
    sMac = arrReceived.hwsrc
    sIP = arrReceived.psrc
    if sMac not in arrMacList:
        print(arrReceived.summary().replace(' / Padding',''))
        arrMacList.append(sMac)
        arrLiveSystems.append((sMac,sIP))
if len(arrLiveSystems) == 0: print('[-] No systems found for network ' + sMynet)
else: print('[+] Found ' + str(len(arrLiveSystems)) + ' IP address(es) on network ' + sMynet)
if len(sys.argv) != 3: input('Press enter to close')

'''
예제 #23
0
class Relay(flx.Component):

    txt_packet = ''
    prev_idx = 0
    curr_idx = 0
    summary_txt = ''
    detail_txt = ''
    hexdump_txt = ''

    def init(self):
        self.sniffer = None
        self.refresh()

    def sniff_start(self, ifname):
        p_list[:] = []
        self.summary_txt = ''
        self.detail_txt = ''
        self.hexdump_txt = ''
        self.prev_idx = 0
        self.curr_idx = 0
        self.sniffer = AsyncSniffer(iface=ifname,
                                    prn=lambda x: p_list.append(x))
        self.sniffer.start()

    def sniff_stop(self):
        if self.sniffer and self.sniffer.running:
            self.sniffer.stop()

    def print_packet(self):
        self.curr_idx = len(p_list)
        self.summary_txt = ''
        for i in range(self.prev_idx, self.curr_idx):
            self.summary_txt += p_list[i].summary()
            if i is not self.curr_idx - 1:
                self.summary_txt += '\n'
        self.prev_idx = self.curr_idx
        return self.summary_txt

    def pkt_detail(self, idx):
        if p_list[idx]:
            self.detail_txt = p_list[idx].show(dump=True)
        else:
            self.detail_txt = ''

    def pkt_hexdump(self, idx):
        if p_list[idx]:
            self.hexdump_txt = hexdump(p_list[idx], dump=True)
        else:
            self.hexdump_txt = ''

    def packet_info(self):
        self.curr_idx = len(p_list)
        for i in range(self.prev_idx, self.curr_idx):
            self.emit(
                'packet_info',
                dict(pkt_summary=p_list[i].summary(),
                     pkt_detail=p_list[i].show(dump=True),
                     pkt_hex=hexdump(p_list[i], dump=True)))
        self.prev_idx = self.curr_idx

    def refresh(self):
        self.packet_info()
        asyncio.get_event_loop().call_later(0.5, self.refresh)
예제 #24
0
class Sniffer:
    """
    Constructs the sniffer object; notably takes a config
    keyword to control what mode to run in
    'testing' ignores ssh traffic
    """
    def __init__(
        self,
        config="base",
        openPorts=None,
        whitelist=None,
        portWhitelist=None,
        honeypotIP=None,
        managementIPs=None,
        port_scan_window=None,
        port_scan_sensitivity=None,
        databaser=None,
    ):

        self.config = config
        self.openPorts = [] if openPorts is None else openPorts
        self.whitelist = [] if whitelist is None else whitelist
        self.honeypotIP = honeypotIP
        self.portWhitelist = [] if portWhitelist is None else portWhitelist
        self.managementIPs = managementIPs
        self.scan_window = port_scan_window
        self.scan_sensitivity = port_scan_sensitivity
        self.db = databaser
        # used to detect port scans
        self.portScanTimeout = None
        # also used to detect port scans
        self.PS_RECORD = dict()
        # set used for testing convenience
        self.RECORD = dict()
        # Hash used to tell if we properly updated Sniffer class;
        # there is probably a better way of making this hash
        self.currentHash = hash(self.config)
        self.currentHash += hash(tuple(self.openPorts))
        self.currentHash += hash(tuple(self.whitelist))
        self.currentHash += hash(honeypotIP)
        self.currentHash += hash(tuple(managementIPs))

    def start(self):
        """
        Runs the thread, begins sniffing with given config
        """
        print("Starting async sniffer")
        # building the base filter
        fltr = "not src host {} ".format(self.honeypotIP)
        # adding a variable number of management ips
        for ip in self.managementIPs:
            fltr += "and not host {} ".format(ip)
        # adding things from the port list
        for port in self.portWhitelist:
            fltr += "and not dst port {} ".format(port)

        # here's where the packet detection starts

        if self.config == "testing":
            # this ignores the ssh spam you get when sending
            # packets between two ssh terminals
            fltr = fltr + " and not (src port ssh or dst port ssh)"
        elif self.config == "base":
            # this above filter ignores the ssh spam you get when sending packets
            #  between two ssh terminals - TODO: TAKE THIS OUT IN PROD
            fltr = fltr + " and not (src port ssh or dst port ssh)"
        elif self.config == "onlyUDP":
            # this last config option is used in testing
            fltr = "udp"

        self.sniffer = AsyncSniffer(filter=fltr,
                                    prn=self.save_packet,
                                    store=False)

        if not self.sniffer:
            raise Exception("Async sniffer not initialized")

        self.sniffer.start()

    def stop(self):
        """
        Attempts to stop the async sniffer
        """
        if not self.sniffer or not self.sniffer.running:
            raise Exception("Async sniffer not initialized")
        self.sniffer.stop()

    def configUpdate(
        self,
        openPorts=None,
        whitelist=None,
        portWhitelist=None,
        honeypotIP=None,
        managementIPs=None,
        port_scan_window=None,
        port_scan_sensitivity=None,
    ):
        """
        Updates configuration options during runtime
        """
        print("Async sniffer updated")
        self.running = False
        self.openPorts = [] if openPorts is None else openPorts
        self.whitelist = [] if whitelist is None else whitelist
        self.portWhitelist = [] if portWhitelist is None else portWhitelist
        self.honeypotIP = honeypotIP
        self.managementIPs = managementIPs
        self.scan_window = port_scan_window
        self.scan_sensitivity = port_scan_sensitivity

        # updates hash
        self.currentHash = hash(self.config)
        self.currentHash += hash(tuple(self.openPorts))
        self.currentHash += hash(tuple(self.whitelist))
        self.currentHash += hash(honeypotIP)
        self.currentHash += hash(tuple(managementIPs))
        self.currentHash += hash(
            tuple([self.scan_window, self.scan_sensitivity]))

        # restart's Sniffer
        try:
            self.stop()
        except Scapy_Exception as ex:
            print("Sniffer did not finish setting up before teardown: ",
                  str(ex))
        self.start()

    def save_packet(self, packet):
        """
        Function for recording a packet during sniff runtime
        packet = the packet passed through the sniff function
        """
        # TODO: make this work with layer 2, for now just skip filtering those packets
        if not packet.haslayer("IP"):
            return

        # timestamp used for port scan detection
        currentTime = int(datetime.now().timestamp())
        if self.portScanTimeout is None:
            self.portScanTimeout = currentTime

        # how to tell if we need to reset our port scan record
        if currentTime > self.portScanTimeout + self.scan_window:
            self.portScanTimeout = currentTime
            self.PS_RECORD = dict()

        # A bunch of packet data, collected to be stored
        sourceMAC = packet.src
        destMAC = packet.dst
        ipLayer = packet.getlayer("IP")
        # IP where this came from
        srcIP = ipLayer.src
        dstIP = ipLayer.dst
        destPort = ipLayer.dport if hasattr(ipLayer, "dport") else None
        srcPort = ipLayer.sport if hasattr(ipLayer, "sport") else None

        if (not ipLayer.haslayer("TCP") and not ipLayer.haslayer("UDP")
                and not ipLayer.haslayer("ICMP")):
            return

        # Whitelist check
        if srcIP not in self.whitelist:
            # Testing config - does not utilize a database
            # isTest = self.config == "onlyUDP" or self.config == "testing"

            trafficType = ("TCP" if ipLayer.haslayer("TCP") else
                           "UDP" if ipLayer.haslayer("UDP") else
                           "ICMP" if ipLayer.haslayer("ICMP") else "Other")

            # Log Entry object we're saving
            log = LogEntry(
                srcPort,
                srcIP,
                sourceMAC,
                destPort,
                dstIP,
                destMAC,
                trafficType,
                ipLayer.len,
                destPort in self.openPorts,
            )

            # self.RECORD is where we save logs for easy testing
            if srcIP in self.RECORD.keys():
                self.RECORD[srcIP].append(log)
            else:
                self.RECORD[srcIP] = [log]

            # saving the database ID in case of port scan detection
            if self.config == "base":
                dbID = self.db.save(log)
            else:
                return

            # self.PS_RECORD is a separate dictionary used for port scan detection
            if srcIP not in self.PS_RECORD.keys():
                self.PS_RECORD[srcIP] = dict()
                self.PS_RECORD[srcIP][log.destPortNumber] = dbID
            else:
                self.PS_RECORD[srcIP][log.destPortNumber] = dbID

                # Sending out the port scan alert
                if len(self.PS_RECORD[srcIP]) > self.scan_sensitivity:
                    self.db.alert(
                        Alert(
                            variant="alert",
                            message="Port scan detected from IP {}".format(
                                srcIP),
                            references=list(self.PS_RECORD[srcIP].values()),
                        ))
                    self.PS_RECORD[srcIP] = dict()