By Jay Beale ( email@example.com ), Lead Developer, Bastille Linux Project, President JJB Security Consulting and Training (C) 2002, Jay Beale
In my last article, "Why do I have to harden?", I discussed how security exploits develop and why you must do more than just patch. Here, I explain what that "do more" bit means... "Hardening" a system is the practice of making that system much harder to crack. I like to think that this involves steps not only to prevent break-ins, but also to detect them when they happen. To this end, I use the following steps/guidelines in tightening security on a Linux/Unix system:
Patch, patch, patch!
Perform a Set-UID root audit of the system
Deactivate all unnecessary network daemons/services
Tighten the configuration s of all remaining network daemons
Harden the core O/S itself
(A little) Intrusion Detection
Educate the SysAdmin and end-users
A standard firewall simply blocks TCP/UDP/ICMP packets according to rules you specify. Usually, you use these to restrict incoming traffic to certain programs/services. For example, you should block incoming NFS/Samba requests from the Internet, as these file-sharing protocols are only designed for local area network use. In addition, you might block off the Windows file-sharing ports, tcp/udp 137-139, to block the increasingly popular macro-virus trojan-ing method. Kurt Seifried's Linux Administrator's Security Guide has excellent information on firewalling particular applications.
Actually, I generally recommend a "default-deny" firewall; that is, a firewall that blocks all traffic except that which you explicitly permit. In other words, if your machine(s) offer only ssh(port 22), mail(port 25), and web(port 80) services, then all other incoming TCP connection attempts should be refused. You'd implement this by permitting all established incoming connections, along with new connections to ports 22, 25 and 80. You'd then deny all other incoming TCP traffic. This stance is a good deal of work, especially at the enterprise level, as you work to figure out exactly what programs/protocols you use. You can make this easier, though, by setting your default policy to "accept and log remaining," for a little while. This lets you deny very little, logging everything that would have been denied by your "default allow" policy.
If possible, firewall your machine both through the kernel's built-in packet filtering and through a packet filter/firewall on your internet connection. In an enterprise, you might even place packet filtering rules on several of your internal routers and switches.
Patching is massively important! A machine running a year-old Operating System version is usually rather vulnerable.2 Patching doesn't take much time and is, hands down, one of the most effective steps you can take towards thwarting crackers. It's really important to keep up with this, by the way - as I showed in my last article, your window of vulnerability is pretty long without procrastinating on patches.
Automate the patch process, if possible, to warn you of new patches as soon as they're released. If you're running Red Hat, take a good look at William Shotts' RH-Errata. This can automatically list the patches you need and download them from an FTP site... If you're not running Red Hat, look into other solutions to, at least, list missing security patches.
Normally, when you log into a system and run a program, it is run with your UID (User ID), so it has exactly the same privileges that you have. Set-UID root programs run with root's UID (0), and thus with root's privileges. When one of these programs has either a bug or an oversight, a cracker can use it to get permanent root access. Since you're not sure which Set-UID root programs will have this kind of problem, you just try to minimize the total number on your system to reduce the number of paths to root access.
On a Linux/Unix system, there are a number of network services/daemons, which listen on TCP/UDP ports and offer remote services. Some examples include Sendmail, for transporting mail, BIND NameD, for providing Domain Name Service, and telnetd, providing remote interactive access. Unfortunately, almost every common network daemon must run as root, since it requires the ability to use a "low3" tcp/udp port. Again unfortunate is that most of these services are offered to the entire Internet, rather that just certain sites/computers, so any cracker can try to abuse them. These two qualities make these services rather dangerous if they develop a security vulnerability... Very recently, crackers discovered a bug in BIND's named - any cracker who could exploit this had root access on large numbers of Unix/Linux boxes, including any RH6.0 box with a running name server!
As in the case with Set-UID root programs, your best bet toward avoiding this is to minimize the number of running network services. If you're not going to be running a web site for another few months, turn off the web server! Deactivate every network daemon that you're not directly using, even if you think you might use it later - you can turn it all back on just as easily! If you have the option, delete these daemons from your system. If you do need to run them, keep up on the patches and try to tighten their configurations...
For any of the network daemons that you can't deactivate, you'll want to lock them down in other ways. You might use firewalling constructively to block access from off-site, or from all but a few hosts/sites. But we can get at this more directly, often. Most of these network daemons can be configured carefully, to limit both the access the daemon has on the system and what kind of interactions the daemon permits. In the first class of actions, we might run the daemon as an alternate (non-root) user and/or "chroot" it.
Remember BIND's named, from our example before? This daemon was remotely exploitable to gain root access! But, you can run BIND's named as a non-root user, fairly easily. named will start as root to attach to tcp/udp port 53, then drop privileges to run as user dns. If someone manages to manipulate named through a bug, they'll only have the access accorded to the dns user, which won't be very comprehensive! Further, we can "chroot" this program - this is where we redefine its "root", its / , to some other directory. In the example of named, you might make a directory called /usr/local/chroot, which becomes named's root directory. You end up having to recreate the necessary environment for it to run, giving you, at least, the following directories:
/usr/local/chroot/dev /usr/local/chroot/lib /usr/local/chroot/sbin /usr/local/chroot/usr /usr/local/chroot/usr/lib /usr/local/chroot/var
In the second class of actions, we'll take steps to run these programs more safely and with a little less trust for the user. I've listed steps you can take for several common services:
Apache Web Server
Turn off CGI scripts altogether or carefully audit each script using well-defined guidelines.
Implemented cgiwrap to run CGI programs more safely
Disallow all Server Side Includes (SSI's) or, at least, the SSI "exec" command.
Restrict the web server to the loopback interface only, if its only needed for local viewing.
BIND Name Server
Restrict zone transfers from any machines besides our established secondary name servers.
Restrict queries for information outside our zones to our site/network only, so our name servers can only be queried for our zone information to prevent cache poisoning attacks.
Use a split horizon DNS setup to offer services more safely.
WU-FTPd FTP Server
Deactivate anonymous ftp to make ftpd exploits much more difficult.
Deactivate user ftp to protect user passwords and prevent the write access that most root-grab ftpd exploits require.
Use the /etc/ftpusers file to restrict access to the ftp daemon to a subset of users.
Define upload/download filenames via regular expressions to only allow "legal" filenames, i.e. filenames using alphanumeric characters and "."'s, without using "."'s as the first character.
Avoid upload access if at all possible - if necessary, avoid download access from upload areas. These steps minimize the risk of many of the ftpd remote root exploits.
There's a common theme running through this entire article so far - have you noticed it? We're trying to confine the number of ways that a user can interact with the daemon. This follows the "Principle of Minimalism" that's incredibly important in security: we try to limit the opportunities that a cracker has to exploit some vulnerable part of the system, by minimizing their access to privileged system components. Let's talk about some of the more advanced steps we can take to harden the core operating system.
There are many steps you can take to harden the core O/S itself. Bastille Linux takes steps4 like the following, along with many more.
Implement kernel-level packet filtering, to achieve greater depth to your defense by filtering on the host and at the router.
Restrict cron access to a small number of users, so attackers stealing accounts can't use cron and so users are more traceable.
Deactivate rhosts-style authentication (rsh, rcp...), since this method is incredibly insecure. (It relies on IP addresses which can be faked.)
Enforce password aging to deactivate unused accounts, so as to leave an attacker fewer unmonitored accounts to steal.
Apply boot security, to stop an attacker from getting root via single user mode and reboots.
Configure PAM settings to disable core dump analysis and host-based Denial of Service (DoS) attacks.
Once you've made all these changes, try implementing host-based Intrusion Detection Systems (IDS). The most popular is the very comprehensive program Tripwire. Tripwire, along with its clones5, monitors the system by looking for illegal changes to vital system files. See my article on Tripwire for details on this program and instructions on deploying it.
The best thing you can do for yourself, the admin, and the users is to bring information to the game. Learn more about how your system works and educate yourself on security. These articles are good for this, but you should also look for good books or classes on the subject, if you've got the time. The more you know about security, the better a job you'll do at all of this and the lower the chance that you'll make some kind of critical, but common, mistake that allows a cracker in. Further, you can pass on this knowledge to the users and get them working with you, rather than against you or the system, to better security. You can go a long way by telling them about good password policies and bad protocols (telnet, ftp...)
OK, so we're done for this week. By the way, you can automate all of these steps, so far, on Red Hat, Mandrake, Debian, TurboLinux, SuSE and HP-UX systems using Bastille Linux. If you've got the time to learn about this yourself, I really recommend that you do this stuff manually. You'll be far more precise and learn a great deal in the process. I'll be continuing this line of articles, explaining each of these steps in greater detail, the way I did with my Set-UID audit article.
Jay Beale is the Lead Developer of the Bastille Linux Project (http://www.bastille-linux.org). He is the author of several articles on Unix/Linux security, along with the upcoming book "Locking Down Linux the Bastille Way," to be published by Addison Wesley. At his day job, Jay is a self-employed security consultant. You can learn more about his articles, talks and favorite security links via http://www.bastille-linux.org/jay.
1If you don't follow the terminology here, skip this section. Alternately, you could check out an intro article on firewalling...
2Even a three year-old OpenBSD system is not in good shape and those guys have been really careful!
3When you're not using kernel "capabilities," a program needs root access to bind to any tcp/udp port below 1024.
5(Kurt's LASG has a section on alternatives, but I think Tripwire is really going in a great direction...)