Project 2 FAQ

CS155, Spring 2008

Main project page

General

What is the late policy for programming projects?

Every student in the class is given a total of 72 late hours that can be applied to the projects and homeworks. These late hours must be taken in chunks of 24 hours (essentially 3 late days) for example, submitting a homework 3 hours later than it's due counts as 24 late hours used. After all your late hours are used up, the assignment score gets halved with every 24 hours the assignment is late — for example, someone submitting a project 47 hours late after having used all her late days will get (1/2)^2 = 1/4 of the grade. If a project has more than one part, each part is considered a separate assignment for late days — for example, if you submit part 1 72 hours late and part 2 24 hours late, then part 1 gets full credit and part 2 gets half credit.

Is magic_quotes_gpc enabled on the web server?

Yes, it's enabled on both zoobar.org and cgi.stanford.edu. The optional magic_quotes_gpc PHP feature escapes single quotes, double quotes, and backslashes in GET and POST data by prepending a backslash. This feature makes it slightly harder to write websites that are vulnerable to cross-site scripting and SQL injection attacks. However, as you will see in this assignment, sites with magic_quotes_gpc are not magically bug-free. There are many ways that sites can turn off magic_quotes_gpc, such as .htaccess files, php.ini files, and by calling stripslashes on the escaped data. Note that magic_quotes_gpc doesn't do anything to angle brackets (<>). For that, you want htmlspecialchars.

Is register_globals enabled on the web server?

No, we've turned it off on zoobar.org and it's off by default on cgi.stanford.edu. The login page is vulnerable to an attack if register_globals is on, which you are welcome to fix if you want in Part 2. (This isn't part of the grading for the assignment, however.)

Why do I get an error when I try to visit login.php with my web browser?

Actually, login.php isn't intended to be viewed directly. It's just a library of functions that get called if the user tries to view one of the other pages and isn't logged in yet.

Is http://zoobar.org/ different from http://www.zoobar.org/?

They contain the same website, but will be treated as separate domains by the browser for purposes of the Same Origin Principle. Each domain has its own cookies and they can't talk to each other using frames. You should use zoobar.org in your project. (If one of your attacks isn't working, check to make sure you haven't added the "www" by mistake.)

Um... Same Origin Principle? Do I need to know what that is?

You should be familiar with the Same Origin Principle before attempting this project. The Same Origin Principle is the most important idea behind web application security; ignoring it will cause you to run into lots of browser security exceptions in Part 1. Watch the lecture on secure web site design for more information.

Can I switch partners between Part 1 and Part 2?

Yes.

Resources

Where can I find more information about JavaScript?

You can find a lot of references on Google. There's a decent one at W3Schools. Pay particular attention the DOM examples.

My JavaScript isn't working, and I don't know why. What can I do?

Two extremely useful tools for debugging in Mozilla Firefox are the JavaScript console and the DOM Inspector. Both can be accessed from the Tools menu. 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. (If the DOM Inspector isn't installed, make sure it's selected when you install Mozilla Firefox.) You might also want to try Firebug.

What do I need to know about CSS?

You only need to know enough to make your attacks disappear. You should know what basic syntax like <style>.warning{display:none}</style> means, and you should feel free to use stealthy attributes like style="display: none; visibility: hidden; height: 0; width: 0; 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.

How can I see cookies and form data that the browser sends?

Try the LiveHTTPHeaders browser extension.

Where can I find more information about PHP?

The definitive resource on PHP is php.net. You can find some introductory tutorials there.

Part 1 Hints

Am I allowed to load scripts or images from other domains?

No, your attacks should not load data from domains other than crypto.stanford.edu and zoobar.org. For example, this file is ok, but this one is not. We are enforcing this policy so that the submission deadline is a hard one.

Are we allowed to include additional files?

Please limit yourself to the files requested. One file per attack, the README file, and the ID file.

Should we submit a tar file?

No, that's not necessary.

Attack A.

How are the graders going to test our URL?

We will put it into the browser's address bar and click the "Go" button.

The example attack doesn't seem to do anything. What's wrong?

You need to be logged in to zoobar.org before the attack will work. When you click the link, you should get a browser alert with the contents of document.cookie.

Why would someone want to steal document.cookie?

The cookie is the user's authentication credential. If you steal someone else's cookie, it is easy to hijack that user's session (although we won't ask you to do so in this project).

