Macy's (http://www.macys.com/) calls itself "the magic of macy's" (at least the word magic is mentioned on their main home page logo). It has a global Alexa rank of 241 at the time of writing. It is a high profile web application. I think not from security perspective but from traffic point of view. Let the game begin. Please open the following URL (keyword GET parameter holds our harmless XSS probe string i.e., "xxxxxxxx'yyyyy</img). The following screen-shot shows reflection of our interest and some other cool things.
Lets start connecting few dots ...
The following line (in particular line #1911 in the screen-shot above and I marked it with label "Seems Interesting (3)") of JavaScript code (keywordsearch = kws.replace(/"/g,'\\\"');) replaces the " globally in the input with \\\" i.e., developers try to escape " with the help of three backslashes. Though escaping with three backslashes works fine given developers're using double quotes for holding the input in script context in general. Normally one backslash like \" (for double quotes) or \\ (for backslash) is enough (e.g., see line #21 in the source code in Chrome).
Note: FYI, on YouTube Gaming web site, you will find escaping with single backslash (i.e., \") and escaping with three backslashs (i.e., \\\") on the same page. Please see line #204 and line #200 in the source code in Chrome browser respectively.
So far so good from the application perspective but wait ... What about backslash (\) if injected in the attack payload? Are they escape backslash? Some of you might remember the blog post I did sometime ago and it was related to backslash. At that time I mentioned, my quest for backslash based XSS will continue and I think I am done with it (you will see when you're done reading this post). For example, open the following URL (http://www1.macys.com/shop/search?keyword=\) and if you will look into the source code, backslash is not escaped (see screen-shot below).
The following XSS attack attack payload can do the job for us i.e., \"; confirm(1); //. The URL looks like: www1.macys.com/shop/search?keyword=\";%20confirm(1);%20//. Some of you might be thinking why I started the payload with \. I explain but first look at the screen-shot of injection i.e., \"; confirm(1); //.
The injected XSS payload \"; confirm(1); // becomes \\\\"; confirm(1); // because " is replaced by \\\" in order to make it an escape character and at the same time, \ is not escaped. In short we have four backslashes in the reflection. Further, please remember, we're inside double quotes because developers're using them for keywordsearch variable declaration. The JavaScript code snippet looks like keywordsearch = "\\\\"; confirm(1); //"; How to interpret this code? The number of backslashes as far as one or three or five (i.e., odd number) is concerned, it is fine but if the count is even number i.e., two or four or six ... & so on then it will make the effect of escaping NULL N VOID.
In short, double quote is no more an escaping character because of four backslashes. The browser will consider it a proper closing double quote (in pair) for a variable declaration. In the XSS attack payload, now comes ; which is a statement terminator. It is followed by a proof of concept JavaScript function call i.e, confirm(1);. Last but not the least, we have a single line comment (i.e., //) in order to neutralize the effect of closing ";. It seems all set for an XSS and the following URL will result in an XSS: www1.macys.com/shop/search?keyword=\";%20confirm(1);%20//. Unfortunately NOT... but why? The game is still on ... It is about connecting some dots.
If you look at one line (i.e., line #1949 because line #1950 is a comment ) above the keywordsearch variable declaration that holds our reflection then you will realize that there is a conditional statement and it is not executed. It means that the control never reaches at the reflection or injection point because the conditional statement evaluates to false at the moment. Why false? The conditional statement is:
while currentPageUrl is a variable i.e., var currentPageUrl = window.location.href. The window.location.href returns the current location of the document and assigns it to the variable currentPageUrl. The URL at the moment is http://www1.macys.com/shop/search?keyword=\%22;%20confirm(1);%20//. The JavaScript string function indexOf returns the first occurrence of the specified value, otherwise returns -1. The specified value in the indexOf function is "cm_kws". The current URL has no occurrence of "cm_kws". It means inside conditional statement, currentPageUrl.indexOf("cm_kws") evaluates to -1 and we all know that -1 != -1 is false :) The end goal is to make this conditional statement true so that the control reaches at the point of reflection and we will be able to execute JavaScript code of our choice.
I think, it is possible to force conditional statement so that it evaluates to true by appending the specified value i.e., "cm_kws" in the URL given currentPageUrl gets the value from window.location.href as I mentioned earlier and can be seen in the screen-shots. The job can be done by adding a new GET parameter named "cm_kws" like http://www1.macys.com/shop/search?keyword=\%22;%20confirm(1);%20//&cm_kws or by simply padding the string "cm_kws" at the end of URL like http://www1.macys.com/shop/search?keyword=\%22;%20confirm(1);%20//cm_kws. In case of adding a new GET parameter "cm_kws", the statement currentPageUrl.indexOf("cm_kws") inside if evaluates to 66 and we all know 66 != -1 is true. In case of padding "cm_kws" at the end of URL, the statement inside if evaluates to 65 and we all know that 65 != -1 is true. The screen-shot given below shows the results of evaluation for better understanding.
It seems all set for an XSS i.e., dots have been connected. The following URL shows a confirm box followed by screen-shot.
For the sake of completeness, I wanted to XSS it with </script><script>confirm(1)</script> given in the reflection (see first screen-shot) </ is not filtered or encoded. The URL looks like ...
It has been blocked by some kind of protection layer. I think WAF or any other protection mechanism (e.g., IPS/IDS) stops the payload. The screen-shot shows Access Denied.
I had seen the same screen-shot on Akamai's site. It makes sense to believe or reasonable to believe that Akamai is not using other vendor's security solutions :) Isn't it? Akamai has its own WAF also i.e., Kona. Builtwith also gives some information that site is using Akamai's hosting services. Further, I am still interested if somebody will tell me or confirm which WAF is in place over there. Your feedback is always welcome.
WAF is meant to bypass and this one is not different. As I said earlier that </script> was blocked but </script%0Aanything> was not blocked. The following are quick bypasses and the screen-shot related to the second URL is also given below.
Before conclusion, I noticed that Macy's allowed to have single (') and double quotes (") in the product name (in case if required). I was thinking that it would not be easy for them to handle single (') and double quotes ("). For example, see the following two products ...
A question came to my mind: what will happen if I will do a search query of the product that have single (') and double quotes (") in their name. How site will treat it? Lets make a search query. I query for the following string i.e., a legit product name having both types of quotes ...
The resulting URL is:
It returns 3 results at the time of writing. Now lets look at the source code and try to figure out any thing interesting. As expected, site starts breaking itself :) The first thing I noticed is a legit search query breaks the attribute context (see screen-shot below).
The second interesting reflection in that case was in script context. It can be seen in the following screen-shot. The screen-shot shows that " from the product name is not escaped or filtered or encoded.
It means that we can leverage the " from the existing product name in constructing the XSS attack string. The XSS payload looks like (please see that the proof of concept JavaScript code does not start with " because we will use the " from the product name):
Now combine this proof of concept XSS payload with the legit search query we made earlier. The final attack string will be ...
The URL at the time of XSS is given below followed by a screen-shot.
I conclude on a saying: "When You Really Pay Attention, Everything Is Your Teacher." (Ezra Bayda)
Lets start connecting few dots ...
The following line (in particular line #1911 in the screen-shot above and I marked it with label "Seems Interesting (3)") of JavaScript code (keywordsearch = kws.replace(/"/g,'\\\"');) replaces the " globally in the input with \\\" i.e., developers try to escape " with the help of three backslashes. Though escaping with three backslashes works fine given developers're using double quotes for holding the input in script context in general. Normally one backslash like \" (for double quotes) or \\ (for backslash) is enough (e.g., see line #21 in the source code in Chrome).
Note: FYI, on YouTube Gaming web site, you will find escaping with single backslash (i.e., \") and escaping with three backslashs (i.e., \\\") on the same page. Please see line #204 and line #200 in the source code in Chrome browser respectively.
So far so good from the application perspective but wait ... What about backslash (\) if injected in the attack payload? Are they escape backslash? Some of you might remember the blog post I did sometime ago and it was related to backslash. At that time I mentioned, my quest for backslash based XSS will continue and I think I am done with it (you will see when you're done reading this post). For example, open the following URL (http://www1.macys.com/shop/search?keyword=\) and if you will look into the source code, backslash is not escaped (see screen-shot below).
The following XSS attack attack payload can do the job for us i.e., \"; confirm(1); //. The URL looks like: www1.macys.com/shop/search?keyword=\";%20confirm(1);%20//. Some of you might be thinking why I started the payload with \. I explain but first look at the screen-shot of injection i.e., \"; confirm(1); //.
The injected XSS payload \"; confirm(1); // becomes \\\\"; confirm(1); // because " is replaced by \\\" in order to make it an escape character and at the same time, \ is not escaped. In short we have four backslashes in the reflection. Further, please remember, we're inside double quotes because developers're using them for keywordsearch variable declaration. The JavaScript code snippet looks like keywordsearch = "\\\\"; confirm(1); //"; How to interpret this code? The number of backslashes as far as one or three or five (i.e., odd number) is concerned, it is fine but if the count is even number i.e., two or four or six ... & so on then it will make the effect of escaping NULL N VOID.
In short, double quote is no more an escaping character because of four backslashes. The browser will consider it a proper closing double quote (in pair) for a variable declaration. In the XSS attack payload, now comes ; which is a statement terminator. It is followed by a proof of concept JavaScript function call i.e, confirm(1);. Last but not the least, we have a single line comment (i.e., //) in order to neutralize the effect of closing ";. It seems all set for an XSS and the following URL will result in an XSS: www1.macys.com/shop/search?keyword=\";%20confirm(1);%20//. Unfortunately NOT... but why? The game is still on ... It is about connecting some dots.
If you look at one line (i.e., line #1949 because line #1950 is a comment ) above the keywordsearch variable declaration that holds our reflection then you will realize that there is a conditional statement and it is not executed. It means that the control never reaches at the reflection or injection point because the conditional statement evaluates to false at the moment. Why false? The conditional statement is:
if(currentPageUrl.indexOf("cm_kws")!= -1)
while currentPageUrl is a variable i.e., var currentPageUrl = window.location.href. The window.location.href returns the current location of the document and assigns it to the variable currentPageUrl. The URL at the moment is http://www1.macys.com/shop/search?keyword=\%22;%20confirm(1);%20//. The JavaScript string function indexOf returns the first occurrence of the specified value, otherwise returns -1. The specified value in the indexOf function is "cm_kws". The current URL has no occurrence of "cm_kws". It means inside conditional statement, currentPageUrl.indexOf("cm_kws") evaluates to -1 and we all know that -1 != -1 is false :) The end goal is to make this conditional statement true so that the control reaches at the point of reflection and we will be able to execute JavaScript code of our choice.
I think, it is possible to force conditional statement so that it evaluates to true by appending the specified value i.e., "cm_kws" in the URL given currentPageUrl gets the value from window.location.href as I mentioned earlier and can be seen in the screen-shots. The job can be done by adding a new GET parameter named "cm_kws" like http://www1.macys.com/shop/search?keyword=\%22;%20confirm(1);%20//&cm_kws or by simply padding the string "cm_kws" at the end of URL like http://www1.macys.com/shop/search?keyword=\%22;%20confirm(1);%20//cm_kws. In case of adding a new GET parameter "cm_kws", the statement currentPageUrl.indexOf("cm_kws") inside if evaluates to 66 and we all know 66 != -1 is true. In case of padding "cm_kws" at the end of URL, the statement inside if evaluates to 65 and we all know that 65 != -1 is true. The screen-shot given below shows the results of evaluation for better understanding.
It seems all set for an XSS i.e., dots have been connected. The following URL shows a confirm box followed by screen-shot.
For the sake of completeness, I wanted to XSS it with </script><script>confirm(1)</script> given in the reflection (see first screen-shot) </ is not filtered or encoded. The URL looks like ...
It has been blocked by some kind of protection layer. I think WAF or any other protection mechanism (e.g., IPS/IDS) stops the payload. The screen-shot shows Access Denied.
I had seen the same screen-shot on Akamai's site. It makes sense to believe or reasonable to believe that Akamai is not using other vendor's security solutions :) Isn't it? Akamai has its own WAF also i.e., Kona. Builtwith also gives some information that site is using Akamai's hosting services. Further, I am still interested if somebody will tell me or confirm which WAF is in place over there. Your feedback is always welcome.
WAF is meant to bypass and this one is not different. As I said earlier that </script> was blocked but </script%0Aanything> was not blocked. The following are quick bypasses and the screen-shot related to the second URL is also given below.
Before conclusion, I noticed that Macy's allowed to have single (') and double quotes (") in the product name (in case if required). I was thinking that it would not be easy for them to handle single (') and double quotes ("). For example, see the following two products ...
A question came to my mind: what will happen if I will do a search query of the product that have single (') and double quotes (") in their name. How site will treat it? Lets make a search query. I query for the following string i.e., a legit product name having both types of quotes ...
Under Armour Men's Raid Performance 10" Shorts
The resulting URL is:
It returns 3 results at the time of writing. Now lets look at the source code and try to figure out any thing interesting. As expected, site starts breaking itself :) The first thing I noticed is a legit search query breaks the attribute context (see screen-shot below).
The second interesting reflection in that case was in script context. It can be seen in the following screen-shot. The screen-shot shows that " from the product name is not escaped or filtered or encoded.
It means that we can leverage the " from the existing product name in constructing the XSS attack string. The XSS payload looks like (please see that the proof of concept JavaScript code does not start with " because we will use the " from the product name):
-confirm(1)-"
Now combine this proof of concept XSS payload with the legit search query we made earlier. The final attack string will be ...
Under Armour Men's Raid Performance 10"-confirm(1)-" Shorts
The URL at the time of XSS is given below followed by a screen-shot.
I conclude on a saying: "When You Really Pay Attention, Everything Is Your Teacher." (Ezra Bayda)