• Twitter
  • FB
  • Github
  • Youtube

Sunday, August 4, 2019

Leveraging AngularJS-based XSS to Privilege Escalation

Greetings everyone, this is Shawar Khan. Been over months since my last write-up as I was quite busy in testing different targets. Recently I found an interesting XSS vulnerability in a target using which I was able to escalate my privileges to an admin user.

XSS is such an interesting vulnerability, after discovering it you can just play and communicate with an application without having to worry about Same-Origin Policy. Everything is our control and most of the protections are broken.

So, it is the application the administrator user has the highest privileges and it was possible to add/delete/edit any user. So my goal was set to escalate my privileges to an administrative user account via XSS. Whenever I've discovered XSS, my main goal is to play around and make it exploitable in such a unique way in which I haven't done before. Grabbing tokens, bypassing CSRF protections or grabbing cookies are just an old form of exploitation now. So, I tried escalating my privileges.

During my test, there were multiple XSS vulnerabilities discovered by the interesting one was found at the user profile page. Each registered user has a different profile page such as "https://www.site.com/users/username-here".

Discovering the AngularJS-based XSS:

This was a page that was reflecting the First & Last name of a user account which was accessible by all privileged user. Applying simple test probes such as "><img src=x onerror=prompt()> didn't shown my any kind of results so there was proper XSS protection being done. All special characters were properly filtered but I thought why not try to get AngularJS based XSS. Went to settings and changed account name to "{{alert(1)}}".

So, I tested the same thing as a different privileged user and navigating to my profile at /users/username_page triggers the payload which confirms that it was accessible by any user:

When trying to escalate privileges, your main goal is to look for functionalities that will edit your role or will invite you to an unrestricted area giving you access to information. In my case, the admin user had the authority to edit/add users so this is something I was willing to target.

In my case, I had a test admin account to test the issue so I knew what request I had to replicate to add a new admin privileged user. In scenarios where you do not have access, simply try to obtain source code of admin account by sending output of document.body.innerHTML and try to obtain information about internal functionalities. XSSHunter and other tools can be exploit to obtain such information.

Understanding the payload serving:

Anyways, the username field had a short length-limit so it was not possible to write the entire exploit code in that field. The username will also add entries to the profile page plus it will look malicious as well. Also, it was not possible to inject a script tag referring to external javascript but that will be length as well.

As always, serving the payload via window.name. I always serve the payload via window.name as there are no issues with the exploit limit and the payload for loading our exploit code is limited to 20 characters as we will be only loading and serving the given payload to eval(atob(top.name)) another benefit of using this technique is that it will bypass many of the validation checks for malicious keywords as our main exploit code will not be inputted inside the vulnerable application so in a nutshell our exploit code is not validated and checked.

So, the window name can be set by opening a URL using window.open(url,"window name here") and intead of window name we will set our exploit code to base64. So by calling window.name it will return our exploit code which will be executed by eval()

Targeting the User modification functionality:

This functionality was found in the admin user portal and the highest privileged user was able to change data and privileges of any user of the application. There are different options such as email change and check boxes to confirm if the user is higher privileged or not. By setting a parameter "csc=1" the user is given full privileges but this can be only done by an admin user. 

In case if the source code is only retrieved it is possible to map all the functionalities by doing a source code review and understanding what endpoints are taking what parameters.

The following was the request that modifies a user to an admin and fully privileged user:

POST /users/attackers-username HTTP/1.1
Host: vulnerablesite.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 141


In order to escalate our privileges, the request above should be reproduced so when our exploit code is accessed by a higher privileged user our user will be modified.

Writing the exploit code:

The first thing we have to retrieve is the CSRF Token so we can validate the request. Sometimes its present in the cookie it self so retrieving it from document.cookie is quite easy but in this case it was found in a meta-tag such as:
<meta name="CSRF_TOKEN" content="TOKEN_HERE">

I opened up the settings page located at /settings using fetch() and stored its output in a variable woot. Then I used woot.getElementsByTagName('meta')[3]['content'] to retrieve the value of CSRF token and stored it into a new variable csrf_token, now our exploit code is something like:

var woot = document.createElement('html');
fetch('https://vulnerablesite.com/settings',{credentials: 'include'}).then((resp) => resp.text()).then(function(data){

var csrf_token = woot.getElementsByTagName('meta')[3]['content']

now we will have to use reproduce the request which can be easilly done with XHR:

function privilege_escalate(){
var req = new XMLHttpRequest();
req.withCredentials = true;
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
The above privilege_escalate() function when executed will send a POST request that will change information of the attacker's account which in my case is mrs-camylle-kertzmazevalwindowname and also changed the name to our payload {{eval(atob(window.name))}}  this will keep the name so when a window.name is having an exploit code it will be used to execute the exploit code from window.name. Also, this request has the csc=1 which will change the privileges of our user.

Final exploit code:

The exploit code can be base64 encoded further and used as window name so when it is executed by eval(atob(window.name)) it will be triggered. We can now use the following code that will open up our profile page and will set our exploit code to window name. So once the window.name is accessed our exploit is triggered:


In the following screenshot we can see that our user is having access to limited functionalities:
Attacker's account before exploit

After successfully executing our exploit code in a higher privileged user, our account will have highest privileges and access to admin functionalities. As seen in the screenshot below:
Attacker's account after exploit execution

Take aways:

  1. Whenever testing for XSS vulnerabilities, don't just stop when the application is properly filtering user input such as < > and other characters. Move one step further and try other techniques to achieve XSS such as the one mentioned in this write-up. Try using {{alert(1}} or try to upload files such as .swf, .svg, .html, .url and others.
  2. Never stop at detection of a vulnerability, always try to play around to know its limitations and its range. In case of XSS, try to interact with unique functionalities, fuzz around to see what else you can achieve instead of just a popup.
  3. Try something unique & think out of the box!


Post a Comment

Want to contact?

Get in touch with me