Psionic PortSentry

PortSentry is an adaptive packet filter. It watches for a threshold number of connects (including stealth connects) to closed ports, then blocks that IP from connecting to any port. This page documents, largely for my own use, how to install and use PortSentry, and provides (or will provide) some evaluation of it. PortSentry is part of the Abacus toolkit from Psionic; Craig Rowland wrote it. It's still under development, my RCS timestamps show mods on 06/26/01 in July of 2001.

Summary

PortSentry works as advertised. Following the documentation would be easier if the default advised setup were made clearer. I think using the fast 'portsentry -atcp' is the way to go, since it's fast and doesn't bind to any ports.

However, where would we really apply this? Most ports on a host should start out with IPchains DENY rules, unless there's a legitimate service. And many legit services should be limited to, say, your local subnet(s). For a public server, with HTTP, FTP, ssh, etc., it might well be a good thing to drop a client to all ports when suspicious activity is seen on any port. Nonetheless, most exploits and attempted exploits I've seen are not preceded by any scan -- the magic packet destined for the IIS '.ida' or LPRng format string attack simply shows up.

Defense in depth calls for using this tool, but I also should look at some of the Snort-based stuff for adaptive firewalling. E.g, snort2iptables, girr, hogwash, or Guardian at:
http://www.snort.org/snort-files.htm

Installation Notes

$ wget http://www.psionic.com/tools/portsentry-1.1.tar.gz
$ wget http://www.psionic.com/tools/portsentry-1.1.tar.gz.sig
$ # import pgp key 0xBFB08FDA for Craig Rowland
$ pgp portsentry-1.1.tar.gz.sig
$ make linux
$ sudo make install
Creating psionic directory /usr/local/psionic
Setting directory permissions
Creating portsentry directory /usr/local/psionic/portsentry
Setting directory permissions
chmod 700 /usr/local/psionic/portsentry
Copying files
cp ./portsentry.conf /usr/local/psionic/portsentry
cp ./portsentry.ignore /usr/local/psionic/portsentry
cp ./portsentry /usr/local/psionic/portsentry
Setting permissions
chmod 600 /usr/local/psionic/portsentry/portsentry.ignore
chmod 600 /usr/local/psionic/portsentry/portsentry.conf
chmod 700 /usr/local/psionic/portsentry/portsentry


Edit /usr/local/psionic/portsentry/portsentry.conf and change
your settings if you haven't already. (route, etc)


WARNING: This version and above now use a new
directory structure for storing the program
and config files (/usr/local/psionic/portsentry).
Please make sure you delete the old files when
the testing of this install is complete.

Setup

Most configuration is of the /usr/local/psionic/portsentry/portsentry.conf file. I've made the following changes:

  To start with, we will NOT block scans:

	# 0 = Do not block UDP/TCP scans.
	# 1 = Block UDP/TCP scans.
	# 2 = Run external command only (KILL_RUN_CMD)

	BLOCK_UDP="0"
	BLOCK_TCP="0"

  And we will not used tcpd for this purpose.

	#KILL_HOSTS_DENY="ALL: $TARGET$"

Tests

TCP mode

Starting :
	./portsentry -tcp
and syslog shows all the ports that are being blocked.  netstat -a and
lsof -i show portsentry listening to all ports listed in 
configuation file.

From another host
	telnet 65.100.135.241 11
provokes portsentry to say:
  Jul 25 10:26:14 hillburkholder portsentry[6505]: attackalert: Connect from host: win4lin/65.100.135.245 to TCP port: 11
  Jul 25 10:26:14 hillburkholder portsentry[6505]: attackalert: Ignoring TCP response per configuration file setting.

And I can still SSH to port 22.  So, indeed, I'm not blocked.

From scanner, I can STEALTH scan and ports look open:
	nmap -sS -p 11 65.100.136.241
	11/tcp 	open	systat

Done
	killall portsentry

Stealth TCP mode

  ./portsentry -stcp

...
Jul 25 10:34:08 hillburkholder portsentry[6521]: adminalert: ERROR: Socket 111 is in use and will not be monitored. Attempting to continue 
# note, in '-tcp' mode the message is
Jul 25 10:45:42 hillburkholder portsentry[6554]: adminalert: ERROR: could not bind TCP socket: 111. Attempting to continue 

...

  scan to port 11:

Jul 25 10:35:06 hillburkholder portsentry[6521]: attackalert: TCP SYN/Normal scan from host: win4lin/65.100.135.245 to TCP port: 11
Jul 25 10:35:06 hillburkholder portsentry[6521]: attackalert: Ignoring TCP response per configuration file setting.
 
  stealth scan to port 1, 11:

Jul 25 10:36:32 hillburkholder portsentry[6521]: attackalert: TCP SYN/Normal scan from host: win4lin/65.100.135.245 to TCP port: 11
Jul 25 10:36:32 hillburkholder portsentry[6521]: attackalert: Host: win4lin/65.100.135.245 is already blocked Ignoring
Jul 25 10:36:56 hillburkholder portsentry[6521]: attackalert: TCP SYN/Normal scan from host: win4lin/65.100.135.245 to TCP port: 1
Jul 25 10:36:56 hillburkholder portsentry[6521]: attackalert: Host: win4lin/65.100.135.245 is already blocked Ignoring
  
  when a port first scanned, the host goes into the file 
  'portsentry.tcp' or 'portsentry.stcp' like this:
996079139 - 07/25/2001 10:38:59 Host: win4lin/65.100.135.245 Port: 27665 TCP Blocked

  on the first connect, then portsentry simply refers to that
  table for subsequent connects.


----
  setting the 'BANNER' doesn't seem to do anything in -stcp,
  but does in -tcp (as documented).

---
  now, setting this to actually block:
	  BLOCK_TCP="1"
And when we do kill_route, we'll use ipchains.
	# ipchain support for Linux
	KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY -l"

	# ipchains -L
	Chain input (policy ACCEPT):
	Chain forward (policy ACCEPT):
	Chain output (policy ACCEPT):

Nmap stealth scan show as open all ports to which portsentry
is bound.

Nmap tcp-connect scan shows as closed (221, 243, 382, 643, 775, 795, 894,
1248, 1471, 31337)
and all others as filtered.  Then, a Stealth scan shows that
the target host is down completely....


Syslog:

Jul 25 10:48:50 hillburkholder portsentry[6554]: attackalert: Connect from host: win4lin/65.100.135.245 to TCP port: 27665
...
Jul 25 10:49:04 hillburkholder kernel: Packet log: input DENY eth0 PROTO=6 65.100.135.245:1686 65.100.135.241:296 L=60 S=0x00 I=3589 F=0x4000 T=64 SYN (#1)
Jul 25 10:53:55 hillburkholder kernel: Packet log: input DENY eth0 PROTO=6 65.100.135.245:53558 65.100.135.241:80 L=40 S=0x00 I=5197 F=0x0000 T=49 (#1)

[and so on]

Advanced Stealth


------- advanced stealth -- 
./portsentry -atcp


Startup:
Jul 25 10:58:41 hillburkholder portsentry[6607]: adminalert: Psionic PortSentry 1.1 is starting. 
Jul 25 10:58:41 hillburkholder portsentry[6608]: adminalert: Advanced mode will monitor first 1024 ports
Jul 25 10:58:41 hillburkholder portsentry[6608]: adminalert: Advanced mode will manually exclude port: 113 
Jul 25 10:58:41 hillburkholder portsentry[6608]: adminalert: Advanced mode will manually exclude port: 139 
Jul 25 10:58:41 hillburkholder portsentry[6608]: adminalert: Advanced
Stealth scan detection mode activated. Ignored TCP port: 22 
... and so on for ignoring already bound ports ....

	'lsof -i' doesn't show portsentry even exits.

I can connect OK to port 80, then a stealth scan blocks me 
immediately.

If I delete the blocking from ipchains:
	ipchains -D input 1
Then I can scan I get lots of syslog messages in /var/log/messages
since portsentry sees the host in portsentry.blocked.atcp and doesn't
act on it other than reporting it.  This is part of the the 
portsentry design so it doesn't keep acting on the same IP, but
let's the action defined earlier do the job.

Comments

How fast is portsentry? An nmap scan that start at port 21 (monitored), then steps through 22, 80, 11, 139, 443, 515 (open ports), then into the monitored ports (516, 517, 518, ...) get to

When using nmap in -T Polite timing, the ping worked, then port 21 triggered portsentry (-atcp) and implemented ipchains before it even got to port 22. The same happens in -T Insane, the fastest I can scan with nmap. From my ethereal monitoring, this means in -atcp mode, portsentry kicks within 1/100th of a second. When in -stcp mode, it takes almost a second, and an "insanely" fast scan will show all or some open ports.

Final configuration

ADVANCED_PORTS_TCP="1024"
ADVANCED_PORTS_UDP="1024"
ADVANCED_EXCLUDE_TCP="113,139"
ADVANCED_EXCLUDE_UDP="520,138,137,67"
IGNORE_FILE="/usr/local/psionic/portsentry/portsentry.ignore"
HISTORY_FILE="/usr/local/psionic/portsentry/portsentry.history"
BLOCKED_FILE="/usr/local/psionic/portsentry/portsentry.blocked"
RESOLVE_HOST = "1"
BLOCK_UDP="0"
BLOCK_TCP="1"
KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY -l"
SCAN_TRIGGER="0"

Also, I added my local network to the ignore list.

Links

Return to Peter's Systems Administration Page.

Please feel free to contact me at pburkholder@pobox.com.