Categories
Bug Hunting

Stored XSS Leads to Plaintext Password Disclosure

1. Upload HTML file using image upload feature.
2. Send user link to uploaded file.
3. User opens link and their AUTHH cookie is decoded, revealing their password.

Introduction

This post documents another vulnerability I found in the same company as the last post. I disclosed both vulnerabilities on the same day and was told that both vulnerabilities have been resolved. I was authorized to provide a partial disclosure, hence why I’m writing this post now.

The Story

So once I had logged into the mobile app, I had a look around and noticed a profile image upload feature:

Can upload a profile picture. Please ignore the missing image icon.

I used Burp to intercept the request sent when uploading a profile image and saw something like this:

PATCH request used to upload profile image. This was taken after exploiting the issue. Originally the ProfilePicture parameter contained base64 encoded image data and the ProfilePictureMIME parameter contained image/png.

Basically they are taking the image data, converting it to Base64 and then sending it via a standard JSON OData API request.

The most important thing you should notice is that there is a ProfilePictureMIME parameter. This essentially dictates the file type that is being uploaded.

I tried changing that to text/x-php and application/php but they were not allowed. Surprisingly, text/html was allowed. I then created a basic HTML file that pops an alert:

<!DOCTYPE html>
<html>
<body>
<h1>Test</h1>
</body>
<script>
alert(1);
</script>
</html>

I converted this to Base64 and replaced the value of the ProfilePicture parameter in the request with my base64 encoded HTML file. I set the ProfilePictureMIME to text/html and sent the request. When I used my browser to check the raw file to my image, it actually rendered the HTML! Unfortunately I do not have screenshot of this.

So then I thought of a practical attack scenario. I realized that there was no authentication required to access the raw profile image files from their server. I then realized that the domain hosting these profile images was also the same domain as the one hosting their web application:

Link to image file: https://something.redacted.com/res/img/usermeta//551/USER_7025dffcf32e4097bebe7b530f9f1a5d.png?ts=1584857339
Link to web application: https://something.redacted.com/login/

Using the credentials I found before, I logged into the web application and tried visiting the link to the HTML profile image I uploaded. I could access it.

So then I examined any cookies I could access using JavaScript. One of these was called AUTHH. It was base64 encoded, so i decoded it using CyberChef and realized that it was the same value as the Authorization header! Since they use HTTP Basic Authentication, the credentials are in plaintext!

Cookie: AUTHH=QmFzaWMgWm1GclpUcG1ZV3RsY0dGemN3PT0=

I immediately uploaded the following HTML file:

<html>
<head>
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie@rc/dist/js.cookie.min.js"></script>
</head>
<body>
<h1>Password Display by bad5ect0r</h1>
<p>Your username is: <span id="uname"></span></p>
<p>Your password is: <span id="pass"></span></p>
</body>
<script>
$(document).ready(function () {
const AUTHH = Cookies.get('AUTHH');
const unb64 = atob(AUTHH);
const basic = unb64.split(' ');
const uname_pass = atob(basic[1]).split(':');
const user = uname_pass[0];
const pass = uname_pass[1];
$('#uname').html(user);
$('#pass').html(pass);
});
</script>
</html>

This basically displays the user’s username and password by decoding their AUTHH cookie. A real attacker would just forward this information to their server after getting a user to click on the link to their malicious profile image.

I reloaded the link while still authenticated to the web application, and it worked like a charm!

Viewing that link would disclose your username and password.

I immediately sent out another email to the company alerting them of this new vulnerability and while there was some initial confusion on the severity of this bug, they were able to prioritize getting it fixed.

I look forward to finding more bugs on this company’s platform and hope that one day they move to a rewards based system to encourage repeat hackers.

Takeaway

If you can’t upload a web shell, try the next best thing, an HTML file to get stored XSS.

Disclosure Timeline

  • 21/03/2020 – Issue was reported to the company.
  • 25/03/2020 – Follow up.
  • 27/03/2020 – Acknowledged by the company.
  • 03/04/2020 – Issues were fixed.
  • 15/05/2020 – Partial disclosure was authorized.

Leave a Reply

Your email address will not be published. Required fields are marked *