This page has information on tools that can be used to secure a system against attackers coming at it from the network.
- 1 References
- 2 Closing ports
- 3 fail2ban
- 4 Using iptables for network security
- 5 Protecting the FTP service
- 6 Using portsentry for protection against portscans etc.
- 7 Password handling
- 8 logcheck
- 9 Nessus
- General documentation
- Firewall security
- See the separate page on this wiki
The obvious first step to secure a system is to shut down any unneeded services. If a port is not open, an attacker cannot exploit it. I suggest this procedure:
- Identify open ports using a local tool such as
- Close unneeded ports
- Double-check that things work as intended by using an external port scanner
- Proceed to tighten security with firewall rules and/or a tool like fail2ban
Identify open ports
Open ports can be identified using
netstat. Here is the output from
pelargir after its reincarnation as a Dedicated Server:
root@pelargir:~# netstat -lnptu Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 6160/postgres tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 49598/exim4 tcp 0 0 0.0.0.0:389 0.0.0.0:* LISTEN 36937/slapd tcp 0 0 0.0.0.0:48645 0.0.0.0:* LISTEN 439/rpc.statd tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 419/rpcbind tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 484/sshd tcp6 0 0 ::1:5432 :::* LISTEN 6160/postgres tcp6 0 0 ::1:25 :::* LISTEN 49598/exim4 tcp6 0 0 :::443 :::* LISTEN 39875/apache2 tcp6 0 0 :::389 :::* LISTEN 36937/slapd tcp6 0 0 :::42438 :::* LISTEN 439/rpc.statd tcp6 0 0 :::111 :::* LISTEN 419/rpcbind tcp6 0 0 :::80 :::* LISTEN 39875/apache2 tcp6 0 0 :::22 :::* LISTEN 484/sshd udp 0 0 127.0.0.1:615 0.0.0.0:* 439/rpc.statd udp 0 0 0.0.0.0:1005 0.0.0.0:* 419/rpcbind udp 0 0 0.0.0.0:44084 0.0.0.0:* 439/rpc.statd udp 0 0 0.0.0.0:111 0.0.0.0:* 419/rpcbind udp 0 0 126.96.36.199:123 0.0.0.0:* 17205/ntpd udp 0 0 127.0.0.1:123 0.0.0.0:* 17205/ntpd udp 0 0 0.0.0.0:123 0.0.0.0:* 17205/ntpd udp6 0 0 :::1005 :::* 419/rpcbind udp6 0 0 :::50893 :::* 439/rpc.statd udp6 0 0 :::111 :::* 419/rpcbind udp6 0 0 ::1:123 :::* 17205/ntpd udp6 0 0 fe80::42a8:f0ff:fe7:123 :::* 17205/ntpd udp6 0 0 :::123 :::* 17205/ntpd
- Those services listening on 127.0.0.1 (IPv4) and ::1 (IPv6) can be ignored
- Those listening on 0.0.0.0 (IPv4) and :: (IPv6) are also listening on the outgoing interface
- OK are
- Not OK are
Close unneeded ports
- I need OpenLDAP, but it doesn't need to listen on 127.0.0.1
/etc/default/slapdmodify the entry for
- slapd now only listens on the internal Unix domain socket
rpcbind and rpc.statd
- First we need to find out why this package is installed at all
root@pelargir:~# which rpcbind /sbin/rpcbind root@pelargir:~# dpkg -S /sbin/rpcbind rpcbind: /sbin/rpcbind root@pelargir:~# aptitude why rpcbind i nfs-common Depends rpcbind
- Since I don't need to mount NFS I removed the
nfs-commonpackage, and was thus also able to remove the
- After much try & error I had succeeded in preventing
ntpdfrom listening on the public interface (with an "interface ignore" statement in
/etc/ntp.conf) but this caused
ntpdfrom functioning correctly
- In the end I had to give up and let
- For more details see the NTP page on this wiki
Other ports that were open in older versions of Debian:
- 79/finger: Can be closed by removing the package
- 111/portmapper: Can be closed by removing the package
portmap. NFS used to require the portmapper, not sure if this still is the case today.
- 113/ident: Can be closed by removing the package midentd
If you don't want to uninstall packages, another way how to close some ports / disable some services is by editing
Currently I'm using
fail2ban to provide automated security for my box. See the separate page on this wiki.
Using iptables for network security
Note: I no longer use the
iptables rules in this section, they may or may not work as presented here.
For basics about how iptables is working, see the iptables chapter on the NetworkConfiguration page.
It is possible to use special iptables modules for extended functionality. The modules are referenced by name with -m <module> or --match <module>. After -m has been specified, additional command line options become available.
About the recent module
The recent module is capable of temporarily storing IP addresses in tables, and to query those tables if they contain a certain IP address. This feature can be used to give iptables a memory of past behaviour of network clients, and to react to behaviour that becomes abusive over time.
Protection against SSH brute force attack
In a nutshell
This chapter presents 3 iptables rules that effectively block a network client's connection attempts on port 22 (the SSH port), if that client has made more than 5 connection attempts during the last 20 seconds.
Offenses are logged to the system log, where you can examine the problem at leisure. For instance, I have written a small script that parses the system log files and produces a table digest with details about offending IP addresses:
iptables -A INPUT --source ! 192.168.1.0/24 -p tcp --dport 22 -m state --state ESTABLISHED -m recent --set --name SSH -j ACCEPT
Dissemination of the rule:
- store all addresses that are the source for a connect to the ssh port (--dport 22) in the table "SSH"
- the "SSH" table is internal to the "recent" module; it is not an iptables table that can be used with -t
- only match successful TCP/IP connections (--state ESTABLISHED), so that packets with forged source address cannot lead to problems (e.g. someone sends packets whose source address = my DNS server)
- it is basically very simple to forge the source address of a single packet; if we would react to an initiating SYN packet someone could easily provoke us into blocking the wrong source address
- an established TCP/IP connection means that our server has already sent a SYN/ACK packet in response to the initiating SYN packet; the server is now getting another answering ACK packet, and this ACK packet is matched by the above rule
- if someone wants to provoke us into blocking a wrong source address, that party must therefore send us an acceptable ACK packet; to make the ACK packet acceptable, the bad guy must have information from our server's SYN/ACK packet, and that is quite difficult - the bad guy must be able to manipulate routing so that our server's SYN/ACK packet is not sent to the SYN packet's forged source address, but to one of the bad guy's hosts where he can extract the necessary information to forge the ACK packet
- I can only imagine one case where this is possible: the bad guy must have control of my ISP's network, e.g. because the bad guy is the ISP itself
Rules 2 + 3
Rules 2+3 are now used to evaluate the content of the "SSH" table:
iptables -A INPUT --source ! 192.168.1.0/24 -p tcp --dport 22 -m recent --rcheck --seconds 20 --hitcount 6 --rttl --name SSH -j LOG --log-prefix "SSH_brute_force" iptables -A INPUT --source ! 192.168.1.0/24 -p tcp --dport 22 -m recent --update --seconds 20 --hitcount 6 --rttl --name SSH -j DROP
Dissemination of the rules:
- ignore all packets (-j DROP) from addresses that have initiated 5 or more connects in the last 20 seconds to the ssh port
- the key option is --update: it checks if the address is already present in the "SSH" table, together with the extended search operators --seconds, --hitcount and --rttl
- if the check returns false, the entire rule evaluates to false, i.e. the rule's target is not executed and the entry in the "SSH" table is not updated
- in other words: before the -j DROP target is executed for the first time, the source address must have established 5 TCP/IP connections to port 22 in the last 20 seconds
- if the check returns true, the target is executed (i.e. the packet is dropped) and the hit count is increased by 1
- in other words: as soon as the threshold has been exceeded every additional packet is dropped, i.e. on the 6th connection attempt the initiating SYN packet is already dropped
- in order to get "unlocked", the bad guy has to remain quiet for 20 seconds (maybe less, the important thing is that in the last 20 seconds not more than 5 hits have occurred for the same TTL), or he needs to come in with packets that have a different TTL (usually this means a different IP route, but of course our hypothetical bad guy may easily manipulate his IP packets to contain arbitrary TTL values)
- the -j LOG rule uses --rcheck instead of --update in order not to increase the hit count unnecessarily
- it would be possible to use -j REJECT instead of -j DROP; this is more network-friendly, but it also lets the bad guy know that his packets were rejected
In order to examine the current content of the "SSH" table:
Note on table size: A certain table of the recent module is limited to 100 entries by default. In order to raise this limit the kernel module must be inserted into the kernel using a special parameter:
modprobe ipt_recent ip_list_tot=<maximum>
Protecting the FTP service
The best way, of course, to protect the FTP service is to disable it completely. If you want to keep the service alive (for whatever insane reasons you have), you will soon notice that brute-force attacks are mounted against you. You can protect yourself by using fail2ban or a similar tool, but first and foremost you should take care to disable FTP for all users except those that really need the service.
This can be done by editing this file:
All users that are listed in this file are not allowed to log in. Typically
root and other special system users are already listed in this file, but you may want to double-check to be sure.
Note: Anonymous FTP access is possible only if the user
ftp exists in
/etc/passwd. This user may or may not exist on your system - double-check to make sure it isn't there.
Persistent attackers can be taken care of by the following iptables rule:
iptables -I INPUT -s 188.8.131.52 -p tcp --dport 21 -j DROP
Using portsentry for protection against portscans etc.
portsentry monitors the network interfaces of a machine and takes steps if it detects a port scan in progress. Basically you tell portsentry which ports on a machine are unused, and how it should react if it detects activities on these unused ports.
Note: I no longer use portsentry because the lower port ranges are now effectively blocked by my ADSL router. Before I switched to ADSL, I was using a cable modem which left these ports unprotected. I keep the following section should I ever decide to start using portsentry again.
Identify ports in use
The first step is to identify which ports are in use. You can do this either by invoking
netstat --listening, by using one of the many port scanning tools available on the web (e.g. http://www.grc.com/, look for "Shields UP!"), or by executing a command line port scanning program such as nmap:
pelargir:~# nmap 192.168.0.2 Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2007-01-06 01:31 CET Interesting ports on 192.168.0.2.local.home (192.168.0.2): Not shown: 1666 closed ports PORT STATE SERVICE 21/tcp open ftp 22/tcp open ssh 25/tcp open smtp 80/tcp open http 111/tcp open rpcbind 139/tcp open netbios-ssn 143/tcp open imap 389/tcp open ldap 443/tcp open https 445/tcp open microsoft-ds 873/tcp open rsync 901/tcp open samba-swat 993/tcp open imaps 8000/tcp open http-alt Nmap finished: 1 IP address (1 host up) scanned in 0.179 seconds
Disabling ports that are not needed
It might be a good idea to think about if you really need all those open ports. See the section Closing ports further up for details.
The actual configuration of portsentry is done by modifying one or more of the following files. For each file I give a list of the changes I made to the defaults.
- RESOLVE_HOST="1" (enable DNS name resolution)
- KILL_ROUTE="/sbin/iptables -I INPUT -s $TARGET$ -j DROP" (use iptables instead of route for blocking attackers)
- commented the KILL_HOSTS_DENY entry
Note: I could not find out what ports 515 (printer), 548 (afpovertcp), 67 (bootps) and 68 (bootpc) were, so I left them in the exclude lists for the moment.
- 184.108.40.206/32 (gw.schiller.ch)
- 172.16.0.0/19 (internal network cable ISP)
- 10.0.0.0/8 (internal network cable ISP)
Let the daemon loose
After (re-)starting the daemon to let the modified configuration take effect, portsentry begins placing more and more hosts on iptables black list. In order to see that list:
iptables -t filter -L -n
To remove a host from the black list:
iptables -t filter -D INPUT -s <host> -j DROP
Traditional UNIX accounts
Traditionally, UNIX accounts are kept in system files such as /etc/passwd. Password handling can be varied, though, as detailed in the following short sections.
The term "shadow passwords" means that passwords (or better: their hashes) are stored in the protected system file /etc/shadow, instead of in the system file /etc/passwd which can be read by anyone.
Shadow passwords should always be enabled! If not enabled, you can turn it on by saying
Traditionally, each user's password is encrypted by the crypt algorithm into a hash. That hash is placed into either /etc/shadow, or /etc/passwd if shadow passwords are disabled (which is not a good idea, see above).
Because more modern hashing algorithms provide better protection against hash collisions than crypt, it is desirable to configure the system to use such a modern algorithm for generating password hashes. The hashing algorithm used in a given password in /etc/shadow is recognizable by its $algorithm$ prefix. Supported algorithms are listed in the crypt(3) man page. For instance,
$1$ represents for MD5,
$6$ represents SHA-512.
A modern hashing algorithm is usually enabled by default on a Debian system. If you ever need to change the algorithm you need to edit the system's PAM configuration. For instance, the following configuration uses SHA-512:
root@pelargir:~# grep "^password" /etc/pam.d/common.password password [success=2 default=ignore] pam_unix.so obscure sha512 [...]
See the PAM wiki page for more information on PAM. I have not investigated how to switch between crypt and other hashing algorithms on a system that does not use PAM.
Accounts stored in LDAP
Instead of storing account information in system files (traditional approach), it is possible to manage accounts within an LDAP directory. In this case, the above stuff about shadow passwords and password hashing algorithms does not apply.
The system log files contain such a huge amount of information that it is unpractical - if not outright impossible - to monitor all files for the occasional blip that might indicate a break-in attempt or some other irregularity.
The logcheck tool automatically performs this task for us. Run periodically by cron, logcheck scans those log files that it was configured for and checks if they contain stuff that looks "suspicious". What exactly is (or is not) considered suspicious can be defined through an elaborate set of regular expressions. If suspicious content is discovered, the system administrator is notified by email.
- Main site = http://logcheck.org/
- Detailed information (that was hard for me to understand) about how rules work: http://logcheck.org/docs/README.logcheck-database
- The code of the script /usr/sbin/logcheck
All configuration files are stored in
- The main configuration file is /etc/logcheck/logcheck.conf
- The file that lists the log files to be examined is /etc/logcheck/logcheck.logfiles
- The rules that determine whether or not a log file entry is considered "suspicious" are stored in various sub-directories (e.g. /etc/logcheck/violations.d)
In the main configuration file logcheck.conf I usually set the following things:
- Date format
- Recipient of warning emails
- Report level = server
- Enable support for cracking.ignore.d
How does it work?
In a first step, all log files to be examined are collected in one big text file. At this point, logcheck remembers up to which position it has processed every log file so that in a subsequent run it will not parse the same log file content a second time.
Depending on the report level, different run-parts directories in /etc/logcheck are now evaluated:
- cracking.d and cracking.ignore.d are always evaluated (although cracking.ignore.d can be disabled in the main configuration file)
- violations.d and violations.ignore.d are always evaluated
- The report level determines which kind of the remaining ignore.d directories are evaluated
- The more loose the report level, the more ignore.d directories are evaluated
- For instance, report level "server" causes these directories to be evaluated
The entire report generated by logcheck is mailed with a subject that corresponds to the most critical message found. For instance, if the cracking.d patterns have yielded a result, the subject will be "Security Alerts", even if at a later stage logcheck finds messages that are less critical.
The report can have three types of messages:
- Type/level 1: cracking.d patterns minus cracking.ignore.d patterns
- Type/level 2: violations.d patterns minus violations.ignore.d patterns minus cracking.d patterns
- Type/level 3: All messages minus ignore.d.* patterns minus violations.d patterns minus cracking.d patterns
Positive matching of patterns vs. ignore patterns
- A message that was positively matched on levels 1 or 2 is always ignored on a lower level; in these cases, the ignore patterns of the lower levels are not relevant
- A message for which an ignore pattern exists on levels 1 or 2, but which was not positively matched, may still be matched on a lower level; in other words, ignore patterns of a higher level are not relevant for a lower level
How patterns are processed
Lines in a pattern file that begin with "#" are treated as comments. All other lines are egrep regular expressions. This means that, for instance, parantheses need to be escaped ("\(foobar\)").
The way how patterns are processed for cracking.d and violations.d is not obvious. A basic overview has been given in the previous chapter, the following list provides more details.
- Files in
violations.d) are processed one after the other; each file is supposed to "belong" to a package
- The patterns in a package file are used to find "suspicious" messages, i.e. to find positive matches
- If the patterns for a package file "foo" yield one or more positive matches, these matches are now cleaned as follows
- If there is a file with the same name (e.g. "foo") in the cracking.ignore.d (or violations.ignore.d) folder, the ignore patterns in that file are applied
- Also applied are the patterns in an ignore file with the same name and with a prefix "logcheck-" (e.g. "logcheck-foo")
- Also applied are the patterns in an ignore file named "local"
- Also applied are the patterns in all ignore files with a prefix "local-"
The list of files consulted for ignore patterns is even longer (at least it was when I last checked the script code of logcheck), but I am not interested in enumerating all these esoteric uses. The most important conclusion from the above list is:
cracking.ignore.d/local and violations.ignore.d/local contain ignore patterns that are applied for all packages.
In addition, it should be noted that if the ignore pattern file ignore.server.d/local exists, its name has no special meaning. This is because package names are only relevant for cracking and violations.
logcheck can be executed manually at any time, just like that without specifying any parameters. To get a hint about what logcheck actually does, run it in debug mode
The best way to use logcheck is to run it periodically and automatically through the cron service. When you install the Debian package, this is already enabled by default. The cron script is stored in
It will execute logcheck every day at 2am, and at reboot time.
After you have taken all kinds of measures to make your system invulnerable to attacks, at some point you will start wondering whether your actions are successful. Is your machine, in effect, secure?
Nessus is a security scanner that you can use to test your system for vulnerabilities. You select a number of more or less destructive tests, then let Nessus execute them in an attempt to break into (or break down) your system.
A useful three-part introduction article about Nessus can be found here:
- Part 1: http://www.securityfocus.com/infocus/1741
- Part 2: http://www.securityfocus.com/infocus/1753
- Part 3: http://www.securityfocus.com/infocus/1759
On my Mac, I use fink to install Nessus.
Create a user with the command
The tool is interactive and queries you for the following information
- User name
- Ruleset that specifies what the user can or cannot do; you will probably enter nothing here (just hit Ctrl+D)
On my Mac, the user data is stored in
Next you need to create a certificate that is going to be used to encrypt the traffic between the Nessus client and server. The following command accomplishes this:
On my Mac, this creates/updates the following files and directories:
/sw/etc/nessus/nessusd.conf /sw/var/nessus /sw/com/nessus
Finally, you should update your installed set of Nessus plugins. You should do this before each scan because plugins are often updated, or new plugins become available, to test vulnerabilities that were newly discovered. A plugin update can be done with
Doing a test
Start the daemon (use -D if you want the daemon to detach itself from the console and run in the background):
Start the client:
Connect to the daemon by entering network details, user name and password.
On the "Plugins" tab, select the plugins you want to run. By default, all "dangerous" plugins are disabled (i.e. plugins that have the potential of crashing the machine).
On the "Scan Options" tab, select "Nmap (NASL wrapper)" to be used as a port scanner. If you want you can disable the "safe checks" option, which should be enabled by default.
Finally, on the "Target" tab you enter the host you want to scan, check the and click the "Start the scan" button at the bottom of the client window.
Audit of osgiliath.herzbube.ch on 28.05.2007
- Dangerous plugins = off
- Safe checks = on
Revealed weaknesses that I fixed:
- On TCP ports 6969 and 9696 an instance of nngssrv (No Name Go Server) was listening; I had the thing installed a few months ago when I was evaluating turn-based Go servers, but then forgot to kill the process when I was done
Revealed weaknesses that I need to consider for the future:
- Web server supports TRACE and/or TRACK methods which can be used for cross-site-scripting attacks
- LDAP server on port 389 may allow - if improperly configured - the retrieval of information by an anonymous user through NULL BASE queries
- Web server offers 4 strong and 2 weak SSLv2 ciphers; the weak ciphers - if chosen by a client to communicate with the server - may not be strong enough to resist brute force attack
Nessus also pointed out other potential weaknesses (e.g. web clients can browse the /doc directory, thus revealing what kind of programs are installed on the machine) that I did not fix because I thought they were not severe enough to harm my small private server.