3

I came across a very interesting case:

  • The user uploads a file
  • The file contains <script>alert(2)</script>
  • The web app shows the file's content to the user -> the XSS payload is executed
  • The file is not stored in any way, shape or form (forget about Stored XSS)
  • There's no anti-CSRF token on the file upload

XSS via File Upload, right? Not a big deal.

Now the big question is: how to exploit it without literally asking the victim 'Hey, can you upload this file??'

Initially I dug into "Cross-Site File Upload" and read plenty of literature but I couldn't find any clear example of an XSS being exploited in this way.

The best blog post about this specific scenario is the following: https://www.exploresecurity.com/a-tricky-case-of-xss/ (here the injection is in the filename and not in the content, but it changes very little). The key is that the payload leverages a browser related issue and therefore it is not general-purpose.

The blog post also describes some of the challenges I've already faced: the file upload should not be chosen by the victim, the file upload should also return something to let the victim's browser run the JS payload, the attacker's page should deliver a multipart/form-data encoding, etc...

I've spent a considerable amount of time trying to find the answer to the "Is this even possible?" question and since I have not been lucky so far, I wanted to ask you guys if you've already faced a similar riddle.

This is the demo vulnerable page I'm using for my tests:

<html>
<body bgcolor="lightyellow">


<?php
echo "The file ". $_FILES["fileToUpload"]["name"] . " has been uploaded.";
$fileContent = file_get_contents($_FILES['fileToUpload']['tmp_name']);
echo "<p>The file contents are: <hr/><br>$fileContent<br/><hr/>"; 
?>

<p><p><hr/>
<b>Here you can upload your file!</b>

<form action="vuln-upload.php" method="post" enctype="multipart/form-data">
  <input type="file" name="fileToUpload" id="fileToUpload">
  <input type="submit" value="Upload" name="submit">
</form>

</body>
</html>

Thank you a lot for the help!

Cob013
  • 133
  • 4

1 Answers1

2

The cross-domain file upload attack is prevented by the Same Origin Policy (SOP).

The only way to automate the file upload, with arbitrary contents set by an attacker, is using the FormData API. This involves constructing a file upload HTTP request and sending it via XMLHttpRequest (ajax request) or the Fetch API. You cannot perform cross-domain requests with these methods unless the target domain explicitly allows it via CORS.

If the target domain set a header such as Access-Control-Allow-Origin: *, thus effectively disabling SOP, then you could exploit this by constructing a form data object that contains your XSS payload:

var payload = new FormData();
payload.append("file", "<script>alert(1);</script>", "evil.txt");
fetch("http://target.domain/upload.php", {
    method: "post",
    body: payload,
    credentials: "include"
});

This sends a file upload via JavaScript, with an arbitrary name and contents. But, as I said earlier, this only works if the victim domain bypasses the same origin policy with a dangerous CORS header.

One thing I noticed in your demo code is that the filename being displayed is also vulnerable to XSS, since it is printed without being escaped. You might consider an attack page like the following:

<script>
function rename_file_to_something_evil()
{
    var form = document.getElementById('exploit');
    form.files[0].rename('<script>alert(1);</script>'); /* this is not real JS */
}
</script>
<form id="exploit" method="POST" target="http://target.domain/upload.php" enctype="multipart/form-data">
    <input type="file" name="file" value="Choose a file..." />
    <input type="submit" onclick="rename_file_to_something_evil()" value="Upload" />
</form>

Here, the idea would be that we could get the user to upload any file through some social engineering pretext, e.g. "upload a picture of yourself and we'll show you what you look like in 20 years", then use JavaScript to rename the file to an XSS payload and send it to the target domain, since cross-domain form submissions are allowed even if cross-domain ajax/fetch requests aren't. If we could get the user to manually initiate that request, but somehow modify the contents, perhaps we could exploit it that way. However, the standards developers considered this attack vector already, so they made it impossible to rename a file with JavaScript before upload. You can't apply a FormData object to a form in the DOM either.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • Thanks Poly for the quick and descriptive answer! The vulnerable server is just something I wrote to test the XSS, but the filename rename was indeed interesting. All in all, if no CORS is enabled what you're saying is that it's not possible to perform an XSS in this way. I read about file upload Server to Server via PHP Curl, but that will not return the content of the vulnerable page (due to SOP) hence it's not a viable solution. Did I read your answer correctly? – Cob013 Feb 17 '21 at 20:20
  • Yes, that's correct. – Polynomial Feb 17 '21 at 21:18