6.893 Fall 2009 Lab 2: Privilege separation

Handed out: Monday, September 28, 2009
Part 1 due: Sunday, October 4, 2009 (11:59pm)
All parts due: Friday, October 16, 2009 (11:59pm)

Introduction

This lab will introduce you to the privilege separation technique, in the context of a simple PHP web application called zoobar, where users transfer "zoobars" (credits) between each other. To help you privilege-separate this application, we have also provided a clone of the OKWS web server that we discussed in lecture; our clone is called the ZooKWS web server. In this lab, you will set up a privilege separated web server, examine possible vulnerabilities, and break up the application code into less-privileged components to minimize the effects of any single vulnerability.

As with the previous lab, you will do your work in a VMware virtual machine. If you have not done so for the previous lab, you should make sure that you have VMware Player installed. For Mac users, MIT has a site license for VMware Fusion. You can download VMware Fusion from this web site. The virtual machine image you will need for this lab is the same one as for the previous lab. You should re-use your existing virtual machine from that lab, but, if you need to start from scratch, you can download it again here.

To get started, you will first need to install some additional software packages inside your virtual machine for this lab. Please log into your virtual machine as root and run the following commands:

root@vm-6893:~# apt-get update
root@vm-6893:~# apt-get install php5-cgi php5-cli libssl-dev netcat-openbsd
root@vm-6893:~# 

Now, download the lab 2 source code, and unpack it into /home/httpd/lab2. This package contains an implementation of the zookws web server and the zoobar web site. Because this lab will involve manipulating Unix users and permissions, you will find it convenient to do your work (or, at the very least, run the make command, which installs the code) as root. You can type the following commands to set up the lab2 source code:

root@vm-6893:~# cd /home/httpd
root@vm-6893:/home/httpd# wget http://pdos.csail.mit.edu/6.893/2009/labs/lab2/lab2.tar.gz
...
root@vm-6893:/home/httpd# tar zxvf lab2.tar.gz
lab2/
...
root@vm-6893:/home/httpd# 

To help you keep track of any changes you make to the base source code, the lab2 code comes with a pre-initialized git repository, which you can use to manage your source code modifications. Here's a overview of Git and the Git user's manual, which you may find useful.

Once your source code is in place, make sure that you can compile and install the web server and the zoobar application:

root@vm-6893:~# cd /home/httpd/lab2
root@vm-6893:/home/httpd/lab2# make
gcc -g -std=c99 -Werror   -c -o zookld.o zookld.c
gcc -g -std=c99 -Werror   -c -o utility.o utility.c
gcc   zookld.o utility.o  -lcrypto -o zookld
gcc -g -std=c99 -Werror   -c -o zookd.o zookd.c
gcc   zookd.o utility.o  -lcrypto -o zookd
gcc -g -std=c99 -Werror   -c -o zookfs.o zookfs.c
gcc   zookfs.o utility.o  -lcrypto -o zookfs
gcc -g -std=c99 -Werror   -c -o zooksvc.o zooksvc.c
gcc   zooksvc.o  -lcrypto -o zooksvc
./chroot-setup.sh
+ JAIL=/jail
+ ZOOBAR=../zoobar
...
root@vm-6893:/home/httpd/lab2# 

The make command both compiles the web server, and installs it with all the necessary permissions in the /jail directory. If you need to change how the files are installed (e.g. change the owner or the permissions on some of the files or directories), you should edit the chroot-setup.sh script in the lab2 directory.

Now, make sure you can run the web server, and access the web site from your browser, as follows:

root@vm-6893:/home/httpd/lab2# /sbin/ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0c:29:35:67:88  
          inet addr:172.16.148.129  Bcast:172.16.148.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe35:6788/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:322 errors:0 dropped:0 overruns:0 frame:0
          TX packets:206 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:29640 (29.6 KB)  TX bytes:29010 (29.0 KB)
          Interrupt:17 Base address:0x1080 

root@vm-6893:/home/httpd/lab2# ./zookld 80
listening on port 80
launching zookd
...

The /sbin/ifconfig command will give you the virtual machine's IP address. In this particular example, you would want to open your browser and go to http://172.16.148.129/index.php. You should see the PHP information page. To see the zoobar web site, you can browse to http://172.16.148.129/zoobar/index.php. Play around with this web application to get a feel for what it allows users to do. In short, a registered user can update his/her profile, transfer "zoobars" (credits) to another user, and look up the zoobar balance, profile, and transactions of other users in the system.

If something doesn't seem to be working, try to figure out what went wrong, or contact the course staff, before proceeding further.

Part 1: Web Server Setup

In the first part of this lab assignment, you will read the provided source code of the zookws web server and further secure it using privilege separation.

The zookws web server is modeled after OKWS from lecture. Similarly to OKWS, zookws consists of a launcher daemon zookld that launches services configured in the file zook.conf, a dispatcher zookd that routes requests to corresponding services, as well as several services. For simplicity zookws does not implement helper or logger daemon as OKWS does.

