Endpoint Protection

 View Only

PortSentry for Attack Detection - Part Two 

May 29, 2002 02:00 AM

by Ido Dubrawsky

PortSentry for Attack Detection - Part Two
by Ido Dubrawsky
last updated May 29, 2002

This is the second in a two-part series on PortSentry. The first article discussed how PortSentry works to identify attacks, as well as what types of attacks it identifies. This article will focus on building, installing, and operating PortSentry. The focus here will be on the various configuration options available for PortSentry, as well as some of the benefits and drawbacks of those options.

Building PortSentry

Building PortSentry is fairly straighforward. The gzipped tarball can been downloaded from the Psionic Technologies Web site. Before actually compiling the software, several variables need to be set in the portsentry_config.h file. These are:

  • CONFIG_FILE- Path to the PortSentry configuration file (default: /usr/local/psionic/portsentry2/portsentry.conf).
  • WRAPPER_HOSTS_DENY - Path and name of the TCP wrappers hosts.deny file (default: /etc/hosts.deny).
  • SYSLOG_FACILITY - As the name suggests, this is the syslog facility PortSentry is to use (default: LOG_DAEMON).
  • SYSLOG_LEVEL - The syslog level PortSentry is to use (default: LOG_NOTICE).

Typically the CONFIG_FILE variable will be changed to match the local installation scheme on the host and the SYSLOG_FACILITY should be changed to one of the LOG_LOCAL[0-7] values. Changing the SYSLOG_FACILITY to one of the LOG_LOCAL values allows the administrator more flexibility by having PortSentry log to its own file. The other two variables can be left with their default values.

The next step (which is actually not how Psionic Technologies recommends the software be compiled) is to actually build the program. This is done using the make facility and passing it the operating system name. Currently PortSentry defines the following systems: Linux, BSD, OpenBSD, FreeBSD, NetBSD, and generic. On a Linux system the command is:

 # make linux 

This compiles PortSentry. The command to install the program is:

 # make install 

By default this will install the program in /usr/local/psionic. The default location can be changed by changing the INSTALLDIR variable in the Makefile. Any changes to this variable should be reflected in the the CONFIG_FILE varible of portsentry_config.h as noted above. Once the software is installed it needs to be configured.

Configuring PortSentry

PortSentry is controlled through two files:

  • portsentry.conf - the main configuration file; and,
  • portsentry.ignore - a file of IP addresses that PortSentry should ignore if it connects to a monitored port.

The portsentry.ignore file is simply a list of IP addresses along with the associated netmask in "slash" notation as shown below:

 172.16.88.0/24 10.16.17.0/24 192.168.0.0/16 127.0.0.1/32 

This file should at the very least contain the localhost (127.0.0.1) as well as the IP addresses assigned to the local system interfaces.

The portsentry.conf file is what controls how portsentry monitors and defends a host when it detects a scan. The first part of the configuration file is used to determine which interface PortSentry will monitor since the program can only monitor one interface at a time. For non-multi-homed systems this is not a problem. However, on systems with two or more interfaces PortSentry will listen on the primary interface. One apparent problem is the monitoring of a dial-up interface. It seems that PortSentry cannot initialize the datalink type used with point-to-point dial-up interfaces, so using PortSentry on a laptop is currently not supported.

To have PortSentry monitor only one interface on a multi-homed system the following variables must be set in the portsentry.conf file:

  • INTERFACE - set this to "auto" or to the name of the interface to be monitored (i.e. eth0, fxp0, etc.).
  • INTERFACE_ADDRESS - This variable must be set to the IP address of the interface to be monitored. This information is not automatically determined by PortSentry.

The next two sections involve configuring the ports that PortSentry will monitor, as well as the location of the ignore, history and blocked files. Psionic provides some default lists of ports, which can be categorized in one of three ways: "comprehensive", "aware", and "bare-bones". The "comprehensive" list includes 54 TCP ports and 32 UDP ports ranging from tcpmux all the way up to 54321/TCP (a port sometimes used by the SchoolBus and BackOrifice 2000 trojans). The "aware" list consists of 30 TCP and 18 UDP ports with a similar spread. The "bare-bones" list contains 24 TCP and 14 UDP ports.

