def _process_packet_helper(self, pkt): if sc.ARP in pkt: return self._process_arp(pkt) if sc.DHCP in pkt: return self._process_dhcp(pkt) # SYN-ACK response to SYN scans if sc.TCP in pkt and pkt[sc.TCP].flags == 'SA' and sc.IP in pkt: tcp_layer = pkt[sc.TCP] if tcp_layer.dport == SYN_SCAN_SOURCE_PORT and tcp_layer.ack == SYN_SCAN_SEQ_NUM + 1: return self._process_syn_scan(pkt) # Must have Ether frame and IP frame. if not (sc.Ether in pkt and sc.IP in pkt): return src_mac = pkt[sc.Ether].src dst_mac = pkt[sc.Ether].dst src_oui = utils.get_oui(src_mac) dst_oui = utils.get_oui(dst_mac) # Include only devices for internal testing (if set) if utils.TEST_OUI_LIST: if not (src_oui in utils.TEST_OUI_LIST or dst_oui in utils.TEST_OUI_LIST): return # Ignore traffic to and from this host's IP if self._host_state.host_ip in (pkt[sc.IP].src, pkt[sc.IP].dst): return # DNS if sc.DNS in pkt: self._process_dns(pkt) # Commented out the following. We want to see traffic between device and gateway. # # Ignore traffic to and from the gateway's IP # if self._host_state.gateway_ip in (pkt[sc.IP].src, pkt[sc.IP].dst): # return # TCP/UDP if sc.TCP in pkt: protocol = 'tcp' elif sc.UDP in pkt: protocol = 'udp' else: return return self._process_tcp_udp_flow(pkt, protocol)
def get_device_list(): """ Returns a list of devices; constantly changes. """ # Maps device_id -> {device_id, device_vendor, netdisco_name} output_dict = {} host_state = get_host_state() if host_state is None: return json.dumps(output_dict) # Get device vendor with host_state.lock: for (ip, mac) in host_state.ip_mac_dict.items(): # Never include the gateway if ip == host_state.gateway_ip: continue device_id = utils.get_device_id(mac, host_state) device_vendor = oui_parser.get_vendor(utils.get_oui(mac)) output_dict.setdefault( device_id, { 'device_id': device_id, 'device_ip': ip, 'device_vendor': device_vendor, 'netdisco_name': '', 'dhcp_name': '', 'is_inspected': device_id in host_state.device_whitelist }) # Fill out netdisco_name with host_state.lock: for (device_id, device_info_list) in host_state.pending_netdisco_dict.items(): if device_id in output_dict: output_dict[device_id]['netdisco_name'] = device_info_list # Reset pending dict host_state.pending_netdisco_dict = {} # Fill out dhcp_name with host_state.lock: for (device_id, dhcp_name) in host_state.pending_dhcp_dict.items(): if device_id in output_dict: output_dict[device_id]['dhcp_name'] = dhcp_name # Reset pending dict host_state.pending_dhcp_dict = {} return json.dumps(output_dict, indent=2)
def _process_packet_helper(self, pkt): if sc.ARP in pkt: return self._process_arp(pkt) if sc.DHCP in pkt: return self._process_dhcp(pkt) # Must have Ether frame and IP frame. if not (sc.Ether in pkt and sc.IP in pkt): return src_mac = pkt[sc.Ether].src dst_mac = pkt[sc.Ether].dst src_oui = utils.get_oui(src_mac) dst_oui = utils.get_oui(dst_mac) # Include only devices for internal testing (if set) if utils.TEST_OUI_LIST: if not (src_oui in utils.TEST_OUI_LIST or dst_oui in utils.TEST_OUI_LIST): return # Ignore traffic to and from this host's IP if self._host_state.host_ip in (pkt[sc.IP].src, pkt[sc.IP].dst): return # DNS if sc.DNS in pkt: self._process_dns(pkt) # Ignore traffic to and from the gateway's IP if self._host_state.gateway_ip in (pkt[sc.IP].src, pkt[sc.IP].dst): return # Get gateway's MAC try: with self._host_state.lock: gateway_mac = self._host_state.ip_mac_dict[ self._host_state.gateway_ip ] except KeyError: return # Communication must be between this host's MAC (acting as a gateway) # and a non-gateway device host_mac = self._host_state.host_mac this_host_as_gateway = ( (src_mac == host_mac and dst_mac != gateway_mac) or (dst_mac == host_mac and src_mac != gateway_mac) ) if not this_host_as_gateway: return # TCP/UDP if sc.TCP in pkt: protocol = 'tcp' elif sc.UDP in pkt: protocol = 'udp' else: return return self._process_tcp_udp_flow(pkt, protocol)
def _arp_spoof_loop(self): prev_ip_mac_dict = None while True: if not self._host_state.is_inspecting(): time.sleep(2) continue time.sleep(1) with self._lock: if not self._active: return with self._host_state.lock: if not self._host_state.has_consent: utils.log('[ARP Spoof] No consent; no spoofing.') continue # Get ARP cache ip_mac_dict = self._host_state.get_ip_mac_dict_copy() gateway_ip = self._host_state.gateway_ip if str(ip_mac_dict) != str(prev_ip_mac_dict): prev_ip_mac_dict = ip_mac_dict utils.log('[ARP Spoof] Cache:', ip_mac_dict) utils.log('[ARP Spoof] Whitelist:', self._host_state.device_whitelist) # Get gateway MAC addr try: gateway_mac = ip_mac_dict[gateway_ip] except KeyError: continue whitelist_ip_mac = [] # Add gateway whitelist_ip_mac.append((gateway_ip, gateway_mac)) # Build device-to-device whitelist for ip, mac in ip_mac_dict.items(): device_id = utils.get_device_id(mac, self._host_state) if device_id not in self._host_state.device_whitelist: utils.log('[ARP Spoof] Ignore:', ip, mac) continue whitelist_ip_mac.append((ip, mac)) # Spoof individual devices on the network. for (victim_ip, victim_mac) in ip_mac_dict.items(): if victim_ip == gateway_ip: continue # Check against whitelist. victim_device_id = \ utils.get_device_id(victim_mac, self._host_state) if victim_device_id not in self._host_state.device_whitelist: utils.log('[ARP Spoof] Ignore:', victim_ip, victim_mac) continue if utils.TEST_OUI_LIST: victim_mac_oui = utils.get_oui(victim_mac) if victim_mac_oui not in utils.TEST_OUI_LIST: continue utils.safe_run(self._arp_spoof, args=(victim_mac, victim_ip, whitelist_ip_mac)) with self._lock: if not self._active: return time.sleep(max(MIN_ARP_SPOOF_INTERVAL, 2.0 / len(ip_mac_dict)))
def _prepare_upload_data(self): """Returns (window_duration, a dictionary of data to post).""" window_duration = time.time() - self._last_upload_ts # Remove all pending tasks with self._host_state.lock: dns_dict = self._host_state.pending_dns_dict dhcp_dict = self._host_state.pending_dhcp_dict resolver_dict = self._host_state.pending_resolver_dict flow_dict = self._host_state.pending_flow_dict ua_dict = self._host_state.pending_ua_dict ip_mac_dict = self._host_state.ip_mac_dict tls_dict_list = self._host_state.pending_tls_dict_list netdisco_dict = self._host_state.pending_netdisco_dict self._clear_host_state_pending_data() self._last_upload_ts = time.time() # Turn IP -> MAC dict into device_id -> (ip, device_oui) dict, ignoring # gateway's IP. device_dict = {} for (ip, mac) in ip_mac_dict.iteritems(): # Never include the gateway if ip == self._host_state.gateway_ip: continue device_id = utils.get_device_id(mac, self._host_state) oui = utils.get_oui(mac) device_dict[device_id] = (ip, oui) # Process flow_stats for flow_key in flow_dict: flow_stats = flow_dict[flow_key] # Compute unique byte count during this window using seq number for direction in ('inbound', 'outbound'): flow_stats[direction + '_tcp_seq_range'] = get_seq_diff( flow_stats[direction + '_tcp_seq_min_max'] ) flow_stats[direction + '_tcp_ack_range'] = get_seq_diff( flow_stats[direction + '_tcp_ack_min_max'] ) # We use the original byte count or the sequence number as the # final byte count (whichever is larger), although we should # note the caveats of using TCP seq numbers to estimate flow # size in packet_processor.py. flow_stats[direction + '_byte_count'] = max( flow_stats[direction + '_byte_count'], flow_stats[direction + '_tcp_seq_range'] ) # Fill in missing byte count (e.g., due to failure of ARP spoofing) if flow_stats['inbound_byte_count'] == 0: outbound_seq_diff = flow_stats['outbound_tcp_ack_range'] if outbound_seq_diff: flow_stats['inbound_byte_count'] = outbound_seq_diff if flow_stats['outbound_byte_count'] == 0: inbound_seq_diff = flow_stats['inbound_tcp_ack_range'] if inbound_seq_diff: flow_stats['outbound_byte_count'] = inbound_seq_diff # Keep only the byte count fields flow_dict[flow_key] = { 'inbound_byte_count': flow_stats['inbound_byte_count'], 'outbound_byte_count': flow_stats['outbound_byte_count'], 'syn_originator': flow_stats['syn_originator'] } return (window_duration, { 'dns_dict': jsonify_dict(dns_dict), 'flow_dict': jsonify_dict(flow_dict), 'device_dict': jsonify_dict(device_dict), 'ua_dict': jsonify_dict(ua_dict), 'dhcp_dict': jsonify_dict(dhcp_dict), 'resolver_dict': jsonify_dict(resolver_dict), 'client_version': self._host_state.client_version, 'tls_dict_list': json.dumps(tls_dict_list), 'netdisco_dict': jsonify_dict(netdisco_dict), 'duration': str(window_duration), 'client_ts': str(int(time.time())) })