Client-State Manipulation

When a user interacts with a web application, they do it indirectly through a browser. When the user clicks a button or submits a form, the browser sends a request back to the web server. Because the browser runs on a machine that can be controlled by an attacker, the application must not trust any data sent by the browser.
It might seem that not trusting any user data would make it impossible to write a web application but that's not the case. If the user submits a form that says they wish to purchase an item, it's OK to trust that data. But if the submitted form also includes the price of the item, that's something that cannot be trusted.

Elevation of Privilege

Convert your account to an administrator account.

Hint 1

Take a look at the editprofile.gtl page that users and administrators use to edit profile settings. If you're not an administrator, the page looks a bit different. Can you figure out how to fool Gruyere into letting you use this page to update your account?

Hint 2

Can you figure out how to fool Gruyere into thinking you used this page to update your account?

Exploit and Fixes

You can convert your account to being an administrator by issuing either of the following requests:
  • http://google-gruyere.appspot.com/123/saveprofile?action=update&is_admin=True
  • http://google-gruyere.appspot.com/123/saveprofile?action=update&is_admin=True&uid=username (which will make any username into an an admin)
After visiting this URL, your account is now marked as an administrator but your cookie still says you're not. So sign out and back in to get a new cookie. After logging in, notice the 'Manage this server' link on the top right.
The bug here is that there is no validation on the server side that the request is authorized. The only part of the code that restricts the changes that a user is allowed to make are in the template, hiding parts of the UI that they shouldn't have access to. The correct thing to do is to check for authorization on the server, at the time that the request is received.

Cookie Manipulation

Because the HTTP protocol is stateless, there's no way a web server can automatically know that two requests are from the same user. For this reason, cookies were invented. When a web site includes a cookie (an arbitrary string) in a HTTP response, the browser automatically sends the cookie back to the browser on the next request. Web sites can use the cookie to save session state. Gruyere uses cookies to remember the identity of the logged in user. Since the cookie is stored on the client side, it's vulnerable to manipulation. Gruyere protects the cookies from manipulation by adding a hash to it. Notwithstanding the fact that this hash isn't very good protection, you don't need to break the hash to execute an attack. Get Gruyere to issue you a cookie for someone else's account.

Hint 1

Hint 2

Exploit and Fix

You can get Gruyere to issue you a cookie for someone else's account by creating a new account with username "foo|admin|author". When you log into this account, it will issue you the cookie "hash|foo|admin|author||author" which actually logs you into foo as an administrator. (So this is also an elevation of privilege attack.)
Having no restrictions on the characters allowed in usernames means that we have to be careful when we handle them. In this case, the cookie parsing code is tolerant of malformed cookies and it shouldn't be. It should escape the username when it constructs the cookie and it should reject a cookie if it doesn't match the exact pattern it is expecting.
Even if we fix this, Python's hash function is not cryptographically secure. If you look at Python's string_hash function in python/Objects/stringobject.cc you'll see that it hashes the string strictly from left to right. That means that we don't need to know the cookie secret to generate our own hashes; all we need is another string that hashes to the same value, which we can find in a relatively short time on a typical PC. In contrast, with a cryptographic hash function, changing any bit of the string will change many bits of the hash value in an unpredictable way. At a minimum, you should use a secure hash function to protect your cookies. You should also consider encrypting the entire cookie as plain text cookies can expose information you might not want exposed.
And these cookies are also vulnerable to a replay attack. Once a user is issued a cookie, it's good forever and there's no way to revoke it. So if a user is an administrator at one time, they can save the cookie and continue to act as an administrator even if their administrative rights are taken away. While it's convenient to not have to make a database query in order to check whether or not a user is an administrator, that might be too dangerous a detail to store in the cookie. If avoiding additional database access is important, the server could cache a list of recent admin users. Including a timestamp in a cookie and expiring it after some period of time also mitigates against a replay attack.
Another challenge: Since account names are limited to 16 characters, it seems that this trick would not work to log in to the actual administrator account since "administrator|admin" is 19 characters. Can you figure out how to bypass that restriction?