As mentioned above, the portsentry.ignore file is simply a list of IP addresses along with their associated netmasks in "slash" format, which portsentry will not react to if a scan originates from one of those addresses. This list needs to be kept short. If the entire local network is included in this list, PortSentry will ignore scans from hosts on the local network, even if the scanning machine has been compromised by an attacker. It would not look good if a host running PortSentry was compromised simply because another machine on the same network was used as the attackers point of entry.

By default PortSentry will not resolve IP addresses into hostnames. This can be turned on by setting the value of RESOLVE_HOST to 1 in the configuration file however this is not recommended since a large number of connections will cause PortSentry to initiate many DNS queries and may impact system performance.

The next series of sections in the configuration file cover how PortSentry should respond once it has detected a scan. These sections cover dropping routes, adding hosts to TCP wrappers hosts.deny file, or running an external command, which is discussed below. The configuration variables that actually control, whether PortSentry responds to a scan or not, are BLOCK_TCP and BLOCK_DUP. Depending on the values of these two variables, PortSentry can log the scan (a value of 0) but not respond, or respond to a scan by either blocking it or running an external command (values of 1 or 2 respectively).

Blocking a scan can be done in several ways, two of which are mutually exclusive while the third can be complementary to either of the other two. To block a scan, PortSentry can either insert a route into the host's route table, which directs the offending traffic to a veritable "black hole", or it can use one of several packet filtering software packages (ipfwadm, ipchains, ipf, ipfw, or iptables) to block traffic from the scanning host. The action is determined by the setting of the configuration variable KILL_ROUTE.

By inserting a route to drop the offending traffic, PortSentry can make a system appear to vanish from the perspective of the attacking host...almost. Typically the route gateway should be an inactive IP or a "dead host". However, this action can create asynchronous routing whereby traffic enters the target host from one route and exits through another. While this will block full TCP connect scans and requests, UDP attacks could still be blindly executed.

The preferred method of blocking traffic is through the use of a packet filter. Having PortSentry insert additional routes into the host's routing table increases the size of the route table maintained by the OS, it also does not provide for complete protection against UDP attacks. Packet filters are designed to do just that - block unwanted packets and, therefore, are the preferred method in PortSentry.

A bit of warning about indiscriminantly blocking IP addresses because of suspicious scans. This, as may be easily surmised, can quickly lead to a denial of service. Not by an attacker flooding the target system with too much traffic but by the system itself responding to apparent scans by blocking traffic from those addresses in a packet filter. It is quite easy to spoof IP addresses on the Internet and by doing so cause PortSentry to react to those addresses by inserting filter rules (or route table entries). Consider the following two cases:

  1. An attacker spoofs the IP address of the target system's nameserver in the scan. PortSentry, in this example, is configured with the following KILL_ROUTE variable: KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY"

    The scan will cause the following IPchains rule to be inserted into the filter:

    ipchains -I input -s

    and all packets from the name server will be blocked by the packet filter. The host will not be able to do any DNS lookups (assuming it is configured to query only one nameserver for DNS). Any services which require a DNS lookup (either forward or reverse) will fail.

  2. The second case is where the target host receives a service from another system such as an NNTP server or an NTP server. An attacker spoofing the address of the NNTP server could cause the flow of Usenet news articles to be cut off. Causing PortSentry to block packets from an NTP peer or server could eventually result in clock drift on the target host. Applications that require precise timing might be seriously affected.

