def _setup(cls): Configuration.setup(cls) IPSResponse.setup(cls, Log) cls.set_proxy_callback( func=Inspect.portscan ) # this will get called after parsing is complete. Log.notice(f'{cls.__name__} initialization complete.')
def _pre_check(self, nfqueue): if (not self.ddos_engine_enabled and nfqueue.get_mark() == IP_PROXY_DROP): Log.debug( 'packet fast dropped from ip proxy | ddos engine disabled') nfqueue.drop() elif (not self.inspection_enabled): self.forward_packet(nfqueue) else: return True # marking for inspection
def _threshhold_exceeded(self, initial, count): protocol_src_limit = self._IPS.connection_limits[self._packet.protocol] elapsed_time = self._packet.timestamp - initial if (elapsed_time < 2): return False connections_per_second = count / elapsed_time if (connections_per_second < protocol_src_limit): return False # host is now considered active DDOS Log.debug(f'CPS: {connections_per_second}') return True
def _threshhold_exceeded(self, tracked_ip, packet): protocol_src_limit = self._IPS.connection_limits[packet.protocol] elapsed_time = packet.timestamp - tracked_ip['initial'] if (elapsed_time < 2): return False if (tracked_ip['count'] / elapsed_time < protocol_src_limit): return False # host is now considered active DDOS Log.debug('CPS: {}'.format(tracked_ip['count'] / elapsed_time)) return True
def _threshhold_exceeded(self, tracked_ip, packet): elapsed_time = packet.timestamp - tracked_ip['initial'] if (elapsed_time < 2): return False protocol_src_limit = self._IPS.connection_limits[packet.protocol] if (tracked_ip['count'] / elapsed_time < protocol_src_limit): return False # host is now marked as engaging in active d/dos attack. Log.debug('CPS: {}'.format(tracked_ip['count'] / elapsed_time)) return True
def _ddos_inspect(self, packet): ddos = self.ddos_tracker[packet.protocol] with ddos.lock: if not self._ddos_detected(ddos.tracker, packet): return if (self._IPS.ids_mode): Log.log(packet, IPS.LOGGED, engine=IPS.DDOS) elif (self._IPS.ddos_prevention): IPTableManager.proxy_add_rule(packet.conn.tracked_ip, table='mangle', chain='IPS') Log.log(packet, IPS.FILTERED, engine=IPS.DDOS)
def _ddos_timeout(self, tracked_ip, ddos_tracker): # if marked as an active ddos host, will timeout the conn # since we will be dropping all subsequent conn attempts at the kernel level if self.active_ddos_hosts.pop(tracked_ip, None): return 'break' # if tracked ip hasnt been seen for 10 seconds, it will be removed from the ddos tracker and thread will close tracked_connection = ddos_tracker.get(tracked_ip, None) last_seen = tracked_connection.get('last_seen') if (time.time() - last_seen >= 10): ddos_tracker.pop(tracked_ip) Log.debug(f'DDOS TIMED OUT CONN: {tracked_ip}') return 'break' return 5.1
def _ddos_inspect(self): ddos = self.ddos_tracker.get(self._packet.protocol) with ddos['lock']: if not self._ddos_detected(ddos['tracker']): return # this is to supress log entries for ddos hosts that are being detected by the engine, but not blocked # this behavior would only happen in informational logging mode without block enabled. if self._recently_detected(self._packet.conn.tracked_ip): return if (self._IPS.ddos_prevention): IPTableManager.proxy_add_rule(self._packet.conn.tracked_ip, table='mangle', chain='IPS') # TODO: add entry for ids mode Log.log(self._packet, engine=IPS.DDOS)
def _ddos_detected(self, ddos_tracker, packet): tracked_ip = ddos_tracker.get(packet.conn.tracked_ip, None) if (not tracked_ip or fast_time() - tracked_ip['last_seen'] > 15): self._add_to_tracker(ddos_tracker, packet, engine=IPS.DDOS) else: tracked_ip['count'] += 1 tracked_ip['last_seen'] = packet.timestamp # if conn limit exceeded and host is not already marked, returns active ddos and add ip to tracker if self._threshhold_exceeded(tracked_ip, packet): Log.debug(f'ACTIVE BLOCK: {packet.conn.tracked_ip}') # this is to supress log entries for ddos hosts that are being detected by the engine since there is a delay # between detection and kernel offload or some packets are already in queue if (packet.conn.tracked_ip not in self._IPS.fw_rules): self._IPS.fw_rules[ packet.conn.tracked_ip] = packet.timestamp return True return False
def _portscan_inspect(self, IPS_IDS, packet): pscan = self.pscan_tracker[packet.protocol] with pscan.lock: initial_block, active_scanner, pre_detection_logging = self._portscan_detect( pscan.tracker, packet) if (not active_scanner): Log.debug( f'PROXY ACCEPT | {packet.src_ip}:{packet.src_port} > {packet.dst_ip}:{packet.dst_port}.' ) IPS_IDS.forward_packet(packet.nfqueue) return if (IPS_IDS.ids_mode): IPS_IDS.forward_packet(packet.nfqueue) block_status = IPS.LOGGED elif (IPS_IDS.portscan_prevention): packet.nfqueue.drop() # if rejection is enabled on top of prevention port uncreachable packets will be sent back to the scanner. if (IPS_IDS.portscan_reject): self._portscan_reject(pre_detection_logging, packet, initial_block) # if initial block is not set then the current host has already been effectively blocked and does not need # to do anything beyond this point. if (not initial_block): return block_status = self._get_block_status(pre_detection_logging, packet.protocol) Log.debug( f'PROXY INITIAL SCANNER | {block_status.name} | {packet.src_ip}:{packet.src_port} > {packet.dst_ip}:{packet.dst_port}.' ) # NOTE: i think this is stupid. this would effectivly block all portscan logging while passive blocking is # active, right???? if that is the case, we need to figure out a different way to deal with this. i think this # was to ensure ddos wasnt logged as portscan first, but this doesnt soudn liek a good way to do this anymore. if (not IPS_IDS.fw_rules): scan_info = IPS_SCAN_RESULTS(initial_block, active_scanner, block_status) Log.log(packet, scan_info, engine=IPS.PORTSCAN) else: # NOTE: for testing purposes only Log.debug( 'ACTIVE DDOS WHEN ATTEMPTING TO LOG PORTSCAN, LOG HAULTED.')
def _portscan_inspect(self, packet): # TODO: optimize _IPS reference please! :) pscan = self.pscan_tracker[packet.protocol] with pscan.lock: initial_block, active_scanner, pre_detection_logging = self._portscan_detect( pscan.tracker, packet) if (not active_scanner): Log.debug( f'PROXY ACCEPT | {packet.src_ip}:{packet.src_port} > {packet.dst_ip}:{packet.dst_port}.' ) self._IPS.forward_packet(packet.nfqueue) return if (self._IPS.ids_mode): self._IPS.forward_packet(packet.nfqueue) block_status = IPS.LOGGED elif (self._IPS.portscan_prevention): packet.nfqueue.drop() # if rejection is enabled on top of prevention port uncreachable packets will be sent back to the scanner. if (self._IPS.portscan_reject): self._portscan_reject(pre_detection_logging, packet, initial_block) # if initial block is not set then the current host has already been effectively blocked and does not need # to do anything beyond this point. if (not initial_block): return block_status = self._get_block_status(pre_detection_logging, packet.protocol) Log.debug( f'PROXY INITIAL SCANNER | {block_status.name} | {packet.src_ip}:{packet.src_port} > {packet.dst_ip}:{packet.dst_port}.' ) if (not self._IPS.fw_rules): scan_info = IPS_SCAN_RESULTS(initial_block, active_scanner, block_status) Log.log(packet, scan_info, engine=IPS.PORTSCAN) else: # NOTE: for testing purposes only Log.debug( 'ACTIVE DDOS WHEN ATTEMPTING TO LOG PORTSCAN, LOG HAULTED.')
def _portscan_inspect(self): pscan = self.pscan_tracker.get(self._packet.protocol) with pscan['lock']: initial_block, active_scanner, pre_detection_logging = self._portscan_detect( pscan['tracker']) if (not active_scanner): Log.informational( f'PROXY ACCEPT | {self._packet.src_ip}:{self._packet.src_port} > {self._packet.dst_ip}:{self._packet.dst_port}.' ) self._IPS.forward_packet(self._packet.nfqueue) return if (self._IPS.portscan_prevention): if (self._IPS.ids_mode): self._IPS.forward_packet(self._packet.nfqueue) block_status = IPS.LOGGED else: self._packet.nfqueue.drop() if (self._IPS.portscan_reject): self._portscan_reject(pre_detection_logging) if (pre_detection_logging): block_status = self._get_block_status(pre_detection_logging) Log.informational( f'PROXY ACTIVE SCANNER DROP {self._packet.src_ip}:{self._packet.src_port} > {self._packet.dst_ip}:{self._packet.dst_port}.' ) if (not self.active_ddos): scan_info = IPS_SCAN_RESULTS(initial_block, active_scanner, block_status) Log.log(self._packet, scan_info, engine=IPS.PORTSCAN) else: # NOTE: for testing purposes only Log.informational( 'ACTIVE DDOS WHEN ATTEMPTING TO LOG PORTSCAN, LOG HAULTED.' )
# to the reject message actually sent def _portscan_reject(self, pre_detection_logging, packet, initial_block): if (not initial_block): self._IPSResponse.prepare_and_send(packet) else: IPSResponse = self._IPSResponse if (packet.protocol is PROTO.TCP): for port, seq_num in pre_detection_logging.items(): IPSResponse.prepare_and_send( copy(packet).tcp_override(port, seq_num)) elif (packet.protocol is PROTO.UDP): for port, icmp_payload in pre_detection_logging.items(): IPSResponse.prepare_and_send( copy(packet).udp_override(icmp_payload)) def _get_block_status(self, pre_detection_logging, protocol): open_ports = self._IPS.open_ports for port in pre_detection_logging: if port in open_ports[protocol]: return IPS.MISSED return IPS.BLOCKED if __name__ == '__main__': Log.run(name=LOG_NAME) IPS_IDS.run(Log, q_num=2)
# to the reject message actually sent def _portscan_reject(self, pre_detection_logging, packet, initial_block): if (not initial_block): self._IPSResponse.prepare_and_send(packet) else: IPSResponse = self._IPSResponse if (packet.protocol is PROTO.TCP): for port, seq_num in pre_detection_logging.items(): IPSResponse.prepare_and_send( copy(packet).tcp_override(port, seq_num)) elif (packet.protocol is PROTO.UDP): for port, icmp_payload in pre_detection_logging.items(): IPSResponse.prepare_and_send( copy(packet).udp_override(icmp_payload)) def _get_block_status(self, pre_detection_logging, protocol): open_ports = self._IPS.open_ports for port in pre_detection_logging: if port in open_ports[protocol]: return IPS.MISSED return IPS.BLOCKED if __name__ == '__main__': Log.run(name=LOG_NAME, verbose=VERBOSE, root=ROOT) IPS_IDS.run(Log, q_num=2)