How am I supposed to email document.cookie? I didn't know browsers could send email.

You have to convince the browser to send a GET request to the CS155 email script, which will cause an email to be sent by the crypto.stanford.edu server to you and the grader. The grader gets the email automatically, so just put a group member's email address (or a comma-delimited list of addresses) in the "to" field.

How do I convince the browser to send a GET request to an URL of my choosing?

The email script provides some example code. You allocate a JavaScript Image object, and set its src attribute to be the URL you want the browser to retrieve. The browser immediately tries to fetch the content of this image, even though the URL isn't actually pointing at an image and the image hasn't been inserted into the page anywhere. Pretty sneaky.

How to I put code into a URL? I want to have newlines and stuff.

It helps if you URL-encode it.

Should I include the javascript:void part in my code?

If you do, your attack might still work, but it's not necessary if you're already within a <script> tag. Just copy the part starting with (new Image()).src=...

There are two people in our group. What should we put in the netid parameter?

Put in the Leland username of a group member. You can put in both if you want, but it's not necessary. As long as it's clear to the grader that the attack is from your group, you'll be fine.

What's up with this random query parameter?

If you try your attack more than once, the browser might think that it already has the image in its cache, and so it wouldn't send a second GET request. Adding a random number to the end of the URL ensures that the browser will think the new URL is different, and won't use the cache. The email script ignores this parameter, so it's only useful for bypassing the cache.

Why are the characters reflected back different from the ones in the URL?

Your query parameter is URL decoded by the server before being reflected back at the user. You'll need to made sure that your attack code is URL encoded. For example, use + instead of space and %2b instead of +. Here is a URL encoding reference and a handy conversion tool.

Are there any restrictions on the length of my solution URL?

No. Internet Explorer has a limit of 2083 characters or so for URLs, but the limit is much longer in Firefox, which is what we are using for grading. The grader's solution was 297 characters, and shorter solutions are possible.

Since a sending an email is a side effect of the request, wouldn't it make more sense for the browser's request to be a POST instead of a GET?

Yes, but that would prevent the Image trick from working. Real-world attackers are rarely bothered by such semantic distinctions.

What should the email look like if the attack worked?

The parts that might be different are in red.

From: Apache <apache@theory.stanford.edu>
To: grader@zoobar.org, youremail@stanford.edu
Subject: Message from group 'yourlelandusername'

Payload:

ZoobarLogin=YToyOntpOjA7czo4OiJhbnRpZ3JhdiI7aToxO3M6MzI6
ImZlOGIzMTM3YjgyNTFlMDQ2YTIzMzRkNTgxOWM2YWZiIjt9=

I'm afraid of spamming the grader's email account. Should I be careful not to test the attack until I'm ready?

Don't worry, we will be ignoring emails sent to grader@zoobar.org until we start actually grading the first part of the assignment.

My attack is working. What should I do to make it invisible to the user?

The text box should its usual size and in the normal place. No warning text or characters that are normally part of the page should be visible. From the point of view of the visitor, it should appear as if they just went to users.php and didn't put in a username yet (with the possible exception of the address bar, which can be whatever you want). It's ok if the page briefly looks weird before correcting itself. Don't worry about the fact that the header at the top of the page changes with each page view — that's normal site behavior.

That sounds hard.

There are actually several quick and easy ways to do it, so try to think outside the box on this one. If you can't figure it out, try moving on to the other attacks and come back to this one when you're done.

Attack B.