Additional Exploit and Fix

The 16 character limit is implemented on the client side. Just issue your own request:
http://google-gruyere.appspot.com/123/saveprofile?action=new&uid=administrator|admin|author&pw=secret
Again, this restriction should be implemented on the server side, not just the client side.


Cross-Site Request Forgery (XSRF)

The previous section said "If the user submits a form that says they wish to purchase an item, it's OK to trust that data." That's true as long as it really was the user that submitted the form. If your site is vulnerable to XSS, then the attacker can fake any request as if it came from the user. But even if you've protected against XSS, there's another attack that you need to protect against: cross-site request forgery.
When a browser makes requests to a site, it always sends along any cookies it has for that site, regardless of where the request comes from. Additionally, web servers generally cannot distinguish between a request initiated by a deliberate user action (e.g., user clicking on "Submit" button) versus a request made by the browser without user action (e.g., request for an embedded image in a page). Therefore, if a site receives a request to perform some action (like deleting a mail, changing contact address), it cannot know whether this action was knowingly initiated by the user — even if the request contains authentication cookies. An attacker can use this fact to fool the server into performing actions the user did not intend to perform.

More details

For example, suppose Blogger is vulnerable to XSRF attacks (it isn't). And let us say Blogger has a Delete Blog button on the dashboard that points to this URL:
http://www.blogger.com/deleteblog.do?blogId=BLOGID
Bob, the attacker, embeds the following HTML on his web page on http://www.evil.example.com:
<img src="http://www.blogger.com/deleteblog.do?blogId=alice's-blog-id"
    style="display:none">
If the victim, Alice, is logged in to www.blogger.com when she views the above page, here is what happens:
  • Her browser loads the page from http://www.evil.example.com. The browser then tries to load all embedded objects in the page, including the img shown above.
  • The browser makes a request to http://www.blogger.com/deleteblog.do?blogId=alice's-blog-id to load the image. Since Alice is logged into Blogger — that is, she has a Blogger cookie — the browser also sends that cookie in the request.
  • Blogger verifies the cookie is a valid session cookie for Alice. It verifies that the blog referenced by alice's-blog-id is owned by Alice. It deletes Alice's blog.
  • Alice has no idea what hit her.
In this sample attack, since each user has their own blog id, the attack has to be specifically targeted to a single person. In many cases, though, requests like these don't contain any user-specific data.

XSRF Challenge

The goal here is to find a way to perform an account changing action on behalf of a logged in Gruyere user without their knowledge. Assume you can get them to visit a web page under your control.
Find a way to get someone to delete one of their Gruyere snippets.

Hint

What is the URL used to delete a snippet? Look at the URL associated with the "X" next to a snippet.

Exploit and Fix

To exploit, lure a user to visit a page that makes the following request:
http://google-gruyere.appspot.com/123/deletesnippet?index=0
To be especially sneaky, you could set your Gruyere icon to this URL and the victim would be exploited when they visited the main page.
To fix, we should first change /deletesnippet to work via a POST request since this is a state changing action. In the HTML form, change method='get' to method='post'. On the server side, GET and POST requests look the same except that they usually call different handlers. For example, Gruyere uses Python's BaseHTTPServer which calls do_GET for GET requests and do_POST for POST requests.
However, note that changing to POST is not enough of a fix in itself! (Gruyere uses GET requests exclusively because it makes hacking it a bit easier. POST is not more secure than GET but it is more correct: browsers may re-issue GET requests which can result in an action getting executed more than once; browsers won't reissue POST requests without user consent.) Then we need to pass a unique, unpredictable authorization token to the user and require that it get sent back before performing the action. For this authorization token, action_token, we can use a hash of the value of the user's cookie appended to a current timestamp and include this token in all state-changing HTTP requests as an additional HTTP parameter. The reason we use POST over GET requests is that if we pass action_token as a URL parameter, it might leak via HTTP Referer headers. The reason we include the timestamp in our hash is so that we can expire old tokens, which mitigates the risk if it leaks.
When a request is processed, Gruyere should regenerate the token and compare it with the value supplied with the request. If the values are equal, then it should perform the action. Otherwise, it should reject it. The functions that generate and verify the tokens look like this:
def _GenerateXsrfToken(self, cookie):
  """Generates a timestamp and XSRF token for all state changing actions."""

  timestamp = time.time()
  return timestamp + "|" + (str(hash(cookie_secret + cookie + timestamp)))

def _VerifyXsrfToken(self, cookie, action_token):
  """Verifies an XSRF token included in a request."""

  # First, make sure that the token isn't more than a day old.
  (action_time, action_hash) = action_token.split("|", 1)
  now = time.time()
  if now - 86400 > float(action_time):
    return False

  # Second, regenerate it and check that it matches the user supplied value
  hash_to_verify = str(hash(cookie_secret + cookie + action_time)
  return action_hash == hash_to_verify
Oops! There's several things wrong with these functions.

What's missing?

By including the time in the token, we prevent it from being used forever, but if an attacker were to gain access to a copy of the token, they could reuse it as many times as they wanted within that 24 hour period. The expiration time of a token should be set to a small value that represents the reasonable length of time it will take the user to make a request. This token also doesn't protect against an attack where a token for one request is intercepted and then used for a different request. As suggested by the name action_token, the token should be tied to the specific state changing action being performed, such as the URL of the page. A better signature for _GenerateXsrfToken would be (self, cookie, action). For very long actions, like editing snippets, a script on the page could query the server to update the token when the user hits submit. (But read the next section about XSSI to make sure that an attacker won't be able to read that new token.)
XSRF vulnerabilities exist because an attacker can easily script a series of requests to an application and than force a user to execute them by visiting some page. To prevent this type of attack, you need to introduce some value that can't be predicted or scripted by an attacker for every account changing request. Some application frameworks have XSRF protection built in: they automatically include a unique token in every response and verify it on every POST request. Other frameworks provide functions that you can use to do that. If neither of these cases apply, then you'll have to build your own. Be careful of things that don't work: using POST instead of GET is advisable but not sufficient by itself, checking Referer headers is insufficient, and copying cookies into hidden form fields can make your cookies less secure.

Cross Site Script Inclusion (XSSI)

Browsers prevent pages of one domain from reading pages in other domains. But they do not prevent pages of a domain from referencing resources in other domains. In particular, they allow images to be rendered from other domains and scripts to be executed from other domains. An included script doesn't have its own security context. It runs in the security context of the page that included it. For example, if www.evil.example.com includes a script hosted on www.google.com then that script runs in the evil context not in the google context. So any user data in that script will "leak."

XSSI Challenge

Find a way to read someone else's private snippet using XSSI.
That is, create a page on another web site and put something in that page that can read your private snippet. (You don't need to post it to a web site: you can just create a .html in your home directory and double click on it to open in a browser.)

Hint 1

You can run a script from another domain by adding
<SCRIPT src="http://google-gruyere.appspot.com/123/..."></SCRIPT>
to your HTML file. What scripts does Gruyere have?

Hint 2

feed.gtl is a script. Given that, how can you get the private snippet out of the script?

Exploit and Fix

To exploit, put this in an html file:
<script>
function _feed(s) {
  alert("Your private snippet is: " + s['private_snippet']);
}
</script>
<script src="http://google-gruyere.appspot.com/123/feed.gtl"></script>
When the script in feed.gtl is executed, it runs in the context of the attacker's web page and uses the _feed function which can do whatever it wants with the data, including sending it off to another web site.
You might think that you can fix this by eliminating the function call and just having the bare expression. That way, when the script is executed by inclusion, the response will be evaluated and then discarded. That won't work because Javascript allows you to do things like redefine default constructors. So when the object is evaluated, the hosting page's constructors are invoked, which can do whatever they want with the values.
To fix, there are several changes you can make. Any one of these changes will prevent currently possible attacks, but if you add several layers of protection ("defense in depth") you protect against the possibility that you get one of the protections wrong and also against future browser vulnerabilities. First, use an XSRF token as discussed earlier to make sure that JSON results containing confidential data are only returned to your own pages. Second, your JSON response pages should only support POST requests, which prevents the script from being loaded via a script tag. Third, you should make sure that the script is not executable. The standard way of doing this is to append some non-executable prefix to it, like ])}while(1);</x>. A script running in the same domain can read the contents of the response and strip out the prefix, but scripts running in other domains can't.
NOTE: Making the script not executable is more subtle than it seems. It's possible that what makes a script executable may change in the future if new scripting features or languages are introduced. Some people suggest that you can protect the script by making it a comment by surrounding it with /* and */, but that's not as simple as it might seem. (Hint: what if someone included */ in one of their snippets?)
There's much more to XSSI than this. There's a variation of JSON called JSONP which you should avoid using because it allows script injection by design. And there's E4X (Ecmascript for XML) which can result in your HTML file being parsed as a script. Surprisingly, one way to protect against E4X attacks is to put some invalid XML in your files, like the </x> above.