By default zookws configures only one HTTP service, simple_svc, that serves static files and executes dynamic scripts. The simple_svc does so by invoking the executable zookfs, which is jailed in the directory /jail by chroot. You can look into /jail; it contains executables (except for zookld), supporting libraries, and the zoobar web site. See zook.conf and zookfs.c for details.

The launcher daemon zookld is running under root and can bind to a privileged port like 80. Note that in the default configuration, zookd and vulnerable services are inappropriately running under root; an attacker can exploit buffer overflows and cause damages to the server, e.g., unlink a specific file as you have done in Lab 1.

To fix the problem, you should run these services under unprivileged users rather than root.

Exercise 1. Study the zookws's code and understand how each component interacts. Modify the configuration file zook.conf to run zookd and services under unprivileged users. Make sure that the attacker cannot read or write to sensitive files in the server, even zookd and these services contain buffer overflow vulnerabilities. At the same time, ensure that the zoobar application continues to work: that new users can register, transfer credits, and so on.

You may create some additional users, and set appropriate UID and GID for each service in zook.conf.

Now that none of the services are running as root, we will try to further privilege-separate the simple_svc service that handles both static files and dynamic scripts. Although it runs under an unprivileged user, some PHP scripts could easily have security holes; a vulnerable PHP script could be tricked into deleting important static files that the server is serving. A better organization is to split simple_svc into two services, one for static files and the other for PHP scripts, running under different users.

Additionally, a client should only be able to run the intended PHP scripts in the /zoobar directory from a browser, namely index.php, users.php, transfers.php. and zoobars.js.php. For example, a client should not be able to run /index.php, which reveals the PHP configuration information of the server; a client should not be able to access /zoobar/includes/db.php, which may invoke database operations; and so on.

Exercise 2. Create two new HTTP services along the lines of zookfs by copying the zookfs.c source code and modifying the two services, such that one will only execute dynamic content, and one which will only serve static files. Modify the configuration file zook.conf to split the simple_svc service into two services running under different users: the static_files service that only serves static files, and the php_zoobar service that only executes intended PHP scripts in the zoobar directory.

You may use url filtering provided in zook.conf, which supports regular expressions. For example, url = .* matches all requests, while url = /zoobar/(abc|def)\.php only allows requests to /zoobar/abc.php and /zoobar/def.php.

Submit your answers to the first part of the lab assignment by running make handin, and upload the resulting lab2-handin.tar.gz file at http://pdos.csail.mit.edu/cgi-bin/893handin.

Part 2: The zoobar Web Site

In the rest of this lab assignment, you will further secure the zoobar web site using privilege separation. To make sure that you can always refer back to the initial zoobar web site, lab 2 contains two copies of the zoobar source code, and installs two independent zoobar web sites, one under /zoobar/ and one under /zoobar-ps/, both accessible via HTTP from their respective URLs. If you cannot access http://your-vm-ip/zoobar-ps/index.php, you may need to adjust the URL matching rules that you configured in exercise 2. In this part of the lab assignment, you should modify the zoobar-ps source code, leaving the zoobar site intact for your reference.

One of the key features of the zoobar application is the ability to transfer credits between users, and log the transaction securely. This feature is implemented by the script transfer.php. Unfortunately, transfer.php has some logical bugs that may result in wrong transfers.

