Introduction
Insufficient input validation is a problem I encounter practically every time I test an application. I’ve talked about relying on input validation as a prevention mechanism before (see here and item #8 here) but since it’s such a prevalent problem I figured I’d take the time to write about it once again.
While a good supplemental control, by itself input validation is usually woefully inadequate. Quite frankly, it often requires much more effort to try and develop an input validation function that is considered comprehensive enough to use as a stand-alone control vs. using it in conjunction with other, more effective controls (such as output encoding, parameterized queries, etc). I’ve seen some really creative input validation approaches, including very complex regex or other “clever” filtering approaches, most of which are designed to “outsmart” the attacker. The problem with this blacklist-style approach is it must consider every possible permutation of input, and not surprisingly, usually falls short. [I’d love to publish some of my favorites but unfortunately can’t publicly disclose most of my test results].
The real-world example I am able to give today is a simple cross-domain validation flaw I found in one of Paypal’s hosted flash files*. Although the resulting exploit was nothing to write home about, I think it once again highlights why input validation (especially client-side) is never a sufficient stand-alone control, especially when it comes to restricting cross-domain access of flash content.
Finding the Vulnerability
In this particular case, I was conducting a little experiment on bug bounty programs and had shifted my attention to Paypal. I knew it was a big user of flash content so I figured I’d start by seeing what type of hosted .swf objects I could find via Google:
One of the files I came across was pp_demo_player.swf which played a demonstration video on some features of their Mastercard.
The full path to the file was as follows (it has since been removed):
https://cms.paypal.com/cms_content/US/en_US/files/bml/pp_demo_player.swf?config=/cms_content/US/en_US/files/bml/pp_ge_ms_p2v1_05_A_640x360_config.xml&debug=false
Notice the relative path to the *_config.xml file (more on this shortly). This definitely sparked my interest enough to warrant further investigation.
I downloaded the .swf file and opened it in my flash decompiler of choice (http://www.free-decompiler.com/flash/download.html).
While browsing the “scripts folder” I came across the “ApplicationFacade” script:
One of the nice features of JPEXS is the “Traits” window which provides a quick glimpse into the variables declared within a given script. The one that caught my eye was public static const ALLOWED_DOMAINS, an array of regular expressions intended to limit the domains from which flash media content can be loaded.
The array has multiple regular expressions (each for a different, authorized domain) but all of them suffer from the same validation problem, so I’ll only list one of them below.
public static const ALLOWED_DOMAINS:Array = [ ..., "^(([0-9a-zA-Z]+:)?/{0,2})?([^/]*\\.)?paypal\\.com(:[0-9]+)?/?.*$", ... ];
The intention of this regex is to limit the playable content to only that which resides on an authorized paypal.com domain. While it does a good job of blocking domains such as hxxp://www.malicousdomain.com/paypal.com, it is ineffective at preventing the use of a malicious domain that has a subdomain of paypal.com. For example, the following domain would pass the regex test: hxxp://paypal.com.maliciousdomain.com.
Also, the very fact that this regex array contains multiple domains (listed below) is an indicator that the site supports cross-domain content, another “red flag”:
- paypalobjects.com
- paypal.com
- paypal.de
- paypal.co.uk
- paypal.com.au
- paypal.fr
- paypal.com.fr
- paypal.ch
- paypal.com.hk
- paypal.com.mx
If you’re familiar with the Flash Player security model, cross-domain loading of data is prevented by default. However if required, the restriction can be relaxed by listing the authorized domains within a crossdomain.xml policy file (hosted on the web server). There are several reasons as to why this might be required, but if not implemented properly, it may allow for too broad (or even completely unrestricted — *.*) access.
My plan was to prove I could exploit this client-side validation flaw and the site’s use of cross-domain content and force the Paypal site to host any video of my choosing.
Exploiting the Validation Flaw
The regex array is passed to a function within pp_demo_player.swf file called validateUrl:
private function validateUrl(param1:String) : * { var _loc2_:* = false; var _loc3_:Array = ApplicationFacade.ALLOWED_DOMAINS; var _loc4_:uint = 0; while(_loc4_ < _loc3_.length) { if(param1.search(_loc3_[_loc4_]) != -1) { _loc2_ = true; break; } _loc4_++; } return _loc2_; }
Pretty straightforward: loop through the ALLOWED_DOMAINS array elements and if the URL passes the regex, accept it. This function is called as follows:
if(validateUrl(_loc2_)) { loadData(param1); } else { Logger.LOG(Logger.CRITICAL,"XML config file domain is not allowed (" + _loc2_ + ")"); } }
But where is this “XML config” file that dictates which video will be loaded (and which URL will be passed to the validation function)? For that we have to return to the *_config.xml file referenced in the original URL:
https://cms.paypal.com/cms_content/US/en_US/files/bml/pp_demo_player.swf?config=/cms_content/US/en_US/files/bml/pp_ge_ms_p2v1_05_A_640x360_config.xml&debug=false
The downloaded config file contained the following:
Note the chapter file attribute identifies the .flv video that is supposed to be loaded which I’ll change to another video of my choosing. Since PayPal was the target, I decided to go with a video advertising a “PayPal Money Hack” hosted on an arbitrary, external media site:
I updated the file attribute in the *_config.xml file to point to this new video:
Now I just needed to host this *_config.xml file on a server under my control with a path that could pass the flawed regex filter.
I set up a server (for the purposes of the demo I’ll call it localhost) with a sub-domain of paypal.com, where I hosted the above *_config.xml file in the document root along with the following unrestricted crossdomain.xml file:
Now I could force cms.paypal.com to load the “paypal_money_hack” video (or any other of my choosing) by using the following URL:
https://cms.paypal.com/cms_content/US/en_US/files/bml/pp_demo_player.swf?config=hxxp://paypal.com.localhost/pp_ge_ms_p2v1_05_A_640x360_config.xml&debug=true
Conclusion
Hopefully, this simple demo illustrated the potential problems that can arise from poorly configured cross-domain policies and the reliance on client-side input validation.
Of course, poorly configured cross-domain policies can lead to many other security issues more serious than what I’ve illustrated here (http://jeremiahgrossman.blogspot.com/2008/05/crossdomainxml-invites-cross-site.html) and cross domain security extends well beyond a properly configured crossdomain.xml file (http://msdn.microsoft.com/en-us/library/cc709423(v=vs.85).aspx) so I definitely recommend researching this topic further, if you’re so inclined.
Until next time…
*This was one of two vulnerabilities I reported to PayPal mid-2013 that did not qualify for a bounty because they stated it had already been reported (the third time was the charm: http://www.securitysift.com/bug-bounty-blitz/).