Path Traversal

Most web applications serve static resources like images and CSS files. Frequently, applications simply serve all the files in a folder. If the application isn't careful, the user can use a path traversal attack to read files from other folders that they shouldn't have access to. For example, in both Windows and Linux, .. represents the parent directory, so if you can inject ../ in a path you can "escape" to the parent directory.
If an attacker knows the structure of your file system, then they can craft a URL that will traverse out of the installation directory to /etc. For example, if Picasa was vulnerable to path traversal (it isn't) and the Picasa servers use a Unix-like system, then the following would retrieve the password file:
http://www.picasa.com/../../../../../../../etc/passwd

Information disclosure via path traversal

Find a way to read secret.txt from a running Gruyere server.
Amazingly, this attack is not even necessary in many cases: people often install applications and never change the defaults. So the first thing an attacker would try is the default value.

Hint 1

This isn't a black box attack because you need to know that the secret.txt file exists, where it's stored, and where Gruyere stores its resource files. You don't need to look at any source code.

Hint 2

How does the server know which URLs represent resource files? You can use curl or a web proxy to craft request URLs that some browsers may not allow.

Exploit and Fix

To exploit, you can steal secret.txt via this URL:
http://google-gruyere.appspot.com/123/../secret.txt
Some browsers, like Firefox and Chrome, optimize out ../ in URLs. This doesn't provide any security protection because an attacker will use %2f to represent / in the URL; or a tool like curl, a web proxy or a browser that doesn't do that optimization. But if you test your application with one of these browsers to see if you're vulnerable, you might think you were protected when you're not.
To fix, we need to prevent access to files outside the resources directory. Validating file paths is a bit tricky as there are various ways to hide path elements like "../" or "~" that allow escaping out of the resources folder. The best protection is to only serve specific resource files. You can either hardcode a list or when your application starts, you can crawl the resource directory and build a list of files. Then only accept requests for those files. You can even do some optimization here like caching small files in memory which will make your application faster. If you are going to try to file path validation, you need to do it on the final path, not on the URL, as there are numerous ways to represent the same characters in URLs. Note: Changing file permissions will NOT work. Gruyere has to be able to read this file.

Data tampering via path traversal

Find a way to replace secret.txt on a running Gruyere server.

Hint 1

Again, this isn't a black box attack because you need to know about the directory structure that Gruyere uses, specifically where uploaded files are stored.

Hint 2

If I log in as user brie and upload a file, where does the server store it? Can you trick the server into uploading a file to ../../secret.txt?

Exploit and Fix

To exploit, create a new user named .. and upload your new secret.txt. You could also create a user named brie/../...
To fix, you should escape dangerous characters in the username (replacing them with safe characters) before using it. It was earlier suggested that we should restrict the characters allowed in a username, but it probably didn't occur to you that "." was a dangerous character. It's worth noting that there's a vulnerability unique to Windows servers with this implementation. On Windows, filenames are not case sensitive but Gruyere usernames are. So one user can attack another user's files by creating a similar username that differs only in case, e.g., BRIE instead of brie. So we need to not just escape unsafe characters but convert the username to a canonical form that is different for different usernames. Or we could avoid all these issues by assigning each user a unique identifier instead.
Oops! This doesn't completely solve the problem. Even with the above fix in place, there is another way to perform this attack. Can you find it?

Hint

Are there any limits on the filename when you do an upload? You may need to use a special tool like curl or a web proxy to perform this attack.

Another Exploit and Fix

Surprisingly, you can upload a file named ../secret.txt. Gruyere provides no protection against this attack. Most browsers won't let you upload that file but, again, you can do it with curl or other tools. You need the same kind of protection when writing files as you do on read.
As a general rule, you should never store user data in the same place as your application files but that alone won't protect against these attacks since if the user can inject ../ into the file path, they can traverse all the way to the root of the file system and then back down to the normal install location of your application (or even the Python interpreter itself).

Denial of Service

A denial of service (DoS) attack is an attempt to make a server unable to service ordinary requests. A common form of DoS attack is sending more requests to a server than it can handle. The server spends all its time servicing the attacker's requests that it has very little time to service legitimate requests. Protecting an application against these kinds of DoS attacks is outside the scope of this codelab. And attacking Gruyere in this way would be interpreted as an attack on App Engine.
Hackers can also prevent a server from servicing requests by taking advantage of server bugs, such as sending requests that crash a server, make it run out of memory, or otherwise cause it fail serving legitimate requests in some way. In the next few challenges, you'll take advantage of bugs in Gruyere to perform DoS attacks.

DoS - Quit the Server

The simplest form of denial of service is shutting down a service. Find a way to make the server quit.

Hint

How does an administrator make the server quit? The server management page is manage.gtl.

Exploit and Fix

To exploit, make a request to http://google-gruyere.appspot.com/123/quitserver. You should need to be logged in as an administrator to do this, but you don't.
This is another example of a common bug. The server protects against non-administrators accessing certain URLs but the list includes /quit instead of the actual URL /quitserver.
To fix, add /quitserver to the URLS only accessible to administrators:
_PROTECTED_URLS = [
    "/quitserver",
    "/reset"
]
Oops! This doesn't completely solve the problem. The reset URL is in the protected list. Can you figure out how to access it?

Hint

Look carefully at the code that handles URLs and checks for protected ones.

Another Exploit and Fix

To exploit, use http://google-gruyere.appspot.com/123/RESET. The check for protected urls is case sensitive. After doing that check, it capitalizes the string to look up the implementation. This is a classic check/use bug where the condition being checked does not match the actual use. This vulnerability is worse than the previous one because it exposes all the protected urls.
To fix, put the security check inside the dangerous functions rather than outside them. That ensures that no matter how we get there, the security check can't be skipped.

DoS - Overloading the Server

Find a way to overload the server when it processes a request.

Hint 1

You can upload a template that does this.

Hint 2

Every page includes the menubar.gtl template. Can you figure out how to make that template overload the server?

Exploit and Fix

To exploit, create a file named menubar.gtl containing:
[[include:menubar.gtl]]DoS[[/include:menubar.gtl]]
and upload it to the resources directory using a path traversal attack, e.g., creating a user named ../resources. To fix, implement the protections against path traversal and uploading templates discussed earlier.
NOTE: After performing the previous exploit, you'll need to push the reset button.

More on Denial of Service

Unlike a well defined vulnerability like XSS or XSRF, denial of service describes a wide class of attacks. This might mean bringing your service down or flooding your inbox so you can't receive legitimate mail. Some things to consider:
  • If you were evil and greedy, how quickly could you take down your application or starve all of its resources? For example, is it possible for a user to upload their hard drive to your application? Entering the attacker's mindset can help identify DoS points in your application. Additionally, think about where the computationally and memory intensive tasks are in your application and put safeguards in place. Do sanity checks on input values.
  • Put monitoring in place so you can detect when you are under attack and enforce per user quotas and rate limiting to ensure that a small subset of users cannot starve the rest. Abusive patterns could include increased memory usage, higher latency, or more requests or connections than usual.

Code Execution

If an attacker can execute arbitrary code remotely on your server, it's usually game over. They may be able to take control over the running program or potentially break out the process to open a new shell on the computer. From here, it's usually not hard to compromise the entire machine the server is running on.
Similar to information disclosure and denial of service, there is no recipe or specific defense to prevent remote code execution. The program must perform validation of all user input before handling it and where possible, implement functions with least privilege rights. This topic can't be done justice in just a short paragraph, but know that this is likely the scariest results a security bug can have and trumps any of the above attacks.

Code Execution Challenge

Find a code execution exploit.

Hint

You need to use two previous exploits.

Exploit and Fix

To exploit, make a copy of gtl.py (or sanitize.py) and add some exploit code. Now you can either upload a file named ../gtl.py or create a user named .. and upload gtl.py. Then, make the server quit by browsing to http://google-gruyere.appspot.com/123/quitserver. When the server restarts, your code will run.
This attack was possible because Gruyere has permission to both read and write files in the Gruyere directory. Applications should run with the minimal privileges possible.
Why would you attack gtl.py or sanitize.py rather than gruyere.py? When an attacker has a choice, they would usually choose to attack the infrastructure rather than the application itself. The infrastructure is less likely to be updated and less likely to be noticed. When was the last time you checked that no one had replaced python.exe with a trojan?
To fix, fix the two previous exploits.

More on Remote Code Execution

Even though there is no single or simple defense to remote code execution, here is a short list of some preventative measures:
  • Least Privilege: Always run your application with the least privileges it needs.
  • Application Level Checks: Avoid passing user input directly into commands that evaluate arbitrary code, like eval() or system(). Instead, use the user input as a switch to choose from a set of developer controlled commands.
  • Bounds Checks: Implement proper bounds checks for non-safe languages like C++. Avoid unsafe string functions. Keep in mind that even safe languages like Python and Java use native libraries.

Configuration Vulnerabilities

Applications are often installed with default settings that attackers can use to attack them. This is particularly an issue with third party software where an attacker has easy access to a copy of the same application or framework you are running. Hackers know the default account names and passwords. For example, looking at the contents of data.py you know that there's a default administrator account named 'admin' with the password 'secret'. See http://www.governmentsecurity.org/articles/default-logins-and-passwords-for-networked-devices.html for a sample hacker resource.
Configuration vulnerabilities also include features that increase attack surface. A common example is a feature that is on by default but you are not using, so you didn't configure it and the default configuration is vulnerable. It also includes debug features like status pages or dumping stack traces on failures.

Information disclosure #1

Read the contents of the database off of a running server by exploiting a configuration vulnerability.
You should look through the Gruyere code looking for default configurations or debug features that don't belong there.

Hint 1

Look at all the files installed with Gruyere. Are there any files that shouldn't be there?

Hint 2

Look for a .gtl file that isn't referenced anywhere.

Exploit and Fixes

To exploit, you can use the debug dump page dump.gtl to display the contents of the database via the following URL:
http://google-gruyere.appspot.com/123/dump.gtl
To fix, always make sure debug features are not installed. In this case, delete dump.gtl. This is an example of the kind of debug feature that might be left in an application by mistake. If a debug feature like this is necessary, then it needs to be carefully locked down: only admin users should have access and only requests from debug IP addresses should be accepted.
This exploit exposes the users' passwords. Passwords should never be stored in cleartext. Instead, you should use password hashing. The idea is that to authenticate a user, you don't need to know their password, only be convinced that the user knows it. When the user sets their password, you store only a cryptographic hash of the password and a salt value. When the user re-enters their password later, you recompute the hash and if it matches you conclude the password is correct. If an attacker obtains the hash value, it's very difficult for them to reverse that to find the original password. (Which is a good thing, since despite lots of advice to the contrary, users frequently use the same weak passwords for multiple sites.)

Information disclosure #2

Even after implementing the fix described above, an attacker can undo it and execute the attack! How can that be?

Hint

You can upload a file of any type.

Exploit and Fixes

To exploit, Gruyere allows the user to upload files of any type, including .gtl files. So the attacker can simply upload their own copy of dump.gtl or a similar file and than access it. In fact, as noted earlier, hosting arbitrary content on the server is a major security risk whether it's HTML, JavaScript, Flash or something else. Allowing a file with an unknown file type may lead to a security hole in the future.
To fix, we should do several things:
  1. Only files that are part of Gruyere should be treated as templates.
  2. Don't store user uploaded files in the same place as application files.
  3. Consider limiting the types of files that can be uploaded (via a whitelist).

Information disclosure #3

Even after implementing the fixes described above, a similar attack is still possible through a different attack vector. Can you find it?
This attack isn't a a configuration vulnerability, just bad code.

Hint 1

You can insert something in your private snippet which will display the contents of the database.

Hint 2

This attack is closely related to the previous ones. There is a bug in the code that expands templates that you can exploit.

Exploit and Fixes

There is a defect in Gruyere's template expansion code that reparses expanded variables. Specifically, when expanding a block it expands variables in the block. Then it parses the block as a template and expands variables again,
To exploit, add this to your private snippet:
{{_db:pprint}}
To fix, modify the template code so it never reparses inserted variable values. The defect in the code is due to the fact that ExpandTemplate calls _ExpandBlocks followed by _ExpandVariables, but _ExpandBlocks calls ExpandTemplate on nested blocks. So if a variable is expanded inside a nested block and contains something that looks like a variable template, it will get expanded a second time. That sounds complicated because it is complicated. Parsing blocks and variables separately is a fundamental flaw in the design of the expander, so the fix is non-trivial.
This exploit is possible because the template language allows arbitrary database access. It would be safer if the templates were only allowed to access data specifically provided to them. For example, a template could have an associated database query and only the data matched by that query would be passed to the template. This would limit the scope of a bug like this to data that the user was already allowed to access.


AJAX vulnerabilities

Bad AJAX code allows attackers to modify parts of your application in ways that you might not expect. In traditional client development, there is a clear separation between the application and the data it displays. That's not true in web applications as the next two attacks will make clear.

DoS via AJAX

Find an attack that prevents users from seeing their private snippets on the home page. (The attack should be triggered after clicking the refresh link and without using XSS.)

Hint 1

Can you figure out how to change the value of the private snippet in the AJAX response?

Hint 2

What happens if a JSON object has a duplicate key value?

Exploit and Fix

To exploit, create a user named private_snippet and create at least one snippet. The JSON response will then be {'private_snippet' : <user's private snippet>, ..., 'private_snippet' : <attacker's snippet>} and the attacker's snippet replaces the user's.
To fix, the AJAX code needs to make sure that the data only goes where it's supposed to go. The flaw here is that the JSON structure is not robust. A better structure would be [<private_snippet>, {<user> : <snippet>,...}].

Phishing via AJAX

While the previous attack may seem like a minor inconvenience, careless DOM manipulation can lead to much more serious problems.
Find a way to change the sign in link in the upper right corner to point to http://evil.example.com.
(The attack should be triggered after clicking the refresh link and without using XSS or a script.)

Hint 1

Look at what the script does to replace the snippets on the page. Can you get it to replace the sign in link?

Hint 2

Look at the AJAX code to see how it replaces each snippet, and then look at the structure of the home page and see if you can see what else you might be able to replace. (You can't just replace the sign in link. You'll have to replace a bit more.)

Exploit and Fix

To exploit, create a user named menu-right and publish a snippet that looks exactly like the right side of the menu bar.
<a href='http://evil.example.com/login'>Sign in</a>
| <a href='http://evil.example.com/newaccount.gtl'>Sign up</a>
If the user is already logged in, the menu bar will look wrong. But that's ok, since there's a good chance the user will just think they somehow accidentally got logged out of the web site and log in again.
To fix, the process of modifying the DOM needs to be made more robust. When user values are used in as DOM element identifiers, you should ensure that there can't be a conflict as there is here, for example, applying a prefix to user values like id="user_". Even better, use your own identifiers rather than user values.
This spoofing attack is easily detected when the user clicks Sign in and ends up at evil.example.com. A clever attacker could do something harder to detect, like replacing the Sign in link with a script that renders the sign in form on the current page with the form submission going to their server.

Other Vulnerabilities

Buffer Overflow and Integer Overflow

A buffer overflow vulnerability exists when an application does not properly guard its buffers and allow user data to write past the end of a buffer. This excess data can modify other variables, including pointers and function return addresses, leading to arbitrary code execution. Historically, buffer overflow vulnerabilities have been responsible for some of the most widespread internet attacks including SQL Slammer, Blaster and Code Red computer worms. The PS2, Xbox and Wii have all been hacked using buffer overflow exploits.
While not as well known, integer overflow vulnerabilities can be just as dangerous. Any time an integer computation silently returns an incorrect result, the application will operate incorrectly. In the best case, the application fails. In the worst case, there is a security bug. For example, if an application checks that length + 1 < limit then this will succeed if length is the largest positive integer value, which can then expose a buffer overflow vulnerability.
This codelab doesn't cover overflow vulnerabilities because Gruyere is written in Python, and therefore not vulnerable to typical buffer and integer overflow problems. Python won't allow you to read or write outside the bounds of an array and integers can't overflow. While C and C++ programs are most commonly known to expose these vulnerabilities, other languages are not immune. For example, while Java was designed to prevent buffer overflows, it silently ignores integer overflow.
Like all applications, Gruyere is vulnerable to platform vulnerabilities. That is, if there are security bugs in the platforms that Gruyere is built on top of, then those bugs would also apply to Gruyere. Gruyere's platform includes: the Python runtime system and libraries, AppEngine, the operating system that Gruyere runs on and the client side software (including the web browser) that users use to run Gruyere. While platform vulnerabilities are important, they are outside the scope of this codelab as you generally can't fix platform vulnerabilities by making changes to your application. Fixing platform vulnerabilities yourself is also not practical for many people, but you can mitigate your risks by making sure that you are diligent in applying security updates as they are released by platform vendors.

SQL Injection

Just as XSS vulnerabilities allow attackers to inject script into web pages, SQL injection vulnerabilities allow attackers to inject arbitrary scripts into SQL queries. When a SQL query is executed it can either read or write data, so an attacker can use SQL injection to read your entire database as well as overwrite it, as described in the classic Bobby Tables XKCD comic. If you use SQL, the most important advice is to avoid building queries by string concatenation: use API calls instead. This codelab doesn't cover SQL injection because Gruyere doesn't use SQL.


After the Codelab

We hope that you found this codelab instructive. If you want more practice, there are many more security bugs in Gruyere than the ones described above. You should attack your own application using what you've learned and write unit tests to verify that these bugs are not present and won't get introduced in the future. You should also consider using fuzz testing tools. For more information about security at Google, please visit our blog or our corporate security page. And check out Google Code University for more educational resources on a variety of other topics.
If you'd like to share this codelab with others, please consider tweeting or buzzing about it or posting one of these badges on your blog or personal page:
Learn how to make web apps more
secure. Do the Gruyere codelab.
<a href="http://google-gruyere.appspot.com/">
<img src="//google-gruyere.appspot.com/static/gruyere-badge.png"
style="padding:4pt" border="0" alt="Learn how to make web apps more
secure. Do the Gruyere codelab."></a>
(Line breaks above should be copied verbatim.)
Learn how to make web apps more
secure. Do the Gruyere codelab.
<a href="http://google-gruyere.appspot.com/">
<img src="//google-gruyere.appspot.com/static/gruyere-40.png"
style="padding:4pt" border="0" title="Learn how to make web apps more
secure. Do the Gruyere codelab." alt="Learn how to make web apps more 
secure. Do the Gruyere codelab."></a>
(Line breaks above should be copied verbatim.)

Post a Comment

 
Top
Google+