Abacus Sentry - Port scan detection and active defense. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Author : Craig H. Rowland E-Mail : crowland@psionic.com, crowland@bipolar.net Date : 05-25-98 Version: BETA 0.60 Introduction =-=-=-=-=-=- This is the "long" install version. You should read this file if you want to understand everything going on and the method to my madness in the program logic. Skip to Install down below if you don't care about this. The Sentry is part of the Abacus Project suite of tools. The Abacus Project is an initiative to release low-maintenance, generic, and reliable host based intrusion detection software to the Internet community. More information can be obtained from http://www.psionic.com. Sentry has a number of options to detect port scans, when it finds one it can react in the following ways: - A log indicating the incident is made via syslog() - The target host is automatically dropped into /etc/hosts.deny for TCP Wrappers - The local host is automatically re-configured to route all traffic to the target to a dead host to make the target system disappear. - The local host is automatically re-configured to drop all packets from the target via a local packet filter. The purpose of this is to give an admin a heads up that their host is being probed. There are similar programs that do this already (klaxon, etc. I just add a little twist to the whole idea (auto-blocking), plus extensive support for stealth scan detection. Sentry has four "stealth" scan detection modes. Method one uses a pre-defined list of ports to watch over. If someone pokes at them it activates. The second method is what I call "inverse" port binding. Where every port under a range is watched *except* for those that the system has bound for network daemons when the sentry starts or ones that you've manually excluded. This is a very sensitive way for looking for port probes, but also the most prone to false alarms. This program I had lying in the back of my mind for a while now and never really made. My original idea was to just drop the offending target into the TCP wrappers hosts.deny file. The problem though is that there are many daemons that are not run with wrappers, and many systems do not ship with TCP wrappers installed. I had a break as I was installing Kai's SpamShield: http://www.abest.com/~kai/spamshield.html This program would detect spam via system logs and drop the route of the target locally so all the packets just never returned to the spammer. I liked this idea and decided to apply the same idea to a port scan detector. Install =-=-=-= Step ONE: Pull the sentry_config.h file into your editor and make sure the following are to your liking: CONFIG_FILE - The path to the abacus sentry configuration file. WRAPPER_HOSTS_DENY - The path and name of TCP wrapper hosts.deny file. ABACUS_SYSOG_FACILITY - The syslog facility for Sentry to use. ABACUS_SYSLOG_LEVEL - The syslog level to send messages. I would suggest not changing any of these options unless you know what you are doing. Step TWO: Next, pull in sentry.conf into your editor and check/change the following options: TCP_PORTS - A comma delimited string of TCP ports you want Sentry to listen to. This string can NOT have any spaces in it. You can put in as many sockets as you want. Sentry will try to bind them all up until the default limit of 64. For the stealth scan detection modes, the ports are not "bound" per se, but they are monitored at the socket level for connections. For the Advanced Stealth Scan Detection (see below). This list is *ignored* UDP_PORTS - The same as above, except for UDP ports. You need to be very careful with UDP mode as an attacker can forge a port sweep and make you block any number of hosts. Use this option with caution, or not at all if your host is a well-known Internet connected system. For the Advanced Stealth Scan Detection (see below). This list is *ignored* ADVANCED_PORTS_TCP - A number indicating the highest port number to monitor down from. Any port *below* this number is then monitored. The default is 1024 (reserved port range), but can be made as large as 65535 (system max) although I've not tested it extensively with a number this high (I suspect that RedHat 5.0 has a broken bind() call and won't work properly above port 61000, if you run this version you don't want to go above this number). ADVANCED_PORTS_UDP - Same as above, except for UDP. ADVANCED_EXCLUDE_TCP - A comma delimited string of TCP ports that should be manually excluded from monitoring in Advanced mode. These are normally ports that may get hit by mistake by remote clients and shouldn't cause alarms (ident, SSL, etc). ADVANCED_EXCLUDE_UDP - Same as above, except for UDP. IGNORE_FILE - The path to the file that contains IP addresses of hosts you want to always be ignored. This will be explained later. BLOCKED_FILE - The path to the file that contains the IP addresses of blocked hosts. BLOCK_UDP - This option disables all automatic responses to UDP probes. Because UDP can be easily forged, it may allow an attacker to start a denial of service attack against the protected host, causing it to block all manner of hosts that should normally be left alone. Setting this option to "0" will disable all responses, although the connects are still logged. This option is mainly useful for Internet exposed hosts, for internal hosts I would leave this enabled. If someone internally is firing spoofed packets at you, then you have a much bigger problem than a denial of service. BLOCK_TCP - Same as above, but for TCP. Packet forgery is not as big a problem though because Sentry waits for a full connect to occur and this is much harder to forge in the basic modes. I would leave this enabled, even for Internet connected hosts. For stealth scan detection modes the UDP warning applies: An attacker can cause you to block hosts you don't want to. I wouldn't worry about this until it is a problem, but you should be aware of it. KILL_ROUTE - This is the command to run to drop the offending route if an attack is detected. This is the *full path* to the route command along with the necessary parameters to make the command work. The macro $TARGET$ will be substituted with the attacking host IP and is REQUIRED in this option. Your gateway should be a *dead host* on the local subnet. On some systems though you can just put in the localhost address (127.0.0.1) and this will probably work. All packets from the target host will get routed to this address so don't mess this up. By far the best method is to use the local packet filter (such as Linux ipfwadm/ipchains or *BSD ipfw). This is a much cleaner solution and is detailed in the config file. KILL_HOSTS_DENY - This is the format of the string to drop into the hosts.deny file that TCP wrappers uses. Again the $TARGET$ macro is expanded out to be the IP of the attacker and is required. You can also drop in any TCP wrapper escape codes here as well (%h, twist, etc). KILL_RUN_CMD - This is a command you want run *before* the route is dropped to the attacker. You can put in any program/script you want executed when an attack is detected. SCAN_TRIGGER - Sentry has a state engine that will remember hosts that connected to it. Setting this value will tell Sentry to allow X number of grace port hits before it reacts. This will detect both sequential and random port sweeps. The default is 0 which will react immediately. A setting of 1 or 2 will reduce false alarms, anything higher is probably too much as anything more than 3 hits to different ports is pretty suspicious behavior. Usually you can leave this at 0 without any consequence, with the exception of Advanced stealth scan detection modes where you may create a "hair trigger" if you aren't careful. Use your own discretion. PORT_BANNER - A text banner you want displayed to the connecting host if the Sentry is activated. Leave this commented out if you don't want this feature. If you do use it, try not to taunt the person too badly, I recommend keeping it professional and to the point. The banner is *not* displayed when stealth scan detection modes are used. Step THREE: Pull the sentry.ignore file into your editor and add in any host you want to have ignored if it connects to a tripwired port. This should always contain at least the localhost (127.0.0.1) and the IP's of the local interfaces. I would *not* recommend putting in every machine IP on your network. It is for this reason that I do not ignore by network numbers. It may be important for you to see who is connecting to you, even if it is a "friendly" machine. This can help you detect internal host compromises faster. Step FOUR: Compile. Type make and pick your system type and allow it to build and install. The default directory is /usr/local/abacus. If you don't like this directory just edit the Makefile and make sure your sentry.conf and sentry_config.h files reflect the new path. Type make install after the build to have it copy files to your install directory. Step FIVE: Start up the sentry. The sentry has six modes of operation. To start up each respective mode: sentry -tcp (basic port-bound TCP mode) sentry -udp (basic port-bound UDP mode) sentry -stcp (Stealth TCP scan detection) sentry -atcp (Advanced TCP stealth scan detection) sentry -sudp ("Stealth" UDP scan detection) sentry -audp (Advanced "Stealth" UDP scan detection) -tcp - Basic port-bound TCP mode The sentry will check the config files and then bind to all the TCP ports in the background. If you want to check the init status you should just look in your local syslog file that you are having the messages directed to. -udp - Basic port-bound UDP mode The sentry will check the config files and then bind to all the UDP ports in the background. If you want to check the init status you should just look in your local syslog file that you are having the messages directed to. -stcp - Stealth TCP scan detection mode Sentry will use a raw socket to monitor all incoming packets. If an incoming packet is destined for a monitored port it will react to block the host. This method will detect connect() scans, SYN/half-open scans, and FIN scans. -sudp - "Stealth" UDP scan detection mode This operates the same as the TCP stealth mode above. UDP ports need to be listed and they are then monitored. This does not bind any sockets, and while not really "stealth" scan detection (doesn't usually apply to UDP), it operates in a similar manner (reacts to *any* UDP packet). -atcp - Advanced TCP stealth scan detection mode Sentry will start by making a list of all the ports listening in the port area under the ADVANCED_PORTS_TCP option and will then create an exclusion list based on these ports. Any host connecting to *any port* in this range that is *not excluded* (i.e not a listening network daemon [SMTP, HTTP, etc.]) is blocked. This has some very powerful implications that you should be aware of: 1) This mode is the most sensitive and the most effective of all the protection options. It reacts to port probes with lightning speed because you don't have to wait for them to hit a tripwired port. 2) Because it reacts so abruptly, you may cut off legitimate traffic. A FTP site may send an ident request at you. If you are monitoring the ident port (113 TCP) then you have just cut off the FTP site you were going to! As a result you should put in ports that fall into this situation in this list. ** Advanced Logic Mode ** - Sentry is intelligent about how it monitors ports. For some protocols such as FTP the client actually opens up ports in the ephemeral range (1024-65535) and the server then connects *back* to you. This would normally cause the port scanner to activate. Sentry though will look at the incoming connection and determine if it is destined for one of these "temporary" bindings. If it is, then the connection is ignored for that one time. As soon as the connection is torn down the window closes and full protection is back again. This is in fact a rudimentary stateful inspection engine! -audp - Advanced UDP "stealth" scan detection mode This is the same as above except for the UDP protocol. This is a very advanced option and you stand a good chance of causing false alarms. This is because Sentry makes no distinction between broadcast and direct traffic. If you have a router on your local network putting out RIP broadcasts then there is a good likelihood you will block them. Use this option with extreme caution. You need to be sure you put in these exclusions into the ADVANCED_EXCLUDE_UDP line (i.e 520 [RIP]) Test the install: Tail the local log and you should see several Sentry initialization messages. A successful startup looks like this: Oct 9 09:11:35 nemesis sentry[1644]: adminalert: sentry is starting. Oct 9 09:11:36 nemesis sentry[1644]: adminalert: Going into listen mode on TCP port: 143 . . . Oct 9 09:11:37 nemsis sentry[1644]: adminalert: Sentry is now active and listening. ************************************************************************ ** The last line indicates the sentry is properly initialized, if you ** ** don't see it then something failed. ** ************************************************************************ You should see all the ports you told it to listen to in the log. If a port is in use Sentry will give you a warning that it couldn't bind to it and will continue on until all the other ports are bound. If *none* of the ports could be bound it will exit with an error. For the Advanced stealth scan detection mode it will list the ports that it will *not* listen for. This is an inverse binding. Now you can go to another host and telnet to a booby-trapped port. You should immediately see something like: Oct 9 09:12:44 nemesis sentry[1644]: attackalert: Connect from host: 123.345.56.78 to TCP port: 143 Oct 9 09:12:46 nemesis sentry[1644]: attackalert: Host server.haxor.org/123.345.56.78 has been blocked via dropped route. Oct 9 09:12:46 nemesis sentry[1644]: attackalert: Host server.haxor.org/123.345.56.78 has been blocked via wrappers. For advanced mode you can telnet to any port not excluded to trip an alarm. If you disconnect and try to telnet back again you should find that the target system is now unreachable. Congratulations you are now operational. If you are running Logcheck this will show up in the next pass and it should be screaming at you. If you do a netstat -rn you will see the suspect host pointed at the dead route you supplied (unless using a packet filter). Drop the sentry commands in a startup file and get back to work as you are done. How will it help? Here are some ideas: - Run as a UDP service on port 69 to catch TFTP probes. - Run as a UDP service on port 161,162 to catch SNMP probes. - Run as a UDP service in the port range 32000-33000 to catch RPC probes. - Run as a TCP service on port 143 to catch IMAP probes. - Run as a TCP service on ports 11,15 to catch netstat/systat probes. - etc. The fact is that Sentry reacts quickly enough that a port scan of your host by an attacker will be stopped within one second after hitting any tripwired port, even faster in the Advanced TCP stealth scan detection mode. For any type of UDP scan it will prove highly irritating for the person trying to scan you as all the ports will likely appear "open." For TCP scans the attacker will simply get no response whatsoever. Safety =-=-=- If I missed anything in the program's safety considerations I would very much like to hear about it before you post it to BugTraq :). Messages =-=-=-=- To the best I could, all states/errors/successes and unknowns are written to the syslog. The following tags identify each one: adminalert: - Some message indicating the status of the Sentry. securityalert: - A message indicating a security relevant event occurred. attackalert: - A host has tripped a sensor and an action was performed. Files =-=-= As it stands now, all hosts are dropped into the sentry.blocked file when they are blocked as well as a sentry.history file. The blocked file is erased each time the sentry is restarted. The history file is simply appended to and can be used as a record of all hosts that have been blocked to date. The way the route blocking works is that it drops the *return* route to the attacking host, not the *incoming* route. If an attacker is doing a UDP scan of your host the packets will still trip the sensor, they just don't return to the host. If the blocked file did not exist the host would be written possibly hundreds or thousands of times to hosts.deny!! This is not good. A better solution is to integrate in with a real packet filter locally (ala Linux ipfwadm which is supported). After a period of time elapses you may wish to delete the local dead route to the offender and keep them in the hosts.deny file. This is solely your choice. If you wish to re-enable blocking should the offender return, just re-start sentry or delete the individual entry from the blocked file. That's about it. I highly recommend you use Logcheck to keep an eye on things in the log files as well so you can detect other problems, and see what the Sentry is saying to you. You can find this program at: http://www.psionic.com/abacus/abacus_logcheck.html Thanks for reading this and write if you have any questions or comments. Thanks, -- Craig crowland@psionic.com crowland@bipolar.net