Problem
How do I detect when hosts on my network(s) are performing port scans and host scans?
Solution
There are actually a couple of answers to that question. This is because Snort developers have gone through several iterations of port scan detectors. The most common is the portscan preprocessor, while the newest is the flow-portscan preprocessor. Finally, portscan2 was supposed to address some of the problems with the portscan preprocessor, such as detection of SYN floods as port scans instead of DoS attacks. All these preprocessors are still compiled into Snort by default, even as late as Version 2.2.0. However, the trend is toward the flow-portscan preprocessor, as this is the first preprocessor to use the flow engine for its data. This section gives some example configurations for all three. The most effort is on the flow-portscan preprocessor, as the other two are no longer part of the default snort.conf file.
Portscan
This is the oldest and most commonly used of the three preprocessors. However, if you are using ACID (Chapter 5), you might want to pull some port scan information into ACID with little changes. To enable this in your snort.conf file, simply enter this example into the file right below the flow preprocessor.
Preprocessor flow: stats_interval 0 hash 2 # # Legacy Support - Porscan Preprocessor from snort 1.x preprocessor portscan: $HOME_NET 4 3 /path/to/logs/portscan.log
When enabled, this preprocessor detects when a source host other than the one in the HOME_NET variable starts more that four port connections within three seconds. When that happens, two events are written: one in the Snort alert file, and the other in the portscan.log file. The alert file notifies the analysts of a possible port scan against one of their resources.
[**] [100:2:1] spp_portscan: portscan status from 10.0.4.100: 1150 connections across 1 hosts: TCP(1150), UDP(0) [**]
The portscan.log file displays the ports targeted and their respective source port(s), as in the next example:
# quick display of an nmap scan (nmap -sT -F 10.0.4.45) Aug 29 03:05:48 10.0.4.100:4530 -> 10.0.4.45:9535 SYN ******S* Aug 29 03:05:48 10.0.4.100:4531 -> 10.0.4.45:1347 SYN ******S* Aug 29 03:05:48 10.0.4.100:4532 -> 10.0.4.45:9992 SYN ******S* Aug 29 03:05:48 10.0.4.100:4533 -> 10.0.4.45:8009 SYN ******S* Aug 29 03:05:48 10.0.4.100:4534 -> 10.0.4.45:583 SYN ******S* Aug 29 03:05:48 10.0.4.100:4535 -> 10.0.4.45:5713 SYN ******S* Aug 29 03:05:48 10.0.4.100:4536 -> 10.0.4.45:2043 SYN ******S* Aug 29 03:05:48 10.0.4.100:4537 -> 10.0.4.45:12345 SYN ******S* Aug 29 03:05:48 10.0.4.100:4538 -> 10.0.4.45:6141 SYN ******S* Aug 29 03:05:48 10.0.4.100:4539 -> 10.0.4.45:518 SYN ******S*
One concern of this preprocessor was how to blanket ignore hosts such as your DNS servers that often appeared as portscan attackers. The solution came in the form of another component of the portscan preprocessor: portscan-ignorehosts. This component simply tells the portscan preprocessor to not alert on any traffic from the host(s) and/or network(s) in a given list. An example of that is as follows; more than one entry into this list is space separated.
# Goes in snort.conf file below "preprocessor portscan" line Preprocessor portscan-ignorehosts: 10.0.4.1 10.0.4.105
This example filters out any port scans coming from either the DNS or web server.
Portscan2
As we mentioned, the portscan preprocessor had some limitations that another group of Snort developers tried to remedy with a rewrite and some added functionality. The portscan2 preprocessor relies on the old conversation tracking preprocessor and can't be enabled when the flow preprocessor is active. Following is an example of a typical conversation and portscan2 configuration.
# First Disable the flow preprocessor # preprocessor flow: stats_interval 0 hash 2 # # Enable the conversation preprocessor preprocessor conversation: allowed_ip_protocols all, timeout 60, max_conversations 50000 # the arguments are: # allowed IP protocols, either a list of protocol numbers or word "all" # timeout (seconds) before connections or conversations are rolled # out of the preprocessor #the max number of conversations that the preprocessor should see # Enable the portscan2 preprocessor preprocessor portscan2: scanners_max 256, targets_max 256, target_limit 3, port_limit 10, timeout 60 # arguments are: # the max number of scanning hosts to support at once # the max number of target hosts to support at once # the number of hosts a scanner must touch before a scan is triggered # number of ports a scanner must touch before a scan is triggered # the timeout period (seconds) before a scanners activity is rolled # out of the preprocessor
When this is enabled, a scan would look like this in your alert file:
[**] [117:1:1] (spp_portscan2) Portscan detected from 10.0.4.100: 1 targets 11 ports in 0 seconds [**] 08/xx-13:27:32.464097 10.0.4.100:3537 -> 10.0.4.1:5232 Tcp TTL:64 TOS:0x0 ID:11424 IpLen:20 DgmLen:60 DF ******S* Seq: 0xEA6B7F8E Ack: 0x0 Win: 0x16D0 TcpLen: 40 TCP Options (5) => MSS: 1460 SackOK TS: 83186478 0 NOP WS: 0
The type of data logged into the default file scan.log in your Snort log directory is much more detailed:
08/xx-13:27:32.464097 TCP src: 10.0.4.100 dst: 10.0.4.1 sport: 3537 dport: 5232 tgts: 1 ports: 11 flags: ******S* event_id: 0 08/xx-13:27:32.464177 TCP src: 10.0.4.100 dst: 10.0.4.1 sport: 3538 dport: 5002 tgts: 1 ports: 12 flags: ******S* event_id: 7 08/xx-13:27:32.464256 TCP src: 10.0.4.100 dst: 10.0.4.1 sport: 3539 dport: 780 tgts: 1 ports: 13 flags: ******S* event_id: 7 08/xx-13:27:32.465642 TCP src: 10.0.4.100 dst: 10.0.4.1 sport: 3540 dport: 1484 tgts: 1 ports: 14 flags: ******S* event_id: 7 08/xx-13:27:32.465722 TCP src: 10.0.4.100 dst: 10.0.4.1 sport: 3541 dport: 2002 tgts: 1 ports: 15 flags: ******S* event_id: 7 08/xx-13:27:32.465802 TCP src: 10.0.4.100 dst: 10.0.4.1 sport: 3542 dport: 214 tgts: 1 ports: 16 flags: ******S* event_id: 7
From this logfile you can immediately determine several facts about this scan:
Flow-portscan
This is the newest preprocessor to detect port scans. This preprocessor is the first to take advantage of the flow preprocessor data. While that is the case, this preprocessor remains one of the hardest preprocessors for people to configure and use.
# for a single IP cable/DSL connection detection config # The would be useful for a network that doesn't server many or any services to # the outside world. #
Note that talkers are hosts that are active on your network such as your workstations for browsing the Web, file sharing, etc. Scanners are hosts that have started communicating with one of your hosts within the learning time of the host over a previously unused port.
preprocessor flow-portscan: # the IP space to use for our allowed/learned network(s) server-watchnet [10.0.4.0/24] # The number of seconds to keep port information on your watchnet for example this # will keep the ports in use on each host for a 1-minute interval before refreshing server-learning-time 60 # the number of requests a port on a host in the watchnet must see before # it's treated as a talker rather than a scanner server-scanner-limit 50 # If you have hosts or networks that you want to ignore and not # count into the learning time place here # src-ignore-net [10.0.4.1/32] # If you have destination networks or hosts that you want to ignore # such as your DNS server or POP mail server place here # dst-ignore-net [10.0.4.1/32] # This sets how the alarms will be sent out. The default setting is # display the alerts "once" per scan. However, in this case we are going # to alarm every time the points go above the threshold. alert-mode all # The tells the preprocessor to send the alarm out in a text message # mode as seen below. However, if you want there is "pktkludge" option # that you can use as well to send to the snort logging system. output-mode msg # This turns on detection much like the stream4 preprocessor for invalid # or odd tcp flows. Such as a SYN/FIN flagged flow. tcp-penalties on
These settings log something like the following in the alert file. This correctly identifies the nmap hostin this case, 10.0.4.100. However, you don't see what ports it's been probing or the targets.
[**] [121:2:1] Portscan detected from 10.0.4.100 Talker(fixed: 1 sliding: 1) Scanner(fixed: 10 sliding: 40) [**] 08/xx-14:29:14.676834 [**] [121:1:1] Portscan detected from 10.0.4.100 Talker(fixed: 1 sliding: 1) Scanner(fixed: 15 sliding: 5) [**] 08/xx-14:29:14.676904 [**] [121:1:1] Portscan detected from 10.0.4.100 Talker(fixed: 1 sliding: 1) Scanner(fixed: 15 sliding: 20) [**] 08/xx-14:29:14.677102
For monitoring a larger network, you might try the following configuration example:
preprocessor flow-portscan: # Network to monitor server-watchnet [192.168.1.0/24,192.168.2.0/24] # Ignore traffic coming from the routers #src-ignore-net [192.168.1.1/32,192.168.2.1/32] # Ignore traffic going for the DNS servers #dst-ignore-net [192.168.1.2/32,192.168.2.2/32] # the number of requests to a single port such as 80/tcp that a hosts # in the watchnet must recieve before the port is ignored for portscans #server-ignore-limit 200 # Time (seconds) to keep there watchnet servers ports before resetting server-learning-time 3600 # the number of requests a port on a host in the watchnet must see before # it's treated as a talker rather than a scanner server-scanner-limit 50 # sets the alert mode to alarm on every event over the threshold alert-mode all # Sends a text message to the alert file output-mode msg # alarm on odd flow tcp flag settings tcp-penalties on # Used for debugging to dump the contents of all of the flow-portscan # 3 "tables" of data to the screen on snort exit. Set to 0 to disable. Dumpall 1
Discussion
As you might have seen, the portscan preprocessor is still useful when detecting port scans from the flow preprocessor. This, combined with the fact that it's one of the simplest preprocessors to set up, makes it a viable preprocessor, especially if you are using a Snort frontend such as ACID (Chapter 5).
However, the portscan2 preprocessor takes quite a bit of memory and requires disabling and reenabling preprocessors. The worst of these is disabling the flow preprocessor. This causes problems even with the Snort rules engine, as quite a few of the new rules use the flow keyword in their detection patterns. The other concern about this preprocessor is the requirement of the conversation preprocessor, which flow was built to replace. The conversation preprocessor didn't handle state very well. However, one useful keyword that the conversation preprocessor had was alert_odd_protocols. The following conversation preprocessor configuration detects when protocols other than TCP, UDP, or ICMP are in use on your network.
Preprocessor conversation: preprocessor conversation: allowed_ip_protocols, 1 6 17,timeout 60, max_conversations 50000, alert_odd_protocols
Finally, with the new flow-portscan preprocessor, we used a small network and larger network configuration example that should at least get you started on detecting port scans on your network(s). However, the flow-portscan can be tweaked for your network. If you want data from the preprocessor's output, you can apply a patch to Snort to get more data back.
You can change from a port scan log entry like this:
[**] [121:3:1] Portscan detected from 10.0.4.100 Talker(fixed: 15 sliding: 15) Scanner(fixed: 0 sliding: 0) [**] 08/xx-15:38:21.619113
To a more detailed log like this:
[**] [121:3:1] (flow_ps) Portscan detected from 10.0.4.100 Talker(fixed: 15 sliding: 15) Scanner(fixed: 0 sliding: 0) [**] 08/xx-16:10:08.174184 10.0.4.100:42027 -> 10.0.4.95:80 TCP TTL:41 TOS:0x0 ID:16080 IpLen:20 DgmLen:40 ***A**** Seq: 0xFB200EDB Ack: 0x8FFD88F7 Win: 0x800 TcpLen: 20
If you want to enable this type of logging, just follow these directions to patch and remake your Snort build.
# copy this code into your system # create file "flow-portscan_output.patch" ### START OF PATCH diff -urN snort-2.2.0_orig/src/generators.h snort-2.2.0/src/generators.h --- snort-2.2.0_orig/src/generators.h 2003-10-20 15:03:19.000000000 +0000 +++ snort-2.2.0/src/generators.h 2004-05-22 23:01:52.000000000 +0000 @@ -316,6 +316,7 @@ #define DECODE_BAD_TRHMR_STR "(snort_decoder) WARNING: Bad Token Ring MR Header!" +#define FLOWPS_PREFIX_STR "(flow_ps) Portscan detected from " #define SCAN2_PREFIX_STR "(spp_portscan2) Portscan detected from " #define CONV_BAD_IP_PROTOCOL_STR "(spp_conversation) Bad IP protocol!" diff -urN snort-2.2.0_orig/src/preprocessors/flow/portscan/flowps_snort.c snort-2.2.0/src/preprocessors/flow/portscan/flowps_snort.c --- snort-2.2.0_orig/src/preprocessors/flow/portscan/flowps_snort.c 2004-03-31 18:09:47.000000000 +0000 +++ snort-2.2.0/src/preprocessors/flow/portscan/flowps_snort.c 2004-05-22 23:04:00.000000000 +0000 @@ -811,6 +811,8 @@ char buf[1024 + 1]; u_int32_t event_id; u_int32_t event_type; /* the sid for the gid */ + Event event; + /* Assign an event type to the display */ if(sep->flags & ALERT_FIXED_SCANNER) @@ -837,18 +839,21 @@ switch(output_type) { case PKTKLUDGE: + DEBUG_WRAP(DebugMessage(DEBUG_FLOWSYS, FLOWPS_PREFIX_STR "%s %s ", + inet_ntoa(*(struct in_addr *) address), "logged using pktkludge.");); /* log a packet to the output system */ p = flowps_mkpacket(sep, orig_packet, address, cur); case VARIABLEMSG: - snprintf(buf, 1024, - "Portscan detected from %s Talker(fixed: %u sliding: %u) Scanner(fixed: %u sliding: %u)", + snprintf(buf, 1024, FLOWPS_PREFIX_STR + "%s Talker(fixed: %u sliding: %u) Scanner(fixed: %u sliding: %u)", inet_ntoa(*(struct in_addr *) address), sep->fixed_talker.score, sep->sliding_talker.score, sep->fixed_scanner.score, sep->sliding_scanner.score); buf[1024] = '