To exemplify the attacks described in this blog post, an online store was created. It only presents a login form, some articles for sale, and ads. It uses a (modified) HTML template from https://shapebootstrap.net. It is then called from an Android application inside a WebView.
Two fake domains were used: myvictimexampledomain.com and myattackerexampledomain.com. In real scenarios, these domains would never be chosen, they are just used in this post for a clear distinction between the victim and an attacker.
The ads.js file has the following content:
When the page loads the previously referred malicious code, everything seems exactly the same as before but in reality, the attacker is able to view the users’ SessionKeys. The attacker just has to look at the attacking web server logs:
The attacker can now hijack all user sessions, and this is far from being the only possible attack.
A part from this scenario where the page owner includes the malicious code, another common situation for this kind of attack is hostile subdomain takeover.
In the previous example, an iframe can be used to block access to the DOM. To achieve this, the “.js” file from the previous example was converted into the following “.html”:
The following change was made in the shop HTML code:
When the web page is loaded the design looks exactly the same as before although the previous attack doesn’t work anymore:
Notice that the requester has changed from “www.myvictimexampledomain.com/shop/” to “myattackerexampledomain.com/ads/ads.html”. As it is being called inside an iframe, it behaves like a completely different page. However, both pages are being loaded in the same browser and the attacker can still take advantage of this to use the user’s sessions for accessing restricted content. This scenario will be discussed later in this blog post.
USING XSSI TO EXFILTRATE DATA
Nowadays, all browsers implement the Same-Origin Policy (SOP) and this mechanism blocks replies from domains that are accessed from different domains. As seen in the previous example, if the attacker tried to access the parent page with “parent.document” this access would be denied by SOP because the page loaded in the iframe is in a different domain than the parent’s domain. Despite of this, there are tags that are allowed by the SOP to be called from different domains, for instance:
- <script src=”…”></script>
- <link rel=”stylesheet” href=”…”>
When accessing content from the online shop using the tags described above, the browser will allow the reply and will also send the respective cookies in the request; even if the requester is the malicious code that is running inside the iframe.
The online shop is using JSONP to consult the server for the client personal data, and it places some of this data in the HTML code for future use in the front-end. This was implemented using the following PHP code:
This code is actually exfiltrating all the information that is being replied by the PHP function, which includes the SessionKeys. To better understand this behavior, the page was loaded and the HTTP traffic was collected. Some of the traffic is shown next and demonstrates that the cookie is being sent and the SOP had no action in the request:
Once again, the attacker can obtain all the client’s session information by consulting the attacking web server logs:
This attack is known as Cross-Site Script Inclusion (XSSI). The main difference between XSSI and Cross-Site Scripting (XSS) is that in XSSI, the malicious code is placed in third party web content, whereas in XSS the malicious code is placed in the victim’s web site. The main purpose of XSSI is to exfiltrate data and it is often seen in malicious web pages that serve illegal content.
In conclusion, using JSONP is bad practice because it makes the web page lose protection from the SOP. Another point worth mentioning is that if a Cross-Origin Resource Sharing (CORS) mechanism is implemented, it must be properly customized to prevent this kind of attack. To increase protection when using iframes, it is advised to explore the sandbox property and implement a Content Security Policy (CSP).
USING STORED XSS TO DENY SERVICE TO ALL USERS
The injection has to be made via an input, whether it is text input, file upload, HTTP header, etc., any kind of data that is somehow crafted and will be executed by the browser.
There are three types of XSS: Stored, Reflected and DOM Based. The key definition to the attack is sending malicious code in the request that will be executed by the browser when it gets the response from the server. If the code is sent in the request and the server places that code in the content of the page that is replied to the client, it is a scenario of Stored or Reflected XSS. If the malicious code was saved in the server side it is Stored XSS. On the other hand, if the code is only reflected in the response from the server it is Reflected XSS.
Dom Based XSS is less known and its concept is mainly the same as the other two types. The difference is that the server doesn’t place the malicious code in the page. The server receives the malicious code similarly to a Reflected XSS but doesn’t reflects it in the answer. It is the DOM that includes the malicious code, and that happens in the client side.
All these attacks are triggered due to lack of validation. Validation should be done in the input and the output as well. The most effective validation is by whitelisting, but that is only possible in scenarios where the data can only be part a limited set of values. When this is not possible, escaping (encoding) and sanitization (filtering) libraries must be used to prevent malicious code from being stored or executed.
Other security measures can be implemented to prevent XSS. For example, the measures include defining the HTTPOnly cookie flag, implementing CSP and the X-XSS-Protection header.
Taking the previous online shop example, it was implemented a section that shows the name of the clients that are active in the website. When these clients log in, their name appears in this section and remains there until their session is closed or expire.
When a user registers for the first time, there is no input validation in the name field. So, if a user places malicious code after the name, it will be saved in the database. Worse is the fact that every time that the name is printed in WebView, the malicious code will be executed.
Capturing the HTTP response traffic in the webserver is possible to see the evil code placed by the attacker and is being sent to all clients that access the website:
And with this successful attack, no user is able to login to the online shop resulting in a huge amount of profit loss. As mentioned before, much worse scenarios are found in the wild. XSS must not be underestimated and the best way to mitigate it is validate all input and output.
USING STORED XSS TO HIJACK CLICKS
To discuss the clickjacking attack, the previous Stored XSS vulnerability in the online shop was used. This attack allows to beat the SOP and the Anti-CSRF tokens. It consists of loading a legitimate webpage inside an iframe and loading it inside a second website. By using the “opacity” property, it is possible to completely hide the iframed page and fool the user to click on it instead of the website that is showing in the browser.
Taking the previous example of the online shop, it was possible to inject code in the user name. This code was then loaded by every user that visited the page. To trigger the Stored XSS vulnerability, instead of the DoS attack referred before, the following code was introduced in the username field: Bill Moore<iframe src=”http://www.mydonationsexampledomain.com/donations/someguy.html” frameBorder=”0″ scrolling=”no” style=”opacity:0; display:block; position:fixed; top:60px; left:-150px; height:400px;”/>.
The http://www.mydonationsexampledomain.com/donations/someguy.html URL is a website that allows to donate money to causes, companies, associations or individuals. In this case, the “someguy.html” page will allow to donate to a specific individual and it looks like the following:
When one of the buttons is clicked, the donation is completed and the following page is shown:
The victim of the online shop is also a user of this donations website and has the session open. Many users don’t close their sessions and that is why a short expiration time in cookies is important.
Returning to the online shop post-XSS attack, it looks harmless as before:
To better understand what is happening, a second screenshot was taken having the opacity of the previously referred iframe (where the donations site is loaded) set to 1. This way it is possible to visualize both pages and that a donate button is over the search box of the online shop:
When a user presses the search box in the online shop he/she is actually donating money to an attacker. Keeping the opacity value set to 1 it is possible to see the result:
When replaying the attack with the opacity set to 0, nothing seems suspicious to the victim. The only abnormal behavior is that the search box seems to be malfunctioning:
This is a very powerful attack and in order to stop it, the donation-website should use CSP or the X-Frame-Options header, to not allow it to be loaded within an iframe. Another security measure would be a programmatic validation to check whether the page is being loaded inside an iframe and stop execution in those situations. A confirmation prompt could also be used to stop this clickjacking attack. All these security measures have to be taken in the site loaded inside the iframe, as that is where the clickjacking is happening. In this example, the online shop is only a website with a stored XSS vulnerability.
USING DOM XSS TO STEAL CREDENTIALS
In terms of XSS, “Stored” is usually the most dangerous and a full example was shown earlier in this post. The Reflected and DOM XSS are very difficult to explore in WebView because of the XSS auditor that is implemented by default and blocks all the XSS code that it identifies.
As explained before, the auditor is based on blacklisting and has been improved over the years. But as it compares the malicious blacklisted content with the reply content it doesn’t work so well with DOM XSS scenarios.
As an example, the previously referred online shop has a DOM XSS vulnerability present in the page code:
The word “test” shows on the screen and it is the last search made. But this new functionality can also allow attackers to deliver XSS attacks. If an attacker places malicious code in the search box, he is affecting only himself. Therefore, the attacker has to make the victim click a link containing malicious code or to convince a victim to place the malicious code in their own search boxes. And that is more easy then it sounds.
The attacker conducted a phishing campaign with a well-crafted email pretending to be sent from the online shop advertising special offers to the users who place the following code in their search boxes: “<svg onload=`<script`-recoverPassword(‘email@example.com’)”.
The “recoverPassword” function mentioned in the malicious script is calling a very helpful server-side method that sends the user’s password to a given email address. This is useful to users that want to login in a different device but forgot the password. They can take advantage of their valid session (that doesn’t expire) and retrieve the password in the email, without the need to reset it. The function is protected by the authenticity of the session, therefore it appears harmless. But in fact, it allows CSRF attacks and in this case, it is being used by the attacker to steal the user’s credentials:
The previous image shows the result of the copy paste made by the victims. This scenario is also very common using links in the format “http://www.myvictimexampledomain.com?search=maliciouscode”, instead of asking the user to copy paste the malicious code.