In both cases above, PortSentry inadvertently blocked a legitimate host because an attacker used spoofed scans. Hosts running PortSentry should be monitored closely (Psionic Technologies recommends the use of their LogSentry product to monitor PortSentry's logfiles) in order to ensure that the host does not end up blocking necessary, legitimate traffic.

The KILL_HOSTS_DENY variable provides for the insertion of the scanning host's IP address into the /etc/hosts.deny file. This provides additional protection if the host is rebooted and the packet filter rule/route blocking traffic from the scanning host is lost. Should that happen, any connections from the source host to a port running a service protected with TCP wrappers will still drop the connection.

Another option provided with PortSentry is the capability to run an external command before or after the scan source host has been blocked. This is configured with the KILL_RUN_CMD configuration option. This command can be anything from a reverse finger to the scanning host to return scan. Whether this command is run before or after PortSentry is blocking traffic from the scan source host is determined with the KILL_RUN_CMD_FIRST option. Setting this to '1' causes PortSentry to execute the external command before PortSentry takes action against the scan source host. A value of '0' causes PortSentry to block first and then execute the command. Extreme care must be used when configuring this option. Tools abound on the Internet that allow even less capable attackers to spoof IP addresses. Nmap has the -S < IP Address > (for specifying the source address of the scan) and the -D < decoy1,[decoy2],...> (for specifying additional "decoy" addresses of the scan). Using these options, an attacker scanning a host that is running PortSentry can cause that host to begin scanning third parties.

The final variable to configure in the portsentry.conf file is the SCAN_TRIGGER value. This controls the number of port connects PortSentry will allow before triggering a response. By default this is set to '0', which causes PortSentry to react immediately to a scan. This variable can be set to any positive value, but the program's authors don't recommend a value greater than 2.

For an example configuration of Portsentry, please refer to the appendix at the end of this article.

Running PortSentry

Once PortSentry's configuration file has been customized, the next step is to simply run the software. PortSentry version 1.0 required that the program be executed with one of two flags: -tcp or -udp in order to start up a portsentry instance which would monitor TCP and UDP traffic. With PortSentry 2.0 that has changed. PortSentry now automatically starts monitoring both TCP and UDP traffic upon program startup. If everything goes as planned, the following messages should be logged once PortSentry starts:

 May 26 22:46:57 rashi portsentry[3111]: adminalert: Monitoring interface eth0 and address: 10.16.17.101  May 26 22:46:57 rashi portsentry[3111]: adminalert: Initializing PortSentry BPF  filters.  May 26 22:46:57 rashi portsentry[3111]: adminalert: Monitoring TCP ports:  1,11,15,79,111,119,143,515,540,635,666,1080,1524,2000,6667,12345,12346, 20034,27374,27665,31337,32771,32772,32773,32774,40421,49724,54320,54321  May 26 22:46:57 rashi portsentry[3111]: adminalert: Monitoring UDP ports:  1,7,9,69,161,162,513,635,2049,27444,32770,32771,32772,32773,32774, 31337,54321  May 26 22:46:57 rashi portsentry[3111]: adminalert: PortSentry is initialized and  monitoring. 

Once PortSentry has been started, the next step is to test the install. This can best be achieved by logging into another host and connecting to one of PortSentry's monitored ports. A properly installed and operating PortSentry should log a message similar to the one below:

 May 26 22:54:50 rashi portsentry[3111]: attackalert: Host 10.16.17.1 has been  blocked via wrappers with string: "ALL: 10.16.17.1" May 26 22:54:50 rashi portsentry[3111]:

 attackalert: TCP SYN scan from host  10.16.17.1/10.16.17.1 to TCP port: 2000 from TCP port: 4427 

Conclusion

PortSentry provides a versatile way to detect scans against a host as well as how to respond to such scans. While care must be used when deciding how sensitive PortSentry should be to scans as well as its method of response PortSentry provides a good first line of defense against attacks.


Appendix - PortSentry Configuration Example

 # PortSentry Configuration # # $Id: portsentry.conf,v 1.36 2002/04/08 19:11:05 crowland Exp crowland $ # # IMPORTANT NOTE: You CAN NOT put spaces between your port arguments. # # The default ports will catch a large number of common probes # # All entries must be in quotes. ########################### # Interface Configurations# ########################### # You can set the interface to monitor with this option. Examples include # "eth0", "ep0", etc. You do not need to put in the dev directory path. # If you leave this as "auto" PortSentry will attempt to monitor the # primary interface automatically. Most people should leave this alone # unless you have a multi-homed system (firewall, etc.) and want to monitor # a particular interface only. INTERFACE="auto" # This MUST BE SET to the address of the interface being monitored on # your system. It is NOT determined automatically in this version of # PortSentry.  INTERFACE_ADDRESS="10.16.17.205" ####################### # Port Configurations # ####################### # # # Some example port configs for stealth modes # # We like to always keep some ports at the "low" end of the spectrum. # This will detect a sequential port sweep really quickly and usually # these ports are not in use (i.e. tcpmux port 1) # # Un-comment these if you are really anal: TCP_PORTS="1,7,9,11,15,21,23,70,79,80,109,110,111,119,138,139,143,512,513,514, 515,540,635,666,1080,1524,2000,2001,4000,4001,5631,5632,5742,6000,6001,6667, 12345,12346,20034,27374,27665,30303,32771,32772,32773,32774,31337,40421,40425, 49724,54320,54321" UDP_PORTS="1,7,9,66,67,68,69,111,137,138,161,162,474,513,517,518,635,666,700, 2049,5631,5632,31335,27444,34555,32770,32771,32772,32773,32774,31337" # # Use these if you just want to be aware (default): TCP_PORTS="1,11,15,79,111,119,143,515,540,635,666,1080,1524,2000,6667,12345, 12346,20034,27374,27665,31337,32771,32772,32773,32774,40421,49724,54320,54321" UDP_PORTS="1,7,9,69,161,162,513,635,2049,27444,32770,32771,32772,32773,32774, 31337,54321" # # Use these for just bare-bones #TCP_PORTS="1,11,15,110,111,143,540,635,1080,1524,2000,12345,12346,20034,27374, 31337,32771,32772,32773,32774,49724,54320,54321" #UDP_PORTS="1,7,9,69,161,162,513,32770,32771,32772,32773,32774,31337,54321" ###################### # Configuration Files# ###################### # # Hosts to ignore IGNORE_FILE="/usr/local/psionic/portsentry2/portsentry.ignore" # Hosts that have been denied (running history) HISTORY_FILE="/usr/local/psionic/portsentry2/portsentry.history" # Hosts that have been denied this session only (temporary until next restart) BLOCKED_FILE="/usr/local/psionic/portsentry2/portsentry.blocked" ############################## # Misc. Configuration Options# ############################## # # DNS Name resolution - Setting this to "1" will turn on DNS lookups # for attacking hosts. Setting it to "0" (or any other value) will shut # it off. Turning on this value can slow down PortSentry if a lot of attacks # are coming in concurrently while it waits for the DNS resolution to return. # This option can can also alert an attacker to PortSentry's presence if they # see DNS queries come to a nameserver they control after they initiate an # attack. The default is to keep this off. RESOLVE_HOST = "0" ################### # Response Options# ################### # Options to dispose of attacker. Each is an action that will # be run if an attack is detected. If you don't want a particular # option then comment it out and it will be skipped. # # The variable $TARGET$ will be substituted with the target attacking # host when an attack is detected. The variable $PORT$ will be substituted # with the port that was scanned. The variable $MODE# will be substitued with # the mode (TCP/UDP) that was used for the detect. # ################## # Ignore Options # ################## # These options allow you to enable automatic response # options for UDP/TCP. This is useful if you just want # warnings for connections, but don't want to react for # a particular protocol (i.e. you want to block TCP, but # not UDP). To prevent a possible Denial of service attack # against stealth scan detection for TCP, you may # want to disable blocking, but leave the warning enabled. # We personally would wait for this to become a problem before # doing though as most attackers really aren't doing this. # The third option allows you to run just the external command # in case of a scan to have a pager script or such execute # but not drop the route. This may be useful for some admins # who want to block TCP, but only want pager/e-mail warnings # on UDP, etc. # # # 0 = Do not block UDP/TCP scans. # 1 = Block UDP/TCP scans. # 2 = Run external command only (KILL_RUN_CMD) BLOCK_UDP="1" BLOCK_TCP="1" ################### # Dropping Routes:# ################### # This command is used to drop the route or add the host into # a local filter table. # # The gateway (XXX.XXX.XXX.XXX) should ideally be a dead host on # the *local* subnet. On some hosts you can also point this at # localhost (127.0.0.1) and get the same effect. NOTE THAT # XXX.XXX.XXX.XXX WILL *NOT* WORK. YOU NEED TO CHANGE IT!! # # ALL KILL ROUTE OPTIONS ARE COMMENTED OUT INITIALLY. Make sure you # uncomment the correct line for your OS. If you OS is not listed # here and you have a route drop command that works then please # mail it to us so we can include it. ONLY ONE KILL_ROUTE OPTION # CAN BE USED AT A TIME SO DON'T UNCOMMENT MULTIPLE LINES. # # NOTE: The route commands are the least optimal way of blocking # and do not provide complete protection against UDP attacks and # will still generate alarms for both UDP and stealth scans. We # always recommend you use a packet filter because they are made # for this purpose. # # Generic #KILL_ROUTE="/sbin/route add $TARGET$ XXX.XXX.XXX.XXX" # Generic Linux #KILL_ROUTE="/sbin/route add -host $TARGET$ gw XXX.XXX.XXX.XXX" # Newer versions of Linux support the reject flag now. This # is cleaner than the above option. #KILL_ROUTE="/sbin/route add -host $TARGET$ reject" # Generic BSD (BSDI, OpenBSD, NetBSD, FreeBSD) #KILL_ROUTE="/sbin/route add $TARGET$ XXX.XXX.XXX.XXX" # Generic Sun #KILL_ROUTE="/usr/sbin/route add $TARGET$ XXX.XXX.XXX.XXX 1" # NEXTSTEP #KILL_ROUTE="/usr/etc/route add $TARGET$ 127.0.0.1 1" # FreeBSD #KILL_ROUTE="route add -net $TARGET$ -netmask 255.255.255.255 127.0.0.1 -blackhole" # Digital UNIX 4.0D (OSF/1 / Compaq Tru64 UNIX) #KILL_ROUTE="/sbin/route add -host -blackhole $TARGET$ 127.0.0.1" # Generic HP-UX #KILL_ROUTE="/usr/sbin/route add net $TARGET$ netmask 255.255.255.0 127.0.0.1" ## # Using a packet filter is the PREFERRED. The below lines # work well on many OS's. Remember, you can only uncomment *one* # KILL_ROUTE option. ## # ipfwadm support for Linux #KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$ -o" # # ipfwadm support for Linux (no logging of denied packets) #KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$" # # ipchain support for Linux #KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY -l" # # ipchain support for Linux (no logging of denied packets) #KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY" # # iptables support for Linux #KILL_ROUTE="/usr/local/bin/iptables -I INPUT -s $TARGET$ -j DROP" # # For those of you running FreeBSD (and compatible) you can # use their built in firewalling as well. # #KILL_ROUTE="/sbin/ipfw add 1 deny all from $TARGET$:255.255.255.255 to any" # # # For those running ipf (OpenBSD, etc.) # NOTE THAT YOU NEED TO CHANGE EXTERNAL_INTERFACE TO A VALID INTERFACE!! # KILL_ROUTE="/bin/echo 'block in log on le0 from $TARGET$/32 to any'  | /sbin/ipf -f -" ############### # TCP Wrappers# ############### # This text will be dropped into the hosts.deny file for wrappers # to use. There are two formats for TCP wrappers: # # Format One: Old Style - The default when extended host processing # options are not enabled. # #KILL_HOSTS_DENY="ALL: $TARGET$" # Format Two: New Style - The format used when extended option # processing is enabled. You can drop in extended processing # options, but be sure you escape all '%' symbols with a backslash # to prevent problems writing out (i.e. \%c \%h ) # KILL_HOSTS_DENY="ALL: $TARGET$ : DENY" ################### # External Command# ################### # This is a command that is run when a host connects, it can be whatever # you want it to be (pager, etc.). This command is executed before the # route is dropped or after depending on the KILL_RUN_CMD_FIRST option below # # # WE NEVER RECOMMEND YOU PUT IN RETALIATORY ACTIONS AGAINST THE HOST  # SCANNING YOU! # # TCP/IP is an *unauthenticated protocol* and people can make scans appear out # of thin air. Do you really want to counter-attack an innocent third party? # That could happen if you aren't careful. # # The KILL_RUN_CMD_FIRST value should be set to "1" to force the command # to run *before* the blocking occurs and should be set to "0" to make the # command run *after* the blocking has occurred. # KILL_RUN_CMD_FIRST = "1" # # KILL_RUN_CMD="/usr/local/bin/return-finger $TARGET$" ##################### # Scan trigger value# ##################### # Enter in the number of port connects you will allow before an # alarm is given. The default is 0 which will react immediately. # A value of 1 or 2 will reduce false alarms. Anything higher is # probably not necessary. This value must always be specified, but # generally can be left at 0. # SCAN_TRIGGER="0" 

Ido Dubrawsky has been working in UNIX and network administration field for almost 8 years. He is currently employed by Cisco Systems in the Cisco Secure Consulting Service as a Network Security Engineer.


Relevant Links

PortSentry for Attack Detection, Part One
Ido Dubrawsky, SecurityFocus
 

This article originally appeared on SecurityFocus.com -- reproduction in whole or in part is not allowed without expressed written consent.

Statistics
0 Favorited
0 Views
0 Files
0 Shares
0 Downloads

Tags and Keywords

Related Entries and Links

No Related Resource entered.