Is this a cross site scripting attack?

No, this is a cross site request forgery attack. You are exploiting the fact that zoobar.org uses only a cookie to authenticate requests, even ones with side effects.

Can I use the vulnerability in user.php from Attack A?

No. Since this not a cross site scripting attack, you do not need to use the vulnerability in user.php. All you have to do convince the user's browser to post malicious form data to transfer.php.

How do I convince the browser to send a malicious POST request to transfer.php?

Put together a form in your HTML document, with http://zoobar.org/transfer.php as the action attribute.

How can I get the form to be submitted with no user interaction?

You can call the "Send" button's click method. Or, you can use JavaScript to call the form's submit method. Keep in mind that the site is looking for a parameter named "submission" in the form data, so if it's not there, the transfer won't happen.

What <input> fields should the form contain?

Use the browser's view source function on transfer.php and you'll get a pretty good idea.

How can I submit a form to zoobar.org without causing the browser's address bar to change to zoobar.org?

Create a hidden <iframe> and make sure the form's target attribute matches the frame's name attribute.

Uh oh, iframes. Will the grader have third-party cookie blocking enabled?

No, third party cookie blocking will not be enabled. It's probably off by default on your browser, but if your attack isn't working because the login cookie isn't sent, you might want to check to make sure. Tools > Options > Privacy > Cookies > Uncheck "for the originating site only"

How do I make the iframe hidden?

There are lots of ways to do it, but the easiest is probably <iframe style="visibility: hidden" ...>.

How do I redirect the browser to the CS155 home page?

Change the document.location property. Note that it is okay (and required, in fact) for the browser's address bar to change to http://crypto.stanford.edu/cs155/ once your attack is complete.

How do ensure that the redirect doesn't happen until after the form data has been posted?

You can trigger the redirect from the frame's onload handler. Depending on how your code is written, this onload handler may get called twice — once when the page initially loads and once when the form is submitted. If this is the case, you'll have to make sure that you change document.location on the second time only.

Attack C.

What can we assume the grader will do when logging in?

The grader will type a username, then click the password field, type a password, then move the mouse over the login button and click it. The grader will then wait for the login to complete.

Why is the site using JavaScript to focus the username field?

It's a convenience for the visitor, so they don't have to select the username field manually when they first come to the page. You may it useful as well.

How do I get the browser to call my injected code?

Because your code is sanitized with htmlspecialchars, you won't be able to inject a simple <script> tag like you did in Attack A. Trick the browser into running your code another way.

Does it have something to do with the GBK character set vulnerability discussed in class?

No, that's not it.

I think I figured it out, but when I call alert to test my attack, nothing happens.

It turns out that calling alert would lead to an infinite loop of dialog boxes, so Firefox is trying to be helpful by preventing it. Try using something like document.loginform.login_username.value=42 to test whether your attack is working.

How am I supposed to invoke the email script from my script without using any characters that will get escaped?

There are numerous static methods of String that you might find usable. Also, don't forget about escape, unescape, and eval.

Encoding by hand is incredibly tedious. Help?

You can save yourself some headaches if you write out your attack as a string in your attack page and then encode it programmatically, using those static String methods.

What do I need to do to make my attack invisible to the user?

You'll have to get a handle on the relevant DOM nodes and make the extraneous text disappear, either by deleting the text or setting the style.display property to "none".

How can I get a handle on the warning message? It doesn't have an id attribute.

It does have other distinguishing characteristics. You may find getElementsByTagName useful. Depending on how your attack works, you may not see a warning message anyway.

I'm sending the email when the form is submitted, but it isn't working. What's wrong?

There's a race condition here where the form may be getting submitted before the email image is downloaded. Once the form submit starts, the thread that's downloading the image is killed. So, to ensure that your attack always works, you should delay the form submission a little bit. You can use addEventListener with an event handler such as function(evt) { evt.preventDefault(); ... }. In this way, you can prevent the form submission until you're ready to trigger it yourself.

