Whoa, What a summer!
I know, we haven’t been active in the last month – blame that on the heat and on my addiction to the sea. I should really get a swimming pool.
OK, enough talking! The summer is almost over and now its time to step on the gas pedal full time.
Today’s case-study also discusses proper user-supplied input handling, but with a twist.
I feel like I talked enough about the importance of properly handling and sanitizing user supplied input. There are tons of XSS and HTML filters out there, and they are doing pretty good job.
But user input doesn’t always being shown onto the page or inserted into the DB. In some cases, many popular web platforms stores it in a cookie.
PayPal, for example, inserts the value of the GET parameter ‘cmd’ as the value of the cookie ‘navcmd’:
GET https://cms.paypal.com/cgi-bin/marketingweb?cmd=test HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
Accept-Encoding: gzip, deflate, br
HTTP/1.1 404 Not Found
Cache-Control: must-revalidate, proxy-revalidate, no-cache, no-store
Cache-Control: max-age=0, no-cache, no-store, must-revalidate
Content-Type: text/html; charset=UTF-8
Date: Wed, 30 Aug 2017 21:15:04 GMT
Set-Cookie: navcmd=test; domain=.paypal.com; path=/; Secure; HttpOnly
There’s no evil with storing user supplied input in a cookie, and its actually a good practice sometimes, if you don’t want to use sessions or other similar mechanism.
A very common use for user supplied input in a cookie is storing a redirect URL: Sometimes you want to remember from which page the user came from, or to where redirect him at the end of the process. Keep that in mind.
Before I’ll get to the vulnerability itself, I’ll tease you a bit and say that this time, the malicious payload bypassed the XSS & HTML sanitation mechanism.
A very known financing company had this exact cookie functionality. User input from some GET parameters has been stored in some cookies. For example, the value of the GET parameter ‘redirect_url’ was stored in the cookie ‘return_url’. This cookie was then used by dozens of other pages in order to redirect users to a “thank you” page. An open redirect attack on that parameter was not possible, because the value of the GET parameter ‘redirect_url’ has been checked & verified before allowing it to be added as a cookie.
At first glance – everything looks fine. I’ve read the JS code that was responsible for sanitizing the input and determined that its doing its job pretty well – no HTML entities or other “bad” characters (like ‘, “) were able to be reflected – thanks to the encodeURI function that was being used intensively.
And then it hit me. encodeURI doesn’t encode characters like ; or = – The exact characters that are being used when setting a cookie!
So, a GET request to the vulnerable URL, without the ‘return_url’ GET parameter (to prevent collisions):
GET https://vulnsite.com/action?vulnGETParameter=xyz;return_url=https://fogmarks.com HTTP/1.1
HTTP/1.1 200 OK
Set-Cookie: vulnGETParameter=xyz;return_url=https://fogmarks.com; domain=.vulnsite.com; path=/; Secure; HttpOnly
The result of this in some cases was an open redirect in pages that relied on the fact the value of ‘return_url’ will always be safe.
When you decide to store user input in a cookie, you must know how to treat it well, and you must remember to dispose it when the time is right. In this case, using the same sanitation mechanism for input that will be shown onto the page and input that will be inserted to a cookie is wrong.
The patch here was simple: instead of using encodeURI, encodeURIComponent() was used.
Happy & chilled autumn folks!