Lab 4: Browser security


This lab will introduce you to browser-based attacks, as well as to how one might go about preventing them. The lab has several parts:

Each part has several exercises that help you build up an attack. All attacks will involve exploiting weaknesses in the zoobar site, but these are representative of weaknesses found in real web sites.

Network setup

For this lab, you will be crafting attacks in your web browser that exploit vulnerabilities in the zoobar web application. To ensure that your exploits work on our machines when we grade your lab, we need to agree on the URL that refers to the zoobar web site. For the purposes of this lab, your zoobar web site must be running on http://localhost:8080/. If you have been using your VM's IP address, such as, it will not work in this lab.

If you are using KVM or VirtualBox, the instructions we provided in lab 1 already ensure that port 8080 on localhost is forwarded to port 8080 in the virtual machine. If you are using VMware, we will use ssh's port forwarding feature to expose your VM's port 8080 as http://localhost:8080/. First find your VM IP address. To do so, log in as root on the console, run ip addr show dev eth0, and note the IP address listed beside inet. (This is the same IP address you have been using for past labs.) Then configure SSH port forwarding as follows (which depends on your SSH client):

The forward will remain in effect as long as the SSH connection is open.

Web browser

In this lab, we recommend that you use the current version of the Google Chrome browser for developing your attacks. There are subtle (and not-so-subtle) differences in the way HTML, JavaScript, and cookies are handled by different browsers, and some attacks that work in Internet Explorer (for example) may not work in Chrome, and vice-versa. The grading script will use a headless version of Chrome, called Puppeteer, to check your solutions.

If you have an ad-blocking extension in your web browser, such as uBlock, you may want to disable it to avoid interfering with the attacks you will be developing in this lab.

Setting up the web server

Before you begin working on these exercises, please use Git to commit your Lab 3 solutions, fetch the latest version of the course repository, and then create a local branch called lab4 based on our lab4 branch, origin/lab4. Do not merge your lab 2 and 3 solutions into lab 4. Here are the shell commands:

student@6566-v24:~$ cd lab
student@6566-v24:~/lab$ git commit -am 'my solution to lab3'
[lab3 c54dd4d] my solution to lab3
 1 files changed, 1 insertions(+), 0 deletions(-)
student@6566-v24:~/lab$ git pull
Already up-to-date.
student@6566-v24:~/lab$ git checkout -b lab4 origin/lab4
Branch lab4 set up to track remote branch lab4 from origin.
Switched to a new branch 'lab4'
student@6566-v24:~/lab$ make

Note that lab 4's source code is based on the initial web server from lab 1. It does not include privilege separation or Python profiles.

Now you can start the zookws web server, as follows.

student@6566-v24:~/lab$ ./zookd 8080

Open your browser and go to the URL http://localhost:8080/. You should see the zoobar web application. If you don't, go back and double-check your steps. If you cannot get the web server to work, get in touch with course staff before proceeding further.

Crafting attacks

You will craft a series of attacks against the zoobar web site you have been working on in previous labs. These attacks exploit vulnerabilities in the web application's design and implementation. Each attack presents a distinct scenario with unique goals and constraints, although in some cases you may be able to re-use parts of your code.

We will run your attacks after wiping clean the database of registered users (except the user named "attacker"), so do not assume the presence of any other users in your submitted attacks.

You can run our tests with make check-lab4; this will execute your attacks against the server, and tell you whether your exploits are working correctly. As in previous labs, keep in mind that the checks performed by make check are not exhaustive, especially with respect to race conditions. You may wish to run the tests multiple times to convince yourself that your exploits are robust.

Some exercises require that the displayed site look a certain way. When make check-lab4 runs, it generates reference images for what the attack page is supposed to look like (answer-XX.ref.png) and what your attack page actually shows (answer-XX.png), and places them in the lab4-tests/ directory. To view these images from lab4-tests/, either copy them to your local machine, or run python3 -m http.server 8080 and view the images by visiting http://localhost:8080/lab4-tests/. Note that Python's http.server caches responses, so you should kill and restart it after a make check-lab4 run.

Part 1: a cross-site scripting (XSS) attack

The zoobar users page has a flaw that allows theft of a logged-in user's cookie from the user's browser, if an attacker can trick the user into clicking a specially-crafted URL constructed by the attacker. Your job is to construct such a URL. An attacker might e-mail the URL to the victim user, hoping the victim will click on it. A real attacker could use a stolen cookie to impersonate the victim.

You will develop the attack in several steps. To learn the necessary infrastructure for constructing the attacks, you first do a few exercises that familiarize yourself with Javascript, the DOM, etc.

Exercise 1: Print cookie.

