Basic Linux Security

I'm often asked whether Linux is as secure as Windows. Please don't ask me that when I have a mouthful of coffee, as coffee stains can be hard to remove from some fabrics.

Of course, Linux is more secure than Windows. It could hardly be less secure, could it? There are several reasons for this:


Despite the inherent security of the platform, Linux users shouldn't be complacent. While there are far, farfewer worms and viruses for Linux, there are some script-kiddie attacks and vulnerabilities in major subsystems from time to time.

With that in mind, here's some advice for those about to connect a Linux system to the Internet for the first time.

Install Linux Securely

When planning your installation, you can take advantage of some of the kernel's features to make the system a tougher target. For example, the kernel can mount filesystems (partitions) read-only. By making the /usr subdirectory a separate filesystem, you can mount it read-only, which makes it a little more difficult for an attacker to upload a rootkit or otherwise modify the files in /usr.

If concerned about physical security, you should password-protect the BIOS settings so an attacker cannot boot the system off floppy or CD-ROM, and you should also password-protect the boot loader (LILO or GRUB) configuration so that she cannot change the kernel command line.

Choose Strong Passwords

Traditional UNIX systems encrypted the users' passwords using an algorithm called salt+crypt and then stored then results in the /etc/passwd file, On login, the system would take the login password, encrypt it in the same way and compare it with the entry in /etc/passwd. If the two agreed, then the user was considered authenticated.

There are two problems with this. First, there is the fact that, while salt+crypt is a one-way algorithm (you can't decrypt the contents of /etc/passwd) it is still not very strong, and is limited to eight-character passwords. Worse, the file /etc/passwd has to be readable by all users, since it relates user ID numbers to names, locates home directories and contains other information about users. This makes it vulnerable to so-called dictionary attacks, in which the attacker takes a dictionary (and many systems have one for spell-checking), encrypts every word in it, and then compares them one-by-one against the encrypted passwords in /etc/passwd. When a match is found, the attacker simply looks up the corresponding word in the dictionary and that's the password.

The solution to the first problem was to switch to a stronger encryption algorithm. Modern Linux systems usually use MD5, which generates a 128-bit hash from arbitrary-length input and is much, much harder to crack. The second problem is solved by moving the passwords out of /etc/passwd and into /etc/shadow, which is readable and writable only by root.

However, it is always possible that an unforeseen vulnerability in an application or daemon somewhere will allow a remote attacker to get a copy of the shadow password file. If this happens, he can go to work on it with a cracking tool like John the Ripper, Crack or Slurpie, which perform dictionary attacks and brute-force attacks. A serious attacker can afford to let this run for days, but experience shows that any weak passwords - dictionary words and derivatives (like the word spelled backwards) will be turned up in a few hours at the most. So:

Never use dictionary words as passwords. Never use any information which an attacker could find out about you or guess, such as your car registration, your spouse's name, pet's names, etc.

Remove Non-Essential Services

Things have changed since the early days of Linux, when some distributions turned everything on in an attempt to demonstrate what good value it was. See? You have a web server! See? You have an FTP server! See? You have NFS, NIS, Samba, ntpd, and lots of other lovely toys to play with! Yes, I see. . . Unfortunately, so does the next hacker to stumble across your box of toys.

These days, services aren't enabled by default. Even if you want Apache to run, after installation it will be disabled in the startup scripts. However, a few things seem to creep past the packagers; for example, Red Hat 9 enables NFS (Sun's Network File System) by default, which is a concern as NFS, and the portmapper service it depends upon, is one of the leading weaknesses of the Linux/Unix world. To disable NFS on a Red Hat system, give the following commands:

service nfslock stop
service nfs stop
service portmap stop
chkconfig nfslock off
chkconfig nfs off
chkconfig off

The service command is supported in many distributions that have the SysVInit scripts package. If you don't have service, you can stop a service by running its script directly - for example:

/etc/rc.d/init.d/nfslock stop

or

/etc/init.d/nfslock