Ok, I prevented the submission. How long should I wait?

If you're lazy, you can use setTimeout with a reasonable number of milliseconds. But the precise way of doing it is to use addEventListener to wait for the "error" event to fire on the Image object you created. That event indicates that the email script's server has started to respond with a non-image file, meaning that it successfully processed the email send request.

What should I do after the email is sent?

You can trigger the submission manually using the login button's click() method. Use removeEventListener() to avoid infinite loops.

Is it okay if the attack doesn't work when the HTML file is clicked on the desktop, but it does work when the URL of the HTML file is put into the browser address bar directly?

That is fine. We will test your attack by putting in the URL of your HTML file in the browser's address bar.

What should the email look like if the attack worked?

The parts that might be different are in red.

From: Apache <apache@theory.stanford.edu>
To: grader@zoobar.org, youremail@stanford.edu
Subject: Message from group 'yourlelandusername'

Payload:

grader,topsecret

Firefox keeps asking if I want to save the password. Is there any way I can turn this off?

You can probably turn it off by setting autocomplete="no" in each of your form fields, but it shouldn't present much of an issue for grading. The grader will have already hit "Never for this site" before testing your attack, so the dialog won't come up.

Attack D.

How does the site sanitize profiles?

It uses strip_tags() to restrict the tags that can be used, and it uses a regular expression to replace certain dangerous words like "onmouseover" with a space character. Note that strip_tags() will remove all tags that are more than 1024 characters. If your solution occurs inside a tag, you will have to make sure it fits inside this limit, or it won't render to the user.

What tags does zoobar.org allow in profiles?

<a> <br> <b> <h1> <h2> <h3> <h4> <i> <img> <li> <ol> <p> <strong> <table> <tr> <td> <th> <u> <ul> <em> <span>

What strings does zoobar.org not allow in profiles?

javascript: eval setTimeout setInterval window target onAbort onBlur onChange onClick onDblClick onDragDrop onError onFocus onKeyDown onKeyPress onKeyUp onLoad onMouseDown onMouseMove onMouseOut onMouseOver onMouseUp onMove onReset onResize onSelect onSubmit onUnload
Keep in mind that this is an example of what not to do. Blacklisting keywords is a recipe for disaster and will annoy, but not limit, a determined attacker.

How do I transfer the zoobar?

Create an <iframe> pointing to transfer.php, set the appropriate form fields inside it, and post the form. Alternatively, you can create a form on the current page with transfer.php as its target, and then post it with the target pointing at an <iframe>. Another option is to use XMLHttpRequest, since you're actually making a same-site request this time. Pick whichever approach you prefer.

How do I create an <iframe>?

You can use the DOM methods document.createElement and document.body.appendChild.

How do I get a handle on form fields inside the <iframe>?

It differs by browser, and only works when the frame's domain matches the parent page (that's the Same Origin Principle). Here's the Firefox way of doing it: iframe.contentDocument.forms[0].zoobars.value = 1;

Are there any alternatives to target, which is blacklisted?

You can use string concatenation to express "target" without actually saying it. The following are equivalent in JavaScript: x.target, x["target"], x["tar"+"get"].

How do I replace the profile?

Use the same technique you just used on transfer.php, but point at index.php instead.

Is there an easy way to get a copy of the current profile?

You can use document.getElementById('profile').innerHTML, but it may mangle quotes in your profile, so be sure to check that the replicated profile is still functional. Also, note that only display:inline tags can be nested inside a <p>.

Part 2 Hints

How hard is this part?

It will be quick if you know PHP. You'll probably spend as much time writing your README file as you will actually coding. However, because not all the test cases are being made public, it's hard to know when you're truly done. Check your code thoroughly to ensure that you haven't missed any input validation vulnerabilities.

What does it mean if my site gives the error about not being able to find txt-db-api.php?