Cookies are HTTP's main mechanism for tracking users across requests. If an attacker can get ahold of another user's cookie, they can completely impersonate that other user. For this exercise, your goal is simply to print the cookie of the currently logged-in user when they access the "Users" page.

  1. Read about how cookies are accessed from Javascript.
  2. Save a copy of zoobar/templates/users.html (you'll need to restore this original version later). Add a <script> tag to users.html that prints the logged-in user's cookie using alert().

    Your script might not work immediately if you made a Javascript programming error. Fortunately, Chrome has fantastic debugging tools accessible in the Inspector: the JavaScript console, the DOM inspector, and the Network monitor. The JavaScript console lets you see which exceptions are being thrown and why. The DOM Inspector lets you peek at the structure of the page and the properties and methods of each node it contains. The Network monitor allows you to inspect the requests going between your browser and the website. By clicking on one of the requests, you can see what cookie your browser is sending, and compare it to what your script prints.

  3. Put the contents of your script in a file named answer-1.js. Your file should only contain javascript (don't include <script> tags).
Exercise 2: Log the cookie.

Modify your script so that it records the user's cookie to the attacker using the logging script. The attack should still be triggered when the user visits the "Users" page.

Please review the instructions at and use that URL in your scripts to record the stolen cookie. You may log as many times as you like while working on the project, but please do not attack or abuse the logging script. Note that the cookie has characters that likely need to be URL encoded. Take a look at encodeURIComponent and decodeURIComponent.

When you have a working script, put it in a file named answer-2.js. Again, your file should only contain javascript (don't include <script> tags).

Exercise 3: Remote execution.

For this exercise, your goal is to craft a URL that, when accessed, will cause the victim's browser to execute some JavaScript you as the attacker has supplied. In particular, for this exercise, we want you to create a URL that contains a piece of code in one of the query parameters, which, due to a bug in zoobar, the "Users" page sends back to the browser. The code will then be executed as JavaScript on the browser. This is known as "Reflected Cross-site Scripting", and it is a very common vulnerability on the Web today.

For this exercise, the JavaScript you inject should call alert() to display the victim's cookies. In subsequent exercises, you will make the attack do more nefarious things. Before you begin, you should restore the original version of zoobar/templates/users.html.

For this exercise, we place some restrictions on how you may develop your exploit. In particular:

When you are done, cut and paste your URL into the address bar of a logged in user, and it should print the victim's cookies (don't forget to start the zoobar server: ./zookld). Once it works, put your attack URL in a file named answer-3.txt. Your URL should be the only thing on the first line of the file.

Hint: You will need to find a cross-site scripting vulnerability on /zoobar/index.cgi/users, and then use it to inject Javascript code into the browser. What input parameters from the HTTP request does the resulting /zoobar/index.cgi/users page display? Which of them are not properly escaped?

Hint: Is this input parameter echo-ed (reflected) verbatim back to victim's browser? What could you put in the input parameter that will cause the victim's browser to execute the reflected input? Remember that the HTTP server performs URL decoding on your request before passing it on to zoobar; make sure that your attack code is URL-encoded (e.g. use + instead of space, and %2b instead of +). This URL encoding reference and this conversion tool may come in handy.

Hint: The browser may cache the results of loading your URL, so you want to make sure that the URL is always different while your developing the URL. You may want to put a random argument into your url: &random=<some random number>.

Exercise 4. Steal cookies.

Modify the URL so that it doesn't print the cookies but logs them for the attacker. Put your attack URL in a file named answer-4.txt.

Hint: Incorporate your logging script from exercise 2 into the URL.

Exercise 5: Hiding your tracks.

With the exploits you have developed thus far, the victim is likely to notice that you stole their cookies, or at least, that something weird is happening. For example, the Users page probably also printed an error message (e.g., "Cannot find that user").

For this exercise, you need to modify your URL to hide your tracks. Except for the browser address bar (which can be different), the grader should see a page that looks exactly the same as when the grader visits http://localhost:8080/zoobar/index.cgi/users. No changes to the site appearance or extraneous text should be visible. Avoiding the red warning text is an important part of this attack (it is ok if the page looks weird briefly before correcting itself). Your script should still send the user's cookie to the logging script.

When you are done, put your attack URL in a file named answer-5.txt.

Hint: You will probably want to use CSS to make your attacks invisible to the user. Familiarize yourself with basic expressions like <style>.warning{display:none}</style>, and feel free to use stealthy attributes like display: none; visibility: hidden; height: 0; width: 0;, and position: absolute; in the HTML of your attacks. Beware that frames and images may behave strangely with display: none, so you might want to use visibility: hidden instead.

This completes part 1 of this lab.

Submit your answers to the first part of this lab assignment by running make and upload the resulting file to the submission web site.

Part 2: Fake Login Page

In this part, you will construct an attack that will steal the victim's username and password if they are not logged in using a fake login form. The attack scenario is that we manage to get the user to visit some malicious web page that we control.

Exercise 6: Make a zoobar login form

Copy the zoobar login form (either by viewing the page source, or using zoobar/templates/login.html) into answer-6.html, and make it work with the existing zoobar site. Much of this will involve prefixing URLs with the address of the web server. This file will be used as a stepping stone to the rest of the exercises in this part, so make sure you can correctly log in to the website using your fake form. Note that you should make no changes to the zoobar code. Submit your HTML in a file named answer-6.html.

Exercise 7: Intercept form submission

In order to steal the victim's credentials, we have to look at the form values just as the user is submitting the form. This is most easily done by attaching an event listener (using addEventListener()) or by setting the onsubmit attribtue of a form. For this exercise, use one of these methods to alert the user's password when the form is submitted. Submit your code in a file named answer-7.html.

Exercise 8: Steal password

Modify answer-7.html to log the username and password (separated by a slash) to you using the logging script when the user submits the login form. Submit your code in a file named answer-8.html.

Please note that after implementing this exercise, the attacker controller webpage will no longer redirect the user to be logged in correctly. You will be fixing this issue in the next exercise.

Hint: When a form is submitted, outstanding requests are cancelled as the browser navigates to the new page. This might lead to your request to log.php not getting through. To work around this, consider cancelling the submission of the form using the preventDefault() method on the event object passed to the submit handler, and then use setTimeout() to submit the form again slightly later. Remember that your submit handler might be invoked again!

Exercise 9: hide your tracks

Modify answer-8.html to hide your tracks: arrange that after stealing the victim's username and password that the user sees the official site. Submit your code in a file named answer-9.html.

Hint: The zoobar application checks how the form was submitted (that is, whether "Log in" or "Register" was clicked) by looking at whether the request parameters contain submit_login or submit_registration. Keep this in mind when you forward the login attempt to the real login page.

Challenge: Password Theft.

Create an attack that will steal the victim's password, even if the victim is diligent about entering their password only when the URL address bar shows http://localhost:8080/zoobar/index.cgi/login. Your solution should be contained in a short HTML document named answer-chal.html.

When grading, the grader will open the page using the web browser (while not logged in to zoobar). Upon loading your document, they should immediately be redirected to http://localhost:8080/zoobar/index.cgi/login. The grader will then enter a username and password, and press the "Log in" button.

Your mission, should you choose to accept it, is to make it so that when the "Log in" button is pressed, the password are logged for the attacker using the logging script. The login form should appear perfectly normal to the user; this means no extraneous text (e.g., warnings) should be visible, and as long as the username and password are correct, the login should proceed the same way it always does.

Hint: For this final attack, you may find that using alert() to test for script injection does not work; Chrome blocks it when it's causing an infinite loop of dialog boxes. Try other ways to probe whether your code is running, such as document.loginform.login_username.value=42.

Part 3: Profile Worm

Worms in the context of web security are scripts that are injected into pages by an attacker, and that automatically spread once they are executed in a victim's browser. The Samy worm is an excellent example, which spread to over a million users on the social network MySpace over the course of just 20 hours. In this part of the lab, you will create a similar worm that, upon execution, will transfer 1 zoobar from the victim to the attacker, and then spread to the victim's profile. Thus, any subsequent visit to the victim's profile will cause additional zoobars to be transferred, and the worm to spread again. You will build up your solution in several stages, much like in the previous parts. This time, however, we won't spell out the steps through exercises.
Exercise 10: Profile Worm.

Your profile worm should be submitted in a file named answer-10.txt. To grade your attack, we will cut and paste the submitted profile code into the profile of the "attacker" user, and view that profile using the grader's account. We will then view the grader's profile with more accounts, checking for both the zoobar transfer and the replication of profile code.

In particular, we require your worm to meet the following criteria:

To get you started, here is a rough outline of how to go about building your worm:

This detailed analysis of the MySpace worm may provide some inspiration.

Note: You will not be graded on the corner case where the user viewing the profile has no zoobars to send.

Hint: In this exercise, as opposed to the previous ones, your exploit runs on the same domain as the target site. This means that you are not subject to Same-Origin Policy restrictions, and that you can issue AJAX requests directly using XMLHttpRequest instead of iframes.

Hint: For this exercise, you may need to create new elements on the page, and access data inside of them. You may find the DOM methods document.createElement and document.body.appendChild useful for this purpose.

Hint: If you choose to use iframes in your solution, you may want to get access to form fields inside an iframe. Exactly how you do so differs by browser, but such access is always restructed by the same-origin policy. In Chrome, you can use iframe.contentDocument.forms[0].some_field_name.value = 1;.


Make sure you have the following files: answer-1.js, answer-2.js, answer-3.txt, answer-5.txt, answer-6.html, answer-7.html, answer-8.html, answer-9.html, answer-10.txt, and if you are doing the challenge, answer-chal.html, containing each of your attacks. Feel free to include any comments about your solutions in the answers.txt file (we would appreciate any feedback you may have on this assignment).

Run make and upload to the submission web site, and you're done!


Thanks to Stanford's CS155 course staff for the original version of this assignment.