Exercise 3. Fix as many logical bugs as you can find in transfer.php (don't worry about attacks such as SQL injection so far). Think carefully about what kinds of inputs an attacker might provide, such as the integer number of credits to transfer, or the username of the recipient.

Now that we have fixed the bugs in the transfer code, we would like to make sure we can deal with any future such bugs that come up. To do so, we want to make sure that we have a reliable log of all zoobar transfers that happened in the system. The current design stores the transfer log in the Transfers SQL table, stored in zoobar/db/zoobar/Transfers.txt. This table is accessible to all PHP code in the zoobar site, which means that an attacker might be able to change the history so that we will never find out about his or her attack.

We will try to make the transfer log more reliable by performing the logging operations in a separate process, running as a different user from the rest of the zoobar code. This user ID will only run logging code, which will insert new log entries into the Transfers table. By setting permissions on the Transfers.txt file accordingly, we will ensure that only the logging code (which will hopefully be trustworthy) will be able to modify log entries, but any other PHP code will be able to read the log.

To break off some PHP code into a separate process, running as a separate user ID, we have provided you with some helper tools. The zooksvc service creates a Unix domain socket, and when someone connects to this socket, it will launch an arbitrary program. We have created a simple echo service using this tool. Look at how zook.conf spawns this echo service, the source code for the echo service tool in svc-echo.php, and the sample client of this service in demo-client.php. The client uses a simple library for connecting to Unix domain socket services, in includes/unixclient.php. Note that the client is meant to be invoked from the command line, rather than being executed as a CGI script via HTTP.

To debug the low-level protocol between the client and the server, you can use the netcat tool. For example, once zookld is running and has started the echo service, you should be able to connect to and interact with the echo service as follows:

root@vm-6893:/home/httpd/lab2# nc -U /jail/echosvc/sock
foo
You said foo
root@vm-6893:/home/httpd/lab2# 
You may find this tool helpful in debugging any new Unix domain socket services you create.

Exercise 4. Create a new service to perform transfer logging as a separate user ID. You will need to create a new service along the lines of svc-echo.php; modify zook.conf to start it appropriately (under a different UID); modify the permissions on the Transfers.txt database file such that only this new service can modify it; and modify the transfer.php code to invoke this service to log transactions, instead of logging transactions directly. Make all of your changes in the lab2 directory rather than in /jail. In particular, if you need to set certain permissions on files or directories, do so in the chroot-setup.sh script.

Now, you will break up the zoobar code into two additional protection domains. First, we want to make sure that only the transfer code is actually able to modify the zoobar balances of different users, so that a vulnerability in the rest of the PHP code will not be able to directly modify the number of zoobars that some user has.

One complication in doing this rests in the fact that the zoobar balance information is stored in the same database file, Person.txt, that stores profile and login information that the rest of the code must be able to modify. To protect zoobar balances from being corrupted by the rest of the PHP code, you will need to create a new database table holding just the zoobar balances for each user, and remove the balance information from the Person table.

Exercise 5. In preparation for privilege-separating the transfer code, split the Zoobars column from the Person table into a new table and remove the Zoobars column from the Person table (you may find this page helpful in understanding how to modify these SQL tables). Change the rest of the PHP code to access the correct table when fetching zoobar balances. Don't forget to handle the case of account creation, when the new user needs to get an initial 10 zoobars.

Note that txt-db-api's SQL engine might not support all of the SQL statements that other databases do. In particular, if you want to join two tables together, you will need to use an explicit join, such as SELECT * FROM A INNER JOIN B ON A.id=B.id WHERE A.id='ha', instead of an implicit join like SELECT * FROM A, B WHERE A.id=B.id AND A.id='ha'. If you are not familiar with SQL and joins, don't worry; it is possible to solve all of the exercises by using simple SQL queries like the ones already present in the code.

Exercise 6. Create a new service to transfer zoobars from one user to another. Change the transfer.php code to invoke this service instead of modifying the zoobar balances directly. Set the permissions on the new balances table such that only the transfer code can modify it, and the rest of the PHP code can only read it. Don't forget to handle account creation, which needs to involve your new transfer service.

Finally, make sure that only the transfer code is able to invoke the logging service -- after all, no other PHP code should be able to generate log entries! You should be able to do this by using groups and group permissions on the directory containing the logging service socket. As before, make sure all of your changes are reflected in the chroot-setup.sh script, and not only in the /jail directory in your VM.

Now our web application should be more secure, because compromises of most of the PHP code will not allow the attacker to modify zoobar balances. Unfortunately, the attacker can still subvert the web site by modifying user passwords or HTTP cookies in the Person database table. For the final part of this assignment, you will move the authentication and cookie-verification code into a separate service that runs under a distinct user ID, to prevent such attacks.

Exercise 7. Split the authentication information (Password, Salt, and Token columns) into a separate SQL table from the base Person table.

Create a new service that implements user login and cookie verification using this table. This service should implement three functions, which correspond to existing functions implemented in includes/auth.php that you will need to replace. First, check the username and password for login, returning an HTTP cookie token if the password is correct. Second, verify whether a token is correct, returning true or false. Third, register a new user, again returning true or false depending on success.

Make sure that the new SQL table storing passwords and tokens is only readable by your new authentication service.

Now that the attacker cannot obtain anyone's passwords or HTTP cookies from the database, there is one last problem to fix.

Exercise 8. In the current design, the attacker can still invoke the transfer service and ask for credits to be transferred between an arbitrary pair of users. Modify the transfer service protocol to require a valid token from the sender of credits, and modify the transfer service implementation to validate this token with the authentication service before approving the transfer.

That's it for the required exercises in this lab. If you find yourself up for another challenge, consider the following problem:

Challenge! Currently, login credentials (passwords) are passed from index.php, users.php, and other "HTTP-facing" PHP scripts, to the authentication service. If the attacker compromised one of these scripts, he or she would be able to steal the passwords of any users that log in during that time. Design a scheme that prevents this attack. You will likely need to make some assumption about when the user will and will not enter their password. For instance, you may want to assume that the user will only enter their password if the URL address bar is pointing to a specific page.

You are done! Run make handin and follow instructions to upload the resulting file.

Acknowledgments

Thanks to Stanford's CS155 course staff for the initial zoobar web application code, which we extended in this lab assignment.