You might have an older version of the source code. Please replace your source code with the latest version by running this command: cp -r /usr/class/cs155/projects/pp2/zoobar ~/cgi-bin. If that doesn't solve the problem for you, please email cs155ta@cs.stanford.edu.

Can I use the nifty JavaScript techniques from the first part of the assignment?

You're trying to secure the site, not add functionality. The number of instances of eval should go down, not up.

Do I need to look at the files in the includes/ directory?

You should skim these files to get a sense for how the site works, but you're not allowed to change them, so don't spend too much time on it.

How can I get the value of the user's cookie in PHP?

It can be found in $_COOKIE[$user->cookieName]. The global variable $user is initialized in common.php, and you can find out more about the User class in auth.php. However, you'll find that the cookie can't be accessed in PHP immediately after being set. An alternative is to obtain document.cookie in JavaScript. You may also find session_start() and $_COOKIE[session_name()], to be more useful, since you won't have to tiptoe around the site's existing authentication quirks.

What should I do to stop cross-site request forgeries in transfer.php and index.php (preventing Attack B)?

This issue was covered in the lecture on secure web site design, and in section. You need to rely on the Same Origin Principle to protect a piece of information that the attacker doesn't know. The exact choice of what information to use is up to you, but it should not be easy to guess for an attacker who does not have access to the user's account. Use that information to validate the posted form data. It should only take a couple lines of code in each file to fix.

Is checking the "HTTP Referer" a valid method of stopping cross-site request forgeries?

No. As discussed in lectures, users may have browsers configured not to send "Referer" information (in fact, "Referer" is never sent for links on HTTPS pages). Also many anonymizing proxies strip "Referer" information.

What should I do to stop Attack D?

You probably want to strengthen the profile sanitizing code and/or replace unsafe JavaScript operations with something simple and straightforward. If you want to guarantee full credit, don't stop sanitizing until you're sure it's secure!

What other threats should I be worried about?

You should be concerned with cross-site scripting attacks that can be used to modify a legitimate user's account in some way (changing the profile, transferring zoobars), or steal the user's authentication information, without that user's knowledge or consent.

How many more of these vulnerabilities are there for me to find?

More than zero.

What user behavior can I expect?

The attacker may entice the user to view a malicious page, either on zoobar.org or elsewhere, but the user will only provide their username and password if the address bar starts with http://zoobar.org/ and the login form looks convincing.

Is it a problem if an attacker's site I'm viewing is able to log me out of zoobar.org against my will?

That's an annoyance, and technically a cross-site request forgery, but by itself it's not a security problem, so you don't have to defend against it.

Is it a problem if an attacker can log me in as another user the attacker knows the password for?

Not unless this can be used to impact the security of the user's own account.

How can I find input validation vulnerabilities and fix them?

Look for other places that database data or form data are reflected back at the user. Determine whether magic_quotes_gpc is providing enough protection or whether some other sanitization function like htmlspecialchars, strip_tags, or addslashes is required.

What user input can I disallow/sanitize?

You won't be penalized for over-sanitizing, as long as the site still works on alphanumeric inputs. Please use the README file to note any restrictions you are imposing on users.

Should we worry about users tampering with their own cookies?

No, that's not really a cross-site scripting attack. You can't change auth.php anyway.

Should we worry about malicious data that's already in the database?

Your database will be empty when we start the attacks. Anything that we put in it during the attacks (e.g. profiles) will be using your web interface. So either sanitize it on the way in, or on the way out.

Can I make additional assumptions that legitimate users will not do stupid things?

If you want to make reasonable additional assumptions, that most likely fine, but please check first with cs155ta@cs.stanford.edu and note your assumptions in the README file.

I think I've found an attack that isn't a cross-site scripting attack. Should I fix it?

Don't spend too much time hardening the site against other attacks, since they won't be part of the grading. However, if it's an interesting attack, you might want to email cs155ta@cs.stanford.edu or mention it in your README file.