📄 portscan.c
字号:
/**************************************************************************** * * Copyright (C) 2004-2007 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ /*** @file portscan.c**** @author Daniel Roelker <droelker@sourcefire.com>**** @brief Detect portscans**** NOTES** - Marc Norton and Jeremy Hewlett were involved in the requirements and** design of this portscan detection engine.** - Thanks to Judy Novak for her suggestion to log open ports** on hosts that are portscanned. This idea makes portscan a lot more** useful for analysts.**** The philosophy of portscan detection that we use is based on a generic** network attack methodology: reconnaissance, network service enumeration,** and service exploitation.**** The reconnaissance phase determines what types of network protocols and** services that a host supports. This is the traditional phase where a** portscan occurs. An important requirement of this phase is that an** attacker does not already know what protocols and services are supported** by the destination host. If an attacker does know what services are** open on the destination host then there is no need for this phase.** Because of this requirement, we assume that if an attacker engages in this** phase that they do not have prior knowledege to what services are open.** So, the attacker will need to query the ports or protocols they are** interested in. Most or at least some of these queries will be negative** and take the form of either an invalid response (TCP RSTs, ICMP** unreachables) or no response (in which case the host is firewalled or** filtered). We detect portscans from these negative queries.** ** The primary goal of this portscan detection engine is to catch nmap and** variant scanners. The engine tracks connection attempts on TCP, UDP,** ICMP, and IP Protocols. If there is a valid response, the connection** is marked as valid. If there is no response or a invalid response** (TCP RST), then we track these attempts separately, so we know the** number of invalid responses and the number of connection attempts that** generated no response. These two values differentiate between a** normal scan and a filtered scan.**** We detect four different scan types, and each scan type has its own** negative query characteristics. This is how we determine what type** of scan we are seeing. The different scans are:**** - Portscan** - Decoy Portscan** - Distributed Portscan** - Portsweep**** Portscan: A portscan is a basic one host to one host scan where** multiple ports are scanned on the destination host. We detect these** scans by looking for a low number of hosts that contacted the** destination host and a high number of unique ports and a high number** of invalid responses or connections.**** Distributed Portscan: A distributed portscan occurs when many hosts** connect to a single destination host and multiple ports are scanned** on the destination host. We detect these scans by looking for a high** number of hosts that contacted the destination host and a high number** of unique ports with a high number of invalid responses or connections.**** Decoy Portscan: A decoy portscan is a variation on a distributed** portscan, the difference being that a decoy portscan connects to a** single port multiple times. This shows up in the unqiue port count that** is tracked. There's still many hosts connecting to the destination host.**** Portsweep: A portsweep is a basic one host to many host scan where** one to a few ports are scanned on each host. We detect these scans by** looking at src hosts for a high number of contacted hosts and a low** number of unique ports with a high number of invalid responses or** connections.**** Each of these scans can also be detected as a filtered portscan, or a** portscan where there wasn't invalid responses and the responses have** been firewalled in some way.** */#include <stdlib.h>#include <string.h>#include <sys/types.h>#ifndef WIN32 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>#endif /* !WIN32 */#include "decode.h"#include "portscan.h"#include "packet_time.h"#include "sfxhash.h"#include "ipobj.h"#include "flow.h"#include "stream_api.h"#ifdef SUP_IP6#define CLEARED &cleared#else#define CLEARED cleared#endiftypedef struct s_PS_INIT{ int detect_scans; int detect_scan_type; int sense_level; int proto_cnt; IPSET *ignore_scanners; IPSET *ignore_scanned; IPSET *watch_ip;} PS_INIT;typedef struct s_PS_HASH_KEY{ ip_t scanner; ip_t scanned;} PS_HASH_KEY;typedef struct s_PS_ALERT_CONF{ short connection_count; short priority_count; short u_ip_count; short u_port_count;} PS_ALERT_CONF;static int g_ps_tracker_size;static PS_INIT g_ps_init;static SFXHASH *g_hash;extern int g_include_midstream;/*** Scanning configurations. This is where we configure what the thresholds** are for the different types of scans, protocols, and sense levels. If** you want to tweak the sense levels, change the values here.*//*** TCP alert configurations*/static PS_ALERT_CONF g_tcp_low_ps = {0,5,25,5};static PS_ALERT_CONF g_tcp_low_decoy_ps = {0,15,50,30};static PS_ALERT_CONF g_tcp_low_sweep = {0,5,5,15};static PS_ALERT_CONF g_tcp_low_dist_ps = {0,15,50,15};static PS_ALERT_CONF g_tcp_med_ps = {200,10,60,15};static PS_ALERT_CONF g_tcp_med_decoy_ps = {200,30,120,60};static PS_ALERT_CONF g_tcp_med_sweep = {30,7,7,10};static PS_ALERT_CONF g_tcp_med_dist_ps = {200,30,120,30};static PS_ALERT_CONF g_tcp_hi_ps = {200,5,100,10};static PS_ALERT_CONF g_tcp_hi_decoy_ps = {200,7,200,60};static PS_ALERT_CONF g_tcp_hi_sweep = {30,3,3,10};static PS_ALERT_CONF g_tcp_hi_dist_ps = {200,5,200,10};/*** UDP alert configurations*/static PS_ALERT_CONF g_udp_low_ps = {0,5,25,5};static PS_ALERT_CONF g_udp_low_decoy_ps = {0,15,50,30};static PS_ALERT_CONF g_udp_low_sweep = {0,5,5,15};static PS_ALERT_CONF g_udp_low_dist_ps = {0,15,50,15};static PS_ALERT_CONF g_udp_med_ps = {200,10,60,15};static PS_ALERT_CONF g_udp_med_decoy_ps = {200,30,120,60};static PS_ALERT_CONF g_udp_med_sweep = {30,5,5,20};static PS_ALERT_CONF g_udp_med_dist_ps = {200,30,120,30};static PS_ALERT_CONF g_udp_hi_ps = {200,3,100,10};static PS_ALERT_CONF g_udp_hi_decoy_ps = {200,7,200,60};static PS_ALERT_CONF g_udp_hi_sweep = {30,3,3,10};static PS_ALERT_CONF g_udp_hi_dist_ps = {200,3,200,10};/*** IP Protocol alert configurations*/static PS_ALERT_CONF g_ip_low_ps = {0,10,10,50};static PS_ALERT_CONF g_ip_low_decoy_ps = {0,40,50,25};static PS_ALERT_CONF g_ip_low_sweep = {0,10,10,10};static PS_ALERT_CONF g_ip_low_dist_ps = {0,15,25,50};static PS_ALERT_CONF g_ip_med_ps = {200,10,10,50};static PS_ALERT_CONF g_ip_med_decoy_ps = {200,40,50,25};static PS_ALERT_CONF g_ip_med_sweep = {30,10,10,10};static PS_ALERT_CONF g_ip_med_dist_ps = {200,15,25,50};static PS_ALERT_CONF g_ip_hi_ps = {200,3,3,10};static PS_ALERT_CONF g_ip_hi_decoy_ps = {200,7,15,5};static PS_ALERT_CONF g_ip_hi_sweep = {30,3,3,7};static PS_ALERT_CONF g_ip_hi_dist_ps = {200,3,11,10};/*** ICMP alert configurations*/static PS_ALERT_CONF g_icmp_low_sweep = {0,5,5,5};static PS_ALERT_CONF g_icmp_med_sweep = {20,5,5,5};static PS_ALERT_CONF g_icmp_hi_sweep = {10,3,3,5};/*** NAME** ps_tracker_free::*//**** This function is passed into the hash algorithm, so that** we only reuse nodes that aren't priority nodes. We have to make** sure that we only track so many priority nodes, otherwise we could** have all priority nodes and not be able to allocate more.*/static int ps_tracker_free(void *key, void *data){ PS_TRACKER *tracker; int iCtr; time_t pkt_time; if(!key || !data) return 0; tracker = (PS_TRACKER *)data; if(!tracker->priority_node) return 0; /* ** Cycle through the protos to see if it's past the time. ** We only get here if we ARE a priority node. */ pkt_time = packet_timeofday(); for(iCtr = 0; iCtr < g_ps_init.proto_cnt; iCtr++) { if(tracker->proto[iCtr].window >= pkt_time) return 1; } return 0;}/*** NAME** ps_init::*//*** Initialize the portscan infrastructure. We check to make sure that** we have enough memory to support at least 100 nodes.** ** @return int** ** @retval -2 memcap is too low*/int ps_init(int detect_scans, int detect_scan_type, int sense_level, IPSET *scanner, IPSET *scanned, IPSET *watch, int memcap){ int proto_cnt = 0; int datasize; proto_cnt += ((detect_scans & PS_PROTO_TCP) ? 1 : 0); proto_cnt += ((detect_scans & PS_PROTO_UDP) ? 1 : 0); proto_cnt += ((detect_scans & PS_PROTO_ICMP) ? 1 : 0); proto_cnt += ((detect_scans & PS_PROTO_IP) ? 1 : 0); if(!proto_cnt) return -1; if(!(detect_scan_type & PS_TYPE_ALL)) return -1; if(sense_level < 1 || sense_level > 3) return -1; /* ** Set the datasize that the hash will be keeping track of. This ** changes dynamically based on the number of protocols that we are ** tracking. */ datasize = sizeof(PS_TRACKER) + (sizeof(PS_PROTO)*(proto_cnt - 1)); if(memcap <= 0 || memcap < (datasize * 100)) return -2; g_hash = sfxhash_new(50000, sizeof(PS_HASH_KEY), datasize, memcap, 1, ps_tracker_free, NULL, 1); if(!g_hash) return -1; g_ps_init.detect_scans = detect_scans; g_ps_init.detect_scan_type = detect_scan_type; g_ps_init.sense_level = sense_level; g_ps_init.ignore_scanners = scanner; g_ps_init.ignore_scanned = scanned; g_ps_init.watch_ip = watch; g_ps_tracker_size = datasize; return 0;}/*** NAME** ps_cleanup::*//**** Cleanup the portscan infrastructure.*/void ps_cleanup(){ if (g_hash) { sfxhash_delete(g_hash); g_hash = NULL; } g_ps_tracker_size = 0;}/*** NAME** ps_ignore_ip::*//**** Check scanner and scanned ips to see if we can filter them out.*/#ifdef SUP_IP6static int ps_ignore_ip(ip_p scanner, unsigned short scanner_port, ip_p scanned, unsigned short scanned_port)#elsestatic int ps_ignore_ip(unsigned long scanner, unsigned short scanner_port, unsigned long scanned, unsigned short scanned_port)#endif{ if(g_ps_init.ignore_scanners) {#ifdef SUP_IP6 if(sfip_family(scanner) == AF_INET6 && ipset_contains(g_ps_init.ignore_scanners, scanner, &scanner_port, IPV6_FAMILY)) return 1; else if(ipset_contains(g_ps_init.ignore_scanners, scanner, &scanner_port, IPV4_FAMILY)) return 1;#else if(ipset_contains(g_ps_init.ignore_scanners, &scanner, &scanner_port, IPV4_FAMILY)) return 1;#endif } if(g_ps_init.ignore_scanned) {#ifdef SUP_IP6 if(sfip_family(scanned) && ipset_contains(g_ps_init.ignore_scanned, scanned, &scanned_port, IPV6_FAMILY)) return 1; else if(ipset_contains(g_ps_init.ignore_scanned, scanned, &scanned_port, IPV4_FAMILY)) return 1;#else if(ipset_contains(g_ps_init.ignore_scanned, &scanned, &scanned_port, IPV4_FAMILY)) return 1;#endif } return 0;}/*** NAME** ps_filter_ignore::*//**** Check the incoming packet to decide whether portscan detection cares** about this packet. We try to ignore as many packets as possible.*/static int ps_filter_ignore(PS_PKT *ps_pkt){ Packet *p; FLOW *flow; int reverse_pkt = 0;#ifdef SUP_IP6 ip_p scanner, scanned;#else unsigned long scanner; unsigned long scanned;#endif p = (Packet *)ps_pkt->pkt; if(!IPH_IS_VALID(p)) return 1; if(p->tcph) { if(!(g_ps_init.detect_scans & PS_PROTO_TCP)) return 1; /* ** This is where we check all of snort's flags for different ** TCP session scenarios. The checks cover: ** ** - dropping packets in established sessions, but not the ** TWH packet. ** - dropping the SYN/ACK packet from the server on a valid ** connection (we'll catch the TWH later if it happens). */ /* ** Ignore packets that are already part of an established TCP ** stream. */ if(((p->packet_flags & (PKT_STREAM_EST | PKT_STREAM_TWH)) == PKT_STREAM_EST) && !(p->tcph->th_flags & TH_RST)) { return 1; } /* ** Ignore the server's initial response, unless it's to RST ** the connection. */ /* if(!(p->tcph->th_flags & TH_RST) && !(p->packet_flags & (PKT_STREAM_EST)) && (p->packet_flags & PKT_FROM_SERVER)) { return 1; } */ } else if(p->udph) { if(!(g_ps_init.detect_scans & PS_PROTO_UDP)) return 1; } else if(p->icmph) { if(p->icmph->type != ICMP_DEST_UNREACH && !(g_ps_init.detect_scans & PS_PROTO_ICMP)) { return 1; } } else { if(!(g_ps_init.detect_scans & PS_PROTO_IP)) return 1; } /* ** Check if the packet is reversed */ if((p->packet_flags & PKT_FROM_SERVER)) { reverse_pkt = 1; } else if(p->icmph && p->icmph->type == ICMP_DEST_UNREACH) { reverse_pkt = 1; } else if (p->udph && p->ssnptr && stream_api && stream_api->version >= STREAM_API_VERSION5) { if (stream_api->get_packet_direction(p) & PKT_FROM_SERVER) reverse_pkt = 1; } else if((p->udph || IPH_IS_VALID(p)) && p->flow) { flow = (FLOW *)p->flow; if(flow->stats.direction == FROM_RESPONDER) reverse_pkt = 1; }#ifdef SUP_IP6 scanner = GET_SRC_IP(p); scanned = GET_DST_IP(p);#else scanner = ntohl(p->iph->ip_src.s_addr); scanned = ntohl(p->iph->ip_dst.s_addr);#endif if(reverse_pkt) { if(ps_ignore_ip(scanned, p->dp, scanner, p->sp)) return 1; } else { if(ps_ignore_ip(scanner, p->sp, scanned, p->dp)) return 1; } ps_pkt->reverse_pkt = reverse_pkt; if(g_ps_init.watch_ip) {#ifdef SUP_IP6 if(sfip_family(scanner) == AF_INET6) { if(ipset_contains(g_ps_init.watch_ip, scanner, &(p->sp), IPV6_FAMILY)) return 0; if(ipset_contains(g_ps_init.watch_ip, scanned, &(p->dp), IPV6_FAMILY)) return 0; } else { if(ipset_contains(g_ps_init.watch_ip, scanner, &(p->sp), IPV4_FAMILY)) return 0; if(ipset_contains(g_ps_init.watch_ip, scanned, &(p->dp), IPV4_FAMILY)) return 0; }#else if(ipset_contains(g_ps_init.watch_ip, &scanner, &(p->sp), IPV4_FAMILY))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -