Nextcloud · Report #2376909
Possible to enumerate valid files in password protected shares/files drop shares as well as spam folder with files
Validstatus
lowseverity
29upvotes
21 Feb 2025disclosed
How it was found
Summary:
It is possible possible to enumerate valid files in password protected shares/files drop shares as well as spam the folder with empty files with an attacker controlled file name.
Steps To Reproduce:
- Create a folder and create the file
foo.txtin it - Share the file publicly and mark it as Files Drops and Password Protected (the combination is not necessary, but simplifies the testing)
- As attacker send a request to
DocumentAPIController#createto enumerate the valid files - As attacker send a request to
DocumentAPIController#createto spam files
I've attached screenshots of these two behaviours here:
{F3055801}
{F3055802}
Supporting Material/References:
DocumentAPIController#create is not validating whether the share is writable, upload only or password protected:
public function create(string $mimeType, string $fileName, string $directoryPath = '/', ?string $shareToken = null, ?int $templateId = null): JSONResponse {
try {
if ($shareToken !== null) {
$share = $this->shareManager->getShareByToken($shareToken);
}
$rootFolder = $shareToken !== null ? $share->getNode() : $this->rootFolder->getUserFolder($this->userId);
$folder = $rootFolder->get($directoryPath);
if (!($folder instanceof Folder)) {
throw new Exception('Node is not a folder');
}
} catch (Throwable $e) {
$this->logger->error('Failed to create document', ['exception' => $e]);
return new JSONResponse([
'status' => 'error',
'message' => $this->l10n->t('Cannot create document')
], Http::STATUS_BAD_REQUEST);
}
$basename = $this->l10n->t('New Document.odt');
switch ($mimeType) {
case 'application/vnd.oasis.opendocument.spreadsheet':
$basename = $this->l10n->t('New Spreadsheet.ods');
break;
case 'application/vnd.oasis.opendocument.presentation':
$basename = $this->l10n->t('New Presentation.odp');
break;
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
$basename = $this->l10n->t('New Document.docx');
break;
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
$basename = $this->l10n->t('New Spreadsheet.xlsx');
break;
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
$basename = $this->l10n->t('New Presentation.pptx');
break;
default:
break;
}
if (!$fileName) {
$fileName = Helper::getNewFileName($folder, $basename);
}
if ($folder->nodeExists($fileName)) {
return new JSONResponse([
'status' => 'error',
'message' => $this->l10n->t('File already exists')
], Http::STATUS_BAD_REQUEST);
}
try {
$file = $folder->newFile($fileName);
$templateType = $this->templateManager->getTemplateTypeForExtension(pathinfo($fileName, PATHINFO_EXTENSION));
$empty = $this->templateManager->getEmpty($templateType);
$templateFile = array_shift($empty);
if ($templateId) {
$templateFile = $this->templateManager->get($templateId);
}
$file->putContent($this->templateManager->getEmptyFileContent($file->getExtension()));
if ($this->templateManager->isSupportedTemplateSource($templateFile->getExtension())) {
// Only use TemplateSource if supported filetype
$this->templateManager->setTemplateSource($file->getId(), $templateFile->getId());
}
} catch (Exception $e) {
$this->logger->error('Failed to create file from template', ['exception' => $e]);
return new JSONResponse([
'status' => 'error',
'message' => $this->l10n->t('Not allowed to create document')
], Http::STATUS_BAD_REQUEST);
}
return new JSONResponse([
'status' => 'success',
'data' => \OCA\Files\Helper::formatFileInfo($file->getFileInfo())
]);
}
Impact
It is possible possible to enumerate valid files in password protected shares/files drop shares as well as spam the folder with empty files with an attacker controlled file name.