The exact location of the start/stop scripts varies from distribution to distribution. The chkconfig command is pretty much unique to Red Hat, but there are many other utilities that can manipulate the symbolic links that control whether (and when) different services start and stop: for example, ntsysv, ksysv and Webmin's System -> Bootup and Shutdown module, to name a few.

Use the ps aux command to list the processes running on your system, and look for unwanted daemons such as nfsd, named, httpd (Apache), smbd (Samba), sendmail and others you may not want. As a double-check, use the netstat -put command to see what daemons are listening on ports for outside connections.

Perform Post-Installation Hardening

Once your system has been set up, there are a few more routine tasks that can further enhance security:


If you really want to nail down the system. consider installing and running a comprehensive system hardening script like Bastille Linux (http://www.bastille-linux.org/). This makes lots of detailed changes on your system. As Bastille runs, it tells you what it is doing; you can choose to skip certain steps, but you will certainly learn a lot about security along the way.

Install an Intrusion Detection System

One of the worst things that can happen is for an attacker to compromise your system, pilfer your files, and then use your system to launch an attack on yet another victim, while you sit blissfully unaware of what's going on. To make sure that alarm bells ring when someone tampers with your setup, you should install an intrusion detection system. These come in two basic flavours: host integrity verification systems, and network intrusion detection sensors.

A host inegrity verification system makes a snapshot of all the critical files on your system: the configuration files in /etc, the binary program files in /bin, /sbin, /lib and under /usr, critical files in /var, and so on. To do this, it records a fingerprint for each file: the file pathname, size, date/time stamps, permissions and ownership, inode number and - perhaps most importantly - an MD5 of the file's contents. It stores all this information in a digitally signed database, and then runs periodic checks to see if anything has changed. Of course, if it has, you know someone is up to no good.

Examples of host integrity verification systems include Tripwire (http://www.tripwire.org) which is included in many distributions and AIDE (Advanced Intrusion Detection Environment: http://www.cs.tut.fi/~rammer/aide.html). Both of these need to be set up in advance of the system being exposed to the shark-infested waters of the Internet, however; if you haven't prepared in advance and you are concerned that your system might have a rootkit installed, try chkrootkit (http://www.chkrootkit.org).

In firewall configurations, you can also install a network intrusion detection system. This basically examines network datagrams, looking for the signatures of various common types of attacks: CGI exploits, certain types of denial-of-service attacks. Perhaps the best-known network IDS is Snort (http://www.snort.org) which can be installed as anything from a basic sniffer to a sensor for a comprehensive database-backed intrusion analysis system.

The big end of town is making a lot of noise about intrusion prevention systems; these are basically IDS's with the ability to automatically add firewall rules which will block an attack. The Linux world has had this for years in the form of PortSentry, an application that can be hard to find online since the company that produced it was taken over by Cisco (but a little googling will turn it up). With PortSentry installed, as soon as someone runs a port scan and hits, say, port 20, you can block their IP address. The beauty of this is that they'll never figure out that you are running a vulnerable version of SSH on port 22 as they are blocked before they get there.

Remote Logging

Wherever possible, make sure that Internet-exposed hosts not only save their logs locally, they also log to a host that is safely inside your firewall. For this to work, you need to invoke the syslogd daemon on the internal logging server with the "-r" option. On Red Hat systems, for example, edit /etc/sysconfig/syslog and set SYSLOGD_OPTIONS="-r".

Next, on the host you want to defend, edit /etc/syslog.conf and set a destination for the desired facilities and priorities to @loghost (where loghost is the logging host, well inside your firewall). For example:

authpriv.* @loghost
*.emerg @loghost

Setting this up means that, in order to cover his tracks, an attacker has to not only edit the log files on the compromised host, but also break through the firewall into the logging host and edit the files there as well - as fast as possible. Most attackers will realise the difficulty of this and back off, real fast. The install scripts of rootkits often check for remote logging and issue warnings to the attacker, who will then vanish into the night.

Use iptables to Protect Bastion Hosts

Create an iptables firewall script to block all datagrams except those destined for the services you choose to make available. On a Red Hat or Fedora system, start with a /etc/sysconfig/iptables file something like:

# iptables rules for ${HOSTNAME} version 0.1 by LB, 2004-05-11
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
[0:0] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
[0:0] -A INPUT -i lo -j ACCEPT
[0:0] -A OUTPUT -o lo -j ACCEPT

# Accept inbound mail connections
# [0:0] -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT

# Accept inbound DNS connections
# [0:0] -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT
# [0:0] -A INPUT -p udp -m udp --dport 53 -j ACCEPT
# Allow incoming SSH connections
[0:0] -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT


Uncomment the lines for mail, DNS, etc as required, and add other lines to allow access to services like htt (port 80) and so on.

Always remember the Golden Rule: That which is not expressly permitted, is denied. That's the way these firewal rules work: A few ports are specifically ACCEPTed, and everything else gets DROPped. Don't ever worry about whether you should block access to some port or other; if you're thinking that way, you have the concept bass-ackwards. You should only thing about adding rules to allow access to specific services that you know are safe and choose to expose. Everything else is blocked anyway.

Learn to Love SSH

Telnet? We don't need no steenkin' telnet!

Anyone who runs a telnetd (telnet server) on a modern Linux system needs his head examined. The trouble with telnet, and similar protocols/services like rsh, rlogin, rcp and ftp, is that they transmit the user's login name and password across the LAN - and potentially, the Internet - as clear text. This can be sniffed off the wire, using special-purpose, or even general-purpose sniffers and protocol analyzers. (Don't think that switched networks reduce the risk here - in fact, they make it even easier for attackers in some respects). Worse still, all subsequent data is also passed in the clear, so that when you su to obtain root privileges, the sniffer can obtain the root password.

So, instead of these weak protocols, use SSH, the Secure SHell. This does everything that the other commands do, and does it simpler, faster (it can optionally compress data being transferred, improving performance on slow links) and more securely. SSH is more secure for two primary reasons:


The use of encryption is fairly easy to understand - this means that when you send a username or password through the session it cannot be sniffed by an attacker. The authentication is important, too, though, as otherwise, an attacker could simply poison DNS and set up a fake system that pretends to be the one you want to log in to. Once you give this torjan horse your username and password, the attacker can re-use it to get into the target system (this is the basis of the online banking phishing scams).

To defeat this, SSH uses public key encryption. Your local workstation challenges the remote system to encrypt a challenge with its private key, then decrypts the response with the remote system's public key. If they don't match, then that's not the correct remote system, and with strict checking enabled (as it is by default on many systems these days) you will not even be allowed to log in.

The same technique can optionally be used to authenticate users. You generate a private/public key pair, then upload your public key to the remote system for future reference. From this time on, you can load your private key (which is usually password-protected, so it can't be stolen or misused) and the remote system will similarly challenge your system to respond to a challenge. This means that you no longer authenticate by a password, making it easier to deal with multiple systems, and by using the ssh-agent you can unlock your private key once and then log into system after system with no password prompts

Most Linux distributions ship with OpenSSH, and it is enabled by default, allowing you to connect to your system from elsewhere.

[les@sleipnir les]$ telnet artemis.rr.lesbell.com.au 22
Trying 192.168.169.181...
Connected to artemis.rr.lesbell.com.au.
Escape character is '^]'.
SSH-1.99-OpenSSH_3.8p1

Protocol mismatch.
Connection closed by foreign host.
[les@sleipnir les]

Check for SUID binaries

In order to allow users to perform some tasks which would normally require access to system resources owned by root, the corresponding binaries have the setuid bit turned on in their permissions. Such programs run with the effective user ID (EUID) of the program's owner (usually root) and not the user running them. Obviously, if an attacker is able to compromise such a program, and perhaps get it to do something like spawning a sub-shell, then the attacker will be able to get a sub-shell with super-user privileges, and now 0wjns your system. Not good.

Use the following command to search your system for SUID executables:

[root@sleipnir root]# find / -perm -4000 -exec ls -l {} \;
-rwsr-xr-x    1 root     root        35376 Feb 13  2003 /usr/bin/chage
-rwsr-xr-x    1 root     root        36216 Feb 13  2003 /usr/bin/gpasswd
-rws--x--x    1 root     root        14140 Feb 25  2003 /usr/bin/chfn
-rws--x--x    1 root     root        11644 Feb 25  2003 /usr/bin/chsh
-rws--x--x    1 root     root         4728 Feb 25  2003 /usr/bin/newgrp
.
.
[root@sleipnir root]#

A switched-on administrator will know just what programs on his system are supposed to be SUID, and will investigate new ones that turn up. Some third-party applications sometimes install SUID programs, and you will need to consider the risks inherent in such programs.

Keep Your Path Righteous

Users who come from the world of DOS and Windows soon realise that Unix shells don't search for programs in the same way as COMMAND.COM or CMD.EXE. Those shells will check to see if a command is built in, then search the current directory, and only then fall back on searching the directories in the PATH environment variable. Unix shells check to see if the command is a built-in, alias or function, and then start searching $PATH without searching the current directory. That's why you see people typing

./script-name

- the "./" part forces the shell to run the script in the current directory. After forgetting this a few times and typing commands twice, novices sometimes think they'll save themselves some grief by editing their .bashrc or similar login script and adding a line like

PATH=./:$PATH

so that the current directory is at the front of the path. Don't do it! There's a good reason why Unix shells don't search the current directory - basically it is that the current directory is often set up by some other user, who could stick Trojan Horse variants of common commands like ls in there, and you'd be executing his trojanned version without realising it. A particularly common exploit in the old days was for a user to create a script in their directory which would make a SUID copy of /bin/bash and hide it, then run the expected command. Along comes an unsuspecting junior administrator, runs the trojanned script, and hey presto! - the cunning user now has a private copy of the bash shell that always runs with root privileges.

Never Put Off To Tomorrow. . .

The single most common reason for systems being compromised is the use of old, out-of-date and known-to-be-vulnerable versions of Linux subsystems. Around half of the compromised systems I see are due to mis-configuration (leaving NFS running, etc.) and the other half have been down to running old versions of wu-ftpd, sendmail and BIND. Make sure that your systems are patched as soon as vulnerabilities are announced. Subscribe to your distribution's security announcements mailing list, and make use of services like Red Hat's up2date and Red Hat Network to automatically apply updates. For Fedora, use yum or apt-rpm; for Debian, use apt-get. These tools are so much more powerful than the much-vaunted Windows Update, there's no excuse for not using them.

Tech Talk

rootkit: A replacement set of programs for common commands such as ls, ps, login, netstat, etc. which are modifed to provide an attacker with a back door into a compromised system and at the same time hide his files, processes, sockets, etc. Once a system has had a rootkit installed on it, you cannot trust the evidence of your own eyes and will find it very difficult to figure out whether it has been compromised or not. Rootkits used to be a Unix-specific phenomenon, but there are now rootkits for Windows.

MD5: A hashing algorithm, which takes variable-length input and produces a 128-bit "fingerprint" as output. According to RFC1321 ("The MD5 Message-Digest Algorithm"), "It is conjectured that it is computationally infeasible to produce two messages having the same message digest, or to produce any message having a given prespecified target message digest".

References and Further Reading

RFC1135 The Helminthiasis of the Internet.

RFC 1321: The MD5 Message-Digest Algorithm. Available online at ftp://ftp.rfc-editor.org/in-notes/rfc1321.txt

Les Bell is a Red Hat Certified Engineer (RHCE) and a Certified Information Systems Security Professional (CISSP)
Page last updated: 23/May/2004 Back to Home Copyright © 1987-2010 Les Bell and Associates Pty Ltd. All rights reserved. webmaster@lesbell.